From 966acb2a92bb740e57b0b7a181df2d21956e1d3d Mon Sep 17 00:00:00 2001 From: Eric Lam Date: Wed, 26 Aug 2026 19:49:19 +0800 Subject: [PATCH 01/17] feat(fulltext2): add 4.2 parser and catalog contracts Backport-of: 1d7b6311ce91df0968da435bb405f3d68137c595 --- go.mod | 7 +- go.sum | 16 +- pkg/catalog/secondary_index_utils.go | 142 +- pkg/catalog/types.go | 33 + pkg/cnservice/server_task.go | 1 + pkg/frontend/variables.go | 17 + pkg/pb/plan/plan.pb.go | 41 + pkg/sql/parsers/dialect/mysql/keywords.go | 4 + pkg/sql/parsers/dialect/mysql/mysql_sql.go | 26152 ++++++++++--------- pkg/sql/parsers/dialect/mysql/mysql_sql.y | 112 +- pkg/sql/parsers/tree/alter.go | 12 + pkg/sql/parsers/tree/create.go | 42 +- pkg/sql/parsers/tree/expr.go | 7 + pkg/sql/plan/function/func_unary.go | 43 + pkg/sql/plan/function/function_id.go | 6 +- pkg/sql/plan/function/list_builtIn.go | 47 + proto/plan.proto | 1 + 17 files changed, 13671 insertions(+), 13012 deletions(-) diff --git a/go.mod b/go.mod index 21ce81d1b0609..f19a526e0b99b 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 github.com/aws/smithy-go v1.22.1 github.com/axiomhq/hyperloglog v0.2.6 + github.com/blevesearch/vellum v1.2.0 github.com/buger/jsonparser v1.1.1 github.com/bytedance/sonic v1.15.2 github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5 @@ -83,7 +84,7 @@ require ( github.com/segmentio/encoding v0.4.0 github.com/shirou/gopsutil/v3 v3.23.12 github.com/smartystreets/goconvey v1.7.2 - github.com/spf13/cobra v1.8.0 + github.com/spf13/cobra v1.10.2 github.com/spkg/bom v1.0.0 github.com/stretchr/testify v1.11.1 github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465 @@ -138,6 +139,8 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.24.2 // indirect + github.com/blevesearch/mmap-go v1.2.0 // indirect + github.com/bufbuild/protocompile v0.6.0 // indirect github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic/loader v0.5.1 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect @@ -231,7 +234,7 @@ require ( github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartystreets/assertions v1.13.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.9 // indirect github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect github.com/tetratelabs/wazero v1.8.1-0.20240916092830-1353ca24fef0 // indirect github.com/tinylib/msgp v1.6.1 // indirect diff --git a/go.sum b/go.sum index 7fc7f591d36df..880e46baeb43c 100644 --- a/go.sum +++ b/go.sum @@ -117,6 +117,12 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bits-and-blooms/bitset v1.24.2 h1:M7/NzVbsytmtfHbumG+K2bremQPMJuqv1JD3vOaFxp0= github.com/bits-and-blooms/bitset v1.24.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blevesearch/mmap-go v1.2.0 h1:l33nNKPFcBjJUMwem6sAYJPUzhUCABoK9FxZDGiFNBI= +github.com/blevesearch/mmap-go v1.2.0/go.mod h1:Vd6+20GBhEdwJnU1Xohgt88XCD/CTWcqbCNxkZpyBo0= +github.com/blevesearch/vellum v1.2.0 h1:xkDiOEsHc2t3Cp0NsNZZ36pvc130sCzcGKOPMzXe+e0= +github.com/blevesearch/vellum v1.2.0/go.mod h1:uEcfBJz7mAOf0Kvq6qoEKQQkLODBF46SINYNkZNae4k= +github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= +github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY= github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= @@ -204,7 +210,7 @@ github.com/cpegeric/pdftotext-go v0.0.0-20241112123704-49cb86a3790e/go.mod h1:zt github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -801,13 +807,13 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spkg/bom v1.0.0 h1:S939THe0ukL5WcTGiGqkgtaW5JW+O6ITaIlpJXTYY64= github.com/spkg/bom v1.0.0/go.mod h1:lAz2VbTuYNcvs7iaFF8WW0ufXrHShJ7ck1fYFFbVXJs= diff --git a/pkg/catalog/secondary_index_utils.go b/pkg/catalog/secondary_index_utils.go index 0ce74fa2148cb..03d92739e8905 100644 --- a/pkg/catalog/secondary_index_utils.go +++ b/pkg/catalog/secondary_index_utils.go @@ -29,15 +29,16 @@ import ( // Index Algorithm names const ( - MoIndexDefaultAlgo = tree.INDEX_TYPE_INVALID // used by UniqueIndex or default SecondaryIndex - MoIndexBTreeAlgo = tree.INDEX_TYPE_BTREE // used for Mocking MySQL behaviour. - MoIndexRTreeAlgo = tree.INDEX_TYPE_RTREE // used for Spatial Index on GEOMETRY columns - MoIndexIvfFlatAlgo = tree.INDEX_TYPE_IVFFLAT // used for IVF flat index on Vector/Array columns - MOIndexMasterAlgo = tree.INDEX_TYPE_MASTER // used for Master Index on VARCHAR columns - MOIndexFullTextAlgo = tree.INDEX_TYPE_FULLTEXT // used for Fulltext Index on VARCHAR columns - MoIndexHnswAlgo = tree.INDEX_TYPE_HNSW // used for HNSW Index on Vector/Array columns - MoIndexCagraAlgo = tree.INDEX_TYPE_CAGRA // used for CAGRA Index on Vector/Array columns - MoIndexIvfpqAlgo = tree.INDEX_TYPE_IVFPQ // used for IVFPQ Index on Vector/Array columns + MoIndexDefaultAlgo = tree.INDEX_TYPE_INVALID // used by UniqueIndex or default SecondaryIndex + MoIndexBTreeAlgo = tree.INDEX_TYPE_BTREE // used for Mocking MySQL behaviour. + MoIndexRTreeAlgo = tree.INDEX_TYPE_RTREE // used for Spatial Index on GEOMETRY columns + MoIndexIvfFlatAlgo = tree.INDEX_TYPE_IVFFLAT // used for IVF flat index on Vector/Array columns + MOIndexMasterAlgo = tree.INDEX_TYPE_MASTER // used for Master Index on VARCHAR columns + MOIndexFullTextAlgo = tree.INDEX_TYPE_FULLTEXT // used for Fulltext Index on VARCHAR columns + MoIndexFullText2Algo = tree.INDEX_TYPE_FULLTEXT2 // CREATE FULLTEXT2 INDEX: WAND positional engine on TEXT/VARCHAR columns + MoIndexHnswAlgo = tree.INDEX_TYPE_HNSW // used for HNSW Index on Vector/Array columns + MoIndexCagraAlgo = tree.INDEX_TYPE_CAGRA // used for CAGRA Index on Vector/Array columns + MoIndexIvfpqAlgo = tree.INDEX_TYPE_IVFPQ // used for IVFPQ Index on Vector/Array columns ) // ToLower is used for before comparing AlgoType and IndexAlgoParamOpType. Reason why they are strings @@ -81,6 +82,14 @@ func IsFullTextIndexAlgo(algo string) bool { return _algo == MOIndexFullTextAlgo.ToString() } +// IsFullText2IndexAlgo reports the distinct WAND fulltext engine used by +// CREATE FULLTEXT2 INDEX. It deliberately remains separate from classic +// FULLTEXT so planner and plugin routing cannot depend on index enumeration order. +func IsFullText2IndexAlgo(algo string) bool { + _algo := ToLower(algo) + return _algo == MoIndexFullText2Algo.ToString() +} + func IsHnswIndexAlgo(algo string) bool { _algo := ToLower(algo) return _algo == MoIndexHnswAlgo.ToString() @@ -107,6 +116,7 @@ const ( AutoUpdate = "auto_update" Day = "day" Hour = "hour" + Second = "second" DistributionMode = "distribution_mode" Quantization = "quantization" BitsPerCode = "bits_per_code" @@ -128,6 +138,14 @@ const ( IndexAlgoParamQuantizerTrainLimit = "quantizer_train_limit" IndexAlgoParamPrefixLengths = "prefix_lengths" + // IndexAlgoParamPrefixLengthsV2 stores a JSON object when a quoted column + // name contains a delimiter used by the legacy prefix encoding. + IndexAlgoParamPrefixLengthsV2 = "prefix_lengths_v2" + IndexAlgoParamMaxPostingsCapacity = "max_postings_capacity" + IndexAlgoParamPositionFree = "position_free" + // IndexAlgoParamVersion identifies the index engine; v2 is the WAND + // FULLTEXT2 engine and absent/1 is the classic FULLTEXT engine. + IndexAlgoParamVersion = "version" ) func ParseIncludeColumnsValue(raw string) ([]string, error) { @@ -230,6 +248,10 @@ func IndexParamsToStringList(indexParams string) (string, error) { res += fmt.Sprintf(" %s = %s ", Hour, val) } + if val, ok := result[Second]; ok { + res += fmt.Sprintf(" %s = %s ", Second, val) + } + if val, ok := result[Quantization]; ok { res += fmt.Sprintf(" %s '%s' ", Quantization, val) } @@ -266,9 +288,19 @@ func IndexParamsToStringList(indexParams string) (string, error) { res += fmt.Sprintf(" %s = %s ", IndexAlgoParamMaxIndexCapacity, val) } + if val, ok := result[IndexAlgoParamMaxPostingsCapacity]; ok { + res += fmt.Sprintf(" %s = %s ", IndexAlgoParamMaxPostingsCapacity, val) + } + if val, ok := result[IndexAlgoParamPositionFree]; ok && val == "true" { + res += fmt.Sprintf(" %s = %s ", IndexAlgoParamPositionFree, val) + } + if val, ok := result[IndexAlgoParamQuantizerTrainLimit]; ok { res += fmt.Sprintf(" %s = %s ", IndexAlgoParamQuantizerTrainLimit, val) } + if val, ok := result[IndexAlgoParamVersion]; ok { + res += fmt.Sprintf(" %s = %s ", IndexAlgoParamVersion, val) + } return res, nil } @@ -298,8 +330,16 @@ func IndexParamsMapToJsonString(res map[string]string) (string, error) { } func AddIndexPrefixLengthsToParams(indexParams string, keyParts []*tree.KeyPart) (string, error) { - prefixLengths := IndexPrefixLengthsToString(keyParts) - if prefixLengths == "" { + if len(keyParts) == 0 { + return indexParams, nil + } + prefixLengths := make(map[string]int, len(keyParts)) + for _, keyPart := range keyParts { + if keyPart != nil && keyPart.ColName != nil && keyPart.Length > 0 { + prefixLengths[keyPart.ColName.ColName()] = keyPart.Length + } + } + if len(prefixLengths) == 0 { return indexParams, nil } @@ -311,10 +351,38 @@ func AddIndexPrefixLengthsToParams(indexParams string, keyParts []*tree.KeyPart) } params = existing } - params[IndexAlgoParamPrefixLengths] = prefixLengths + if err := SetIndexPrefixLengthsInParamMap(params, prefixLengths); err != nil { + return "", err + } return IndexParamsMapToJsonString(params) } +// SetIndexPrefixLengthsInParamMap keeps the legacy representation for ordinary +// names and switches to a lossless JSON value when a quoted identifier contains +// a legacy delimiter. +func SetIndexPrefixLengthsInParamMap(params map[string]string, prefixLengths map[string]int) error { + if params == nil { + return moerr.NewInvalidInputNoCtx("nil index params map") + } + delete(params, IndexAlgoParamPrefixLengths) + delete(params, IndexAlgoParamPrefixLengthsV2) + if len(prefixLengths) == 0 { + return nil + } + for part := range prefixLengths { + if strings.ContainsAny(part, ":,") { + encoded, err := json.Marshal(prefixLengths) + if err != nil { + return err + } + params[IndexAlgoParamPrefixLengthsV2] = string(encoded) + return nil + } + } + params[IndexAlgoParamPrefixLengths] = indexPrefixLengthsMapToLegacyString(prefixLengths) + return nil +} + func IndexPrefixLengthsToString(keyParts []*tree.KeyPart) string { if len(keyParts) == 0 { return "" @@ -331,6 +399,13 @@ func IndexPrefixLengthsToString(keyParts []*tree.KeyPart) string { return "" } + return indexPrefixLengthsMapToLegacyString(prefixLengths) +} + +func indexPrefixLengthsMapToLegacyString(prefixLengths map[string]int) string { + if len(prefixLengths) == 0 { + return "" + } parts := make([]string, 0, len(prefixLengths)) for part := range prefixLengths { parts = append(parts, part) @@ -361,6 +436,22 @@ func IndexPrefixLengthsFromParamsWithError(indexParams string) (map[string]int, return nil, err } + if encoded, ok := params[IndexAlgoParamPrefixLengthsV2]; ok { + if encoded == "" { + return nil, nil + } + prefixLengths := make(map[string]int) + if err := json.Unmarshal([]byte(encoded), &prefixLengths); err != nil { + return nil, moerr.NewInvalidInputNoCtxf("invalid v2 index prefix lengths %q", encoded) + } + for part, length := range prefixLengths { + if part == "" || length <= 0 { + return nil, moerr.NewInvalidInputNoCtxf("invalid v2 index prefix length %q", encoded) + } + } + return prefixLengths, nil + } + encoded := params[IndexAlgoParamPrefixLengths] if encoded == "" { return nil, nil @@ -496,6 +587,33 @@ func IsIndexAsync(indexAlgoParams string) (bool, error) { return false, nil } +// IndexParamAsync is the descriptive name used by the index-plugin framework. +// Keep IsIndexAsync above as a compatibility wrapper for existing callers. +func IndexParamAsync(indexAlgoParams string) (bool, error) { + return IsIndexAsync(indexAlgoParams) +} + +// IndexAlgoParamVersionOf returns the engine version recorded in algo_params. +// Missing or malformed values are legacy/classic version 1. +func IndexAlgoParamVersionOf(indexAlgoParams string) int64 { + if len(indexAlgoParams) == 0 { + return 1 + } + val, err := sonic.Get([]byte(indexAlgoParams), IndexAlgoParamVersion) + if err != nil { + return 1 + } + s, err := val.StrictString() + if err != nil { + return 1 + } + n, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return 1 + } + return n +} + //------------------------[END] IndexAlgoParams------------------------ // ------------------------[START] Aliaser------------------------ diff --git a/pkg/catalog/types.go b/pkg/catalog/types.go index 7154dd0128c33..0d236948e6511 100644 --- a/pkg/catalog/types.go +++ b/pkg/catalog/types.go @@ -421,6 +421,39 @@ const ( FullTextIndex_TabCol_Id = "doc_id" FullTextIndex_TabCol_Position = "pos" + /************ 3c. FULLTEXT v2 (VERSION=2) Index **************/ + + // Fulltext v2 (CREATE FULLTEXT INDEX ... VERSION=2, the WAND positional engine) + // uses the same chunked storage + metadata layout as bm25 — segments are built + // and CDC-maintained, no postings table — as opposed to classic v1's single + // (word,doc_id,pos) postings table. The index stays algo="fulltext"; the version + // param selects the engine and these hidden tables. Values are <= 11 chars + // (IndexAlgoTableType varchar(11) limit), so "ftv2_*" not "fulltext2_*". + FullText2Index_TblType_Metadata = "ftv2_meta" + FullText2Index_TblType_Storage = "ftv2_index" + + FullText2Index_TblCol_Storage_Index_Id = "index_id" + FullText2Index_TblCol_Storage_Chunk_Id = "chunk_id" + FullText2Index_TblCol_Storage_Data = "data" + FullText2Index_TblCol_Storage_Tag = "tag" + + FullText2Index_TblCol_Metadata_Index_Id = "index_id" + FullText2Index_TblCol_Metadata_Timestamp = "timestamp" + FullText2Index_TblCol_Metadata_Checksum = "checksum" + FullText2Index_TblCol_Metadata_Filesize = "filesize" + FullText2Index_TblCol_Metadata_Recency = "recency" + FullText2Index_TblCol_Metadata_Nrow = "nrow" + + // fulltext2_search TVF RESERVED output-column names. Unlike FullTextIndex_TabCol_Id + // ("doc_id", a PHYSICAL classic-index column), these are plan-level output ALIASES of + // the fulltext2_search TVF (its storage is segments, not a doc_id column). The covered + // fast path emits INCLUDE columns as sibling outputs and the runtime classifies the + // output batch BY NAME, so the pk/relevance outputs must use names no user INCLUDE + // column can equal — hence the reserved "__mo_ft_" prefix. Referenced by the coldef + // builders (tablefunc.go, apply_indices_fulltext2.go) and the classifier (fulltext2_search.go). + FullText2Search_OutCol_DocId = "__mo_ft_doc_id" + FullText2Search_OutCol_Score = "__mo_ft_score" + /************ 4. HNSW Index *************/ // HNSW Table Types diff --git a/pkg/cnservice/server_task.go b/pkg/cnservice/server_task.go index 5c04fe17a87c9..f4a8b89535d0b 100644 --- a/pkg/cnservice/server_task.go +++ b/pkg/cnservice/server_task.go @@ -352,6 +352,7 @@ func (s *service) registerExecutorsLocked() { s.task.runner.Attach, s.cfg.UUID, common.ISCPAllocator, + s.fileService, ), ) diff --git a/pkg/frontend/variables.go b/pkg/frontend/variables.go index 04ec4c8fda6a0..2d3b44f5bdc8d 100644 --- a/pkg/frontend/variables.go +++ b/pkg/frontend/variables.go @@ -31,6 +31,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/container/vector" "github.com/matrixorigin/matrixone/pkg/defines" "github.com/matrixorigin/matrixone/pkg/fulltext" + "github.com/matrixorigin/matrixone/pkg/fulltext2" "github.com/matrixorigin/matrixone/pkg/util/gpumode" ) @@ -3816,6 +3817,14 @@ var gSysVarsDefs = map[string]SystemVariable{ Type: InitSystemVariableBoolType("experimental_fulltext_index"), Default: int8(0), }, + "experimental_fulltext2_index": { + Name: "experimental_fulltext2_index", + Scope: ScopeBoth, + Dynamic: true, + SetVarHintApplies: false, + Type: InitSystemVariableBoolType("experimental_fulltext2_index"), + Default: int8(0), + }, "ft_relevancy_algorithm": { Name: fulltext.FulltextRelevancyAlgo, Scope: ScopeBoth, @@ -3824,6 +3833,14 @@ var gSysVarsDefs = map[string]SystemVariable{ Type: InitSystemVariableStringType(fulltext.FulltextRelevancyAlgo), Default: fulltext.FulltextRelevancyAlgo_tfidf, }, + "ft2_relevancy_algorithm": { + Name: fulltext2.Fulltext2RelevancyAlgo, + Scope: ScopeBoth, + Dynamic: true, + SetVarHintApplies: false, + Type: InitSystemVariableStringType(fulltext2.Fulltext2RelevancyAlgo), + Default: fulltext2.Fulltext2RelevancyAlgo_bm25, + }, "experimental_hnsw_index": { Name: "experimental_hnsw_index", Scope: ScopeBoth, diff --git a/pkg/pb/plan/plan.pb.go b/pkg/pb/plan/plan.pb.go index 352674693f131..3334bdf919342 100644 --- a/pkg/pb/plan/plan.pb.go +++ b/pkg/pb/plan/plan.pb.go @@ -11346,6 +11346,7 @@ type AlterTableAlterReIndex struct { IndexName string `protobuf:"bytes,3,opt,name=index_name,json=indexName,proto3" json:"index_name,omitempty"` IndexAlgoParamList int64 `protobuf:"varint,4,opt,name=index_algo_param_list,json=indexAlgoParamList,proto3" json:"index_algo_param_list,omitempty"` ForceSync bool `protobuf:"varint,5,opt,name=force_sync,json=forceSync,proto3" json:"force_sync,omitempty"` + Merge bool `protobuf:"varint,6,opt,name=merge,proto3" json:"merge,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -11419,6 +11420,13 @@ func (m *AlterTableAlterReIndex) GetForceSync() bool { return false } +func (m *AlterTableAlterReIndex) GetMerge() bool { + if m != nil { + return m.Merge + } + return false +} + type AlterTableAlterAutoUpdate struct { DbName string `protobuf:"bytes,1,opt,name=db_name,json=dbName,proto3" json:"db_name,omitempty"` TableName string `protobuf:"bytes,2,opt,name=table_name,json=tableName,proto3" json:"table_name,omitempty"` @@ -25203,6 +25211,16 @@ func (m *AlterTableAlterReIndex) MarshalToSizedBuffer(dAtA []byte) (int, error) i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.Merge { + i-- + if m.Merge { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } if m.ForceSync { i-- if m.ForceSync { @@ -32439,6 +32457,9 @@ func (m *AlterTableAlterReIndex) ProtoSize() (n int) { if m.ForceSync { n += 2 } + if m.Merge { + n += 2 + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -58001,6 +58022,26 @@ func (m *AlterTableAlterReIndex) Unmarshal(dAtA []byte) error { } } m.ForceSync = bool(v != 0) + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Merge", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPlan + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Merge = bool(v != 0) default: iNdEx = preIndex skippy, err := skipPlan(dAtA[iNdEx:]) diff --git a/pkg/sql/parsers/dialect/mysql/keywords.go b/pkg/sql/parsers/dialect/mysql/keywords.go index bf02a4c416ef9..515518888ea26 100644 --- a/pkg/sql/parsers/dialect/mysql/keywords.go +++ b/pkg/sql/parsers/dialect/mysql/keywords.go @@ -64,6 +64,7 @@ func init() { "ivfflat": IVFFLAT, "ivfpq": IVFPQ, "hnsw": HNSW, + "bm25": BM25, "m": M, "ef_construction": EF_CONSTRUCTION, "ef_search": EF_SEARCH, @@ -217,6 +218,7 @@ func init() { "from": FROM, "full": FULL, "fulltext": FULLTEXT, + "fulltext2": FULLTEXT2, "function": FUNCTION, "fields": FIELDS, "file": FILE, @@ -310,6 +312,8 @@ func init() { "kmeans_train_percent": KMEANS_TRAIN_PERCENT, "kmeans_max_iteration": KMEANS_MAX_ITERATION, "max_index_capacity": MAX_INDEX_CAPACITY, + "max_postings_capacity": MAX_POSTINGS_CAPACITY, + "position_free": POSITION_FREE, "quantizer_train_limit": QUANTIZER_TRAIN_LIMIT, "reindex": REINDEX, "limit": LIMIT, diff --git a/pkg/sql/parsers/dialect/mysql/mysql_sql.go b/pkg/sql/parsers/dialect/mysql/mysql_sql.go index 54de4547f84f8..6cdb48bf7dcd4 100644 --- a/pkg/sql/parsers/dialect/mysql/mysql_sql.go +++ b/pkg/sql/parsers/dialect/mysql/mysql_sql.go @@ -1,6 +1,6 @@ -// Code generated by goyacc -o mysql_sql.go mysql_sql.y. DO NOT EDIT. +// Code generated by goyacc -o pkg/sql/parsers/dialect/mysql/mysql_sql.go pkg/sql/parsers/dialect/mysql/mysql_sql.y. DO NOT EDIT. -//line mysql_sql.y:16 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:16 package mysql import ( @@ -8,7 +8,7 @@ import ( __yyunsafe__ "unsafe" ) -//line mysql_sql.y:16 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:16 import ( "fmt" @@ -445,366 +445,370 @@ const MASTER = 57722 const HNSW = 57723 const CAGRA = 57724 const IVFPQ = 57725 -const ZONEMAP = 57726 -const LEADING = 57727 -const BOTH = 57728 -const TRAILING = 57729 -const UNKNOWN = 57730 -const LISTS = 57731 -const OP_TYPE = 57732 -const REINDEX = 57733 -const EF_SEARCH = 57734 -const EF_CONSTRUCTION = 57735 -const M = 57736 -const ASYNC = 57737 -const FORCE_SYNC = 57738 -const AUTO_UPDATE = 57739 -const INTERMEDIATE_GRAPH_DEGREE = 57740 -const GRAPH_DEGREE = 57741 -const QUANTIZATION = 57742 -const BITS_PER_CODE = 57743 -const DISTRIBUTION_MODE = 57744 -const ITOPK_SIZE = 57745 -const INCLUDE = 57746 -const KMEANS_TRAIN_PERCENT = 57747 -const KMEANS_MAX_ITERATION = 57748 -const MAX_INDEX_CAPACITY = 57749 -const QUANTIZER_TRAIN_LIMIT = 57750 -const EXPIRE = 57751 -const ACCOUNT = 57752 -const ACCOUNTS = 57753 -const UNLOCK = 57754 -const DAY = 57755 -const NEVER = 57756 -const PUMP = 57757 -const MYSQL_COMPATIBILITY_MODE = 57758 -const UNIQUE_CHECK_ON_AUTOINCR = 57759 -const MODIFY = 57760 -const CHANGE = 57761 -const SECOND = 57762 -const ASCII = 57763 -const COALESCE = 57764 -const COLLATION = 57765 -const HOUR = 57766 -const MICROSECOND = 57767 -const MINUTE = 57768 -const MONTH = 57769 -const QUARTER = 57770 -const REPEAT = 57771 -const REVERSE = 57772 -const ROW_COUNT = 57773 -const WEEK = 57774 -const REVOKE = 57775 -const FUNCTION = 57776 -const PRIVILEGES = 57777 -const TABLESPACE = 57778 -const EXECUTE = 57779 -const SUPER = 57780 -const GRANT = 57781 -const OPTION = 57782 -const REFERENCES = 57783 -const REPLICATION = 57784 -const SLAVE = 57785 -const CLIENT = 57786 -const USAGE = 57787 -const RELOAD = 57788 -const FILE = 57789 -const FILES = 57790 -const TEMPORARY = 57791 -const ROUTINE = 57792 -const EVENT = 57793 -const SHUTDOWN = 57794 -const NULLX = 57795 -const AUTO_INCREMENT = 57796 -const APPROXNUM = 57797 -const ENGINES = 57798 -const LOW_CARDINALITY = 57799 -const AUTOEXTEND_SIZE = 57800 -const ADMIN_NAME = 57801 -const RANDOM = 57802 -const SUSPEND = 57803 -const ATTRIBUTE = 57804 -const HISTORY = 57805 -const REUSE = 57806 -const CURRENT = 57807 -const OPTIONAL = 57808 -const FAILED_LOGIN_ATTEMPTS = 57809 -const PASSWORD_LOCK_TIME = 57810 -const UNBOUNDED = 57811 -const SECONDARY = 57812 -const RESTRICTED = 57813 -const USER = 57814 -const IDENTIFIED = 57815 -const CIPHER = 57816 -const ISSUER = 57817 -const X509 = 57818 -const SUBJECT = 57819 -const SAN = 57820 -const REQUIRE = 57821 -const SSL = 57822 -const NONE = 57823 -const PASSWORD = 57824 -const SHARED = 57825 -const EXCLUSIVE = 57826 -const MAX_QUERIES_PER_HOUR = 57827 -const MAX_UPDATES_PER_HOUR = 57828 -const MAX_CONNECTIONS_PER_HOUR = 57829 -const MAX_USER_CONNECTIONS = 57830 -const FORMAT = 57831 -const VERBOSE = 57832 -const CONNECTION = 57833 -const TRIGGERS = 57834 -const PROFILES = 57835 -const PROFILE = 57836 -const LOAD = 57837 -const INLINE = 57838 -const INFILE = 57839 -const TERMINATED = 57840 -const OPTIONALLY = 57841 -const ENCLOSED = 57842 -const ESCAPED = 57843 -const STARTING = 57844 -const LINES = 57845 -const ROWS = 57846 -const IMPORT = 57847 -const DISCARD = 57848 -const JSONTYPE = 57849 -const MODUMP = 57850 -const OVER = 57851 -const PRECEDING = 57852 -const FOLLOWING = 57853 -const GROUPS = 57854 -const DATABASES = 57855 -const TABLES = 57856 -const SEQUENCES = 57857 -const EXTENDED = 57858 -const FULL = 57859 -const PROCESSLIST = 57860 -const FIELDS = 57861 -const COLUMNS = 57862 -const OPEN = 57863 -const ERRORS = 57864 -const WARNINGS = 57865 -const INDEXES = 57866 -const SCHEMAS = 57867 -const NODE = 57868 -const LOCKS = 57869 -const ROLES = 57870 -const RULE = 57871 -const RULES = 57872 -const TABLE_NUMBER = 57873 -const COLUMN_NUMBER = 57874 -const TABLE_VALUES = 57875 -const TABLE_SIZE = 57876 -const TASKS = 57877 -const RUNS = 57878 -const NAMES = 57879 -const GLOBAL = 57880 -const PERSIST = 57881 -const SESSION = 57882 -const ISOLATION = 57883 -const LEVEL = 57884 -const READ = 57885 -const WRITE = 57886 -const ONLY = 57887 -const REPEATABLE = 57888 -const COMMITTED = 57889 -const UNCOMMITTED = 57890 -const SERIALIZABLE = 57891 -const LOCAL = 57892 -const EVENTS = 57893 -const PLUGINS = 57894 -const CURRENT_TIMESTAMP = 57895 -const DATABASE = 57896 -const CURRENT_TIME = 57897 -const LOCALTIME = 57898 -const LOCALTIMESTAMP = 57899 -const UTC_DATE = 57900 -const UTC_TIME = 57901 -const UTC_TIMESTAMP = 57902 -const REPLACE = 57903 -const CONVERT = 57904 -const SEPARATOR = 57905 -const TIMESTAMPDIFF = 57906 -const TIMESTAMPADD = 57907 -const CURRENT_DATE = 57908 -const CURRENT_USER = 57909 -const CURRENT_ROLE = 57910 -const SECOND_MICROSECOND = 57911 -const MINUTE_MICROSECOND = 57912 -const MINUTE_SECOND = 57913 -const HOUR_MICROSECOND = 57914 -const HOUR_SECOND = 57915 -const HOUR_MINUTE = 57916 -const DAY_MICROSECOND = 57917 -const DAY_SECOND = 57918 -const DAY_MINUTE = 57919 -const DAY_HOUR = 57920 -const YEAR_MONTH = 57921 -const SQL_TSI_HOUR = 57922 -const SQL_TSI_DAY = 57923 -const SQL_TSI_WEEK = 57924 -const SQL_TSI_MONTH = 57925 -const SQL_TSI_QUARTER = 57926 -const SQL_TSI_YEAR = 57927 -const SQL_TSI_SECOND = 57928 -const SQL_TSI_MINUTE = 57929 -const RECURSIVE = 57930 -const CONFIG = 57931 -const DRAINER = 57932 -const DAEMON = 57933 -const PAUSE = 57934 -const CANCEL = 57935 -const RESUME = 57936 -const SCHEDULE = 57937 -const TIMEZONE = 57938 -const TIMEOUT = 57939 -const TASK = 57940 -const MATCH = 57941 -const AGAINST = 57942 -const BOOLEAN = 57943 -const LANGUAGE = 57944 -const QUERY = 57945 -const EXPANSION = 57946 -const WITHOUT = 57947 -const VALIDATION = 57948 -const UPGRADE = 57949 -const RETRY = 57950 -const ADDDATE = 57951 -const BIT_AND = 57952 -const BIT_OR = 57953 -const BIT_XOR = 57954 -const CAST = 57955 -const COUNT = 57956 -const APPROX_COUNT = 57957 -const APPROX_COUNT_DISTINCT = 57958 -const SERIAL_EXTRACT = 57959 -const APPROX_PERCENTILE = 57960 -const CURDATE = 57961 -const CURTIME = 57962 -const DATE_ADD = 57963 -const DATE_SUB = 57964 -const EXTRACT = 57965 -const GROUP_CONCAT = 57966 -const MAX = 57967 -const MID = 57968 -const MIN = 57969 -const NOW = 57970 -const POSITION = 57971 -const SESSION_USER = 57972 -const STD = 57973 -const STDDEV = 57974 -const MEDIAN = 57975 -const CLUSTER_CENTERS = 57976 -const KMEANS = 57977 -const STDDEV_POP = 57978 -const STDDEV_SAMP = 57979 -const SUBDATE = 57980 -const SUBSTR = 57981 -const SUBSTRING = 57982 -const SUM = 57983 -const SYSDATE = 57984 -const SYSTEM_USER = 57985 -const TRANSLATE = 57986 -const TRIM = 57987 -const VARIANCE = 57988 -const VAR_POP = 57989 -const VAR_SAMP = 57990 -const AVG = 57991 -const RANK = 57992 -const ROW_NUMBER = 57993 -const DENSE_RANK = 57994 -const CUME_DIST = 57995 -const BIT_CAST = 57996 -const LAG = 57997 -const LEAD = 57998 -const FIRST_VALUE = 57999 -const LAST_VALUE = 58000 -const NTH_VALUE = 58001 -const NTILE = 58002 -const PERCENT_RANK = 58003 -const BITMAP_BIT_POSITION = 58004 -const BITMAP_BUCKET_NUMBER = 58005 -const BITMAP_COUNT = 58006 -const BITMAP_CONSTRUCT_AGG = 58007 -const BITMAP_OR_AGG = 58008 -const JSON_ARRAYAGG = 58009 -const JSON_OBJECTAGG = 58010 -const GET_FORMAT = 58011 -const SRID = 58012 -const NEXTVAL = 58013 -const SETVAL = 58014 -const CURRVAL = 58015 -const LASTVAL = 58016 -const ROW = 58017 -const OUTFILE = 58018 -const HEADER = 58019 -const MAX_FILE_SIZE = 58020 -const FORCE_QUOTE = 58021 -const PARALLEL = 58022 -const STRICT = 58023 -const SPLITSIZE = 58024 -const UNUSED = 58025 -const BINDINGS = 58026 -const GENERATED = 58027 -const ALWAYS = 58028 -const STORED = 58029 -const VIRTUAL = 58030 -const DO = 58031 -const PERFORM = 58032 -const DECLARE = 58033 -const LOOP = 58034 -const WHILE = 58035 -const LEAVE = 58036 -const ITERATE = 58037 -const UNTIL = 58038 -const CALL = 58039 -const PREV = 58040 -const SLIDING = 58041 -const FILL = 58042 -const GAPFILL = 58043 -const SPBEGIN = 58044 -const BACKEND = 58045 -const SERVERS = 58046 -const HANDLER = 58047 -const PERCENT = 58048 -const SAMPLE = 58049 -const MO_TS = 58050 -const PITR = 58051 -const RECOVERY_WINDOW = 58052 -const INTERNAL = 58053 -const CDC_TASK_NAME = 58054 -const CDC = 58055 -const ICEBERG = 58056 -const CATALOG = 58057 -const CATALOGS = 58058 -const NAMESPACE = 58059 -const NAMESPACES = 58060 -const REF = 58061 -const FOR_ICEBERG = 58062 -const MONGODB = 58063 -const MONGODB_PATH = 58064 -const MONGODB_CONVERT = 58065 -const CONNECTIONS = 58066 -const DATASTREAM = 58067 -const GROUPING = 58068 -const SETS = 58069 -const CUBE = 58070 -const ROLLUP = 58071 -const LOGSERVICE = 58072 -const REPLICAS = 58073 -const STORES = 58074 -const SETTINGS = 58075 -const DUMP = 58076 -const METADATA = 58077 -const KILL = 58078 -const BACKUP = 58079 -const FILESYSTEM = 58080 -const PARALLELISM = 58081 -const RESTORE = 58082 -const QUERY_RESULT = 58083 -const RETURNING = 58084 -const ARRAY = 58085 +const BM25 = 57726 +const ZONEMAP = 57727 +const LEADING = 57728 +const BOTH = 57729 +const TRAILING = 57730 +const UNKNOWN = 57731 +const LISTS = 57732 +const OP_TYPE = 57733 +const REINDEX = 57734 +const EF_SEARCH = 57735 +const EF_CONSTRUCTION = 57736 +const M = 57737 +const ASYNC = 57738 +const FORCE_SYNC = 57739 +const AUTO_UPDATE = 57740 +const INTERMEDIATE_GRAPH_DEGREE = 57741 +const GRAPH_DEGREE = 57742 +const QUANTIZATION = 57743 +const BITS_PER_CODE = 57744 +const DISTRIBUTION_MODE = 57745 +const ITOPK_SIZE = 57746 +const INCLUDE = 57747 +const KMEANS_TRAIN_PERCENT = 57748 +const KMEANS_MAX_ITERATION = 57749 +const MAX_INDEX_CAPACITY = 57750 +const MAX_POSTINGS_CAPACITY = 57751 +const QUANTIZER_TRAIN_LIMIT = 57752 +const FULLTEXT2 = 57753 +const POSITION_FREE = 57754 +const EXPIRE = 57755 +const ACCOUNT = 57756 +const ACCOUNTS = 57757 +const UNLOCK = 57758 +const DAY = 57759 +const NEVER = 57760 +const PUMP = 57761 +const MYSQL_COMPATIBILITY_MODE = 57762 +const UNIQUE_CHECK_ON_AUTOINCR = 57763 +const MODIFY = 57764 +const CHANGE = 57765 +const SECOND = 57766 +const ASCII = 57767 +const COALESCE = 57768 +const COLLATION = 57769 +const HOUR = 57770 +const MICROSECOND = 57771 +const MINUTE = 57772 +const MONTH = 57773 +const QUARTER = 57774 +const REPEAT = 57775 +const REVERSE = 57776 +const ROW_COUNT = 57777 +const WEEK = 57778 +const REVOKE = 57779 +const FUNCTION = 57780 +const PRIVILEGES = 57781 +const TABLESPACE = 57782 +const EXECUTE = 57783 +const SUPER = 57784 +const GRANT = 57785 +const OPTION = 57786 +const REFERENCES = 57787 +const REPLICATION = 57788 +const SLAVE = 57789 +const CLIENT = 57790 +const USAGE = 57791 +const RELOAD = 57792 +const FILE = 57793 +const FILES = 57794 +const TEMPORARY = 57795 +const ROUTINE = 57796 +const EVENT = 57797 +const SHUTDOWN = 57798 +const NULLX = 57799 +const AUTO_INCREMENT = 57800 +const APPROXNUM = 57801 +const ENGINES = 57802 +const LOW_CARDINALITY = 57803 +const AUTOEXTEND_SIZE = 57804 +const ADMIN_NAME = 57805 +const RANDOM = 57806 +const SUSPEND = 57807 +const ATTRIBUTE = 57808 +const HISTORY = 57809 +const REUSE = 57810 +const CURRENT = 57811 +const OPTIONAL = 57812 +const FAILED_LOGIN_ATTEMPTS = 57813 +const PASSWORD_LOCK_TIME = 57814 +const UNBOUNDED = 57815 +const SECONDARY = 57816 +const RESTRICTED = 57817 +const USER = 57818 +const IDENTIFIED = 57819 +const CIPHER = 57820 +const ISSUER = 57821 +const X509 = 57822 +const SUBJECT = 57823 +const SAN = 57824 +const REQUIRE = 57825 +const SSL = 57826 +const NONE = 57827 +const PASSWORD = 57828 +const SHARED = 57829 +const EXCLUSIVE = 57830 +const MAX_QUERIES_PER_HOUR = 57831 +const MAX_UPDATES_PER_HOUR = 57832 +const MAX_CONNECTIONS_PER_HOUR = 57833 +const MAX_USER_CONNECTIONS = 57834 +const FORMAT = 57835 +const VERBOSE = 57836 +const CONNECTION = 57837 +const TRIGGERS = 57838 +const PROFILES = 57839 +const PROFILE = 57840 +const LOAD = 57841 +const INLINE = 57842 +const INFILE = 57843 +const TERMINATED = 57844 +const OPTIONALLY = 57845 +const ENCLOSED = 57846 +const ESCAPED = 57847 +const STARTING = 57848 +const LINES = 57849 +const ROWS = 57850 +const IMPORT = 57851 +const DISCARD = 57852 +const JSONTYPE = 57853 +const MODUMP = 57854 +const OVER = 57855 +const PRECEDING = 57856 +const FOLLOWING = 57857 +const GROUPS = 57858 +const DATABASES = 57859 +const TABLES = 57860 +const SEQUENCES = 57861 +const EXTENDED = 57862 +const FULL = 57863 +const PROCESSLIST = 57864 +const FIELDS = 57865 +const COLUMNS = 57866 +const OPEN = 57867 +const ERRORS = 57868 +const WARNINGS = 57869 +const INDEXES = 57870 +const SCHEMAS = 57871 +const NODE = 57872 +const LOCKS = 57873 +const ROLES = 57874 +const RULE = 57875 +const RULES = 57876 +const TABLE_NUMBER = 57877 +const COLUMN_NUMBER = 57878 +const TABLE_VALUES = 57879 +const TABLE_SIZE = 57880 +const TASKS = 57881 +const RUNS = 57882 +const NAMES = 57883 +const GLOBAL = 57884 +const PERSIST = 57885 +const SESSION = 57886 +const ISOLATION = 57887 +const LEVEL = 57888 +const READ = 57889 +const WRITE = 57890 +const ONLY = 57891 +const REPEATABLE = 57892 +const COMMITTED = 57893 +const UNCOMMITTED = 57894 +const SERIALIZABLE = 57895 +const LOCAL = 57896 +const EVENTS = 57897 +const PLUGINS = 57898 +const CURRENT_TIMESTAMP = 57899 +const DATABASE = 57900 +const CURRENT_TIME = 57901 +const LOCALTIME = 57902 +const LOCALTIMESTAMP = 57903 +const UTC_DATE = 57904 +const UTC_TIME = 57905 +const UTC_TIMESTAMP = 57906 +const REPLACE = 57907 +const CONVERT = 57908 +const SEPARATOR = 57909 +const TIMESTAMPDIFF = 57910 +const TIMESTAMPADD = 57911 +const CURRENT_DATE = 57912 +const CURRENT_USER = 57913 +const CURRENT_ROLE = 57914 +const SECOND_MICROSECOND = 57915 +const MINUTE_MICROSECOND = 57916 +const MINUTE_SECOND = 57917 +const HOUR_MICROSECOND = 57918 +const HOUR_SECOND = 57919 +const HOUR_MINUTE = 57920 +const DAY_MICROSECOND = 57921 +const DAY_SECOND = 57922 +const DAY_MINUTE = 57923 +const DAY_HOUR = 57924 +const YEAR_MONTH = 57925 +const SQL_TSI_HOUR = 57926 +const SQL_TSI_DAY = 57927 +const SQL_TSI_WEEK = 57928 +const SQL_TSI_MONTH = 57929 +const SQL_TSI_QUARTER = 57930 +const SQL_TSI_YEAR = 57931 +const SQL_TSI_SECOND = 57932 +const SQL_TSI_MINUTE = 57933 +const RECURSIVE = 57934 +const CONFIG = 57935 +const DRAINER = 57936 +const DAEMON = 57937 +const PAUSE = 57938 +const CANCEL = 57939 +const RESUME = 57940 +const SCHEDULE = 57941 +const TIMEZONE = 57942 +const TIMEOUT = 57943 +const TASK = 57944 +const MATCH = 57945 +const AGAINST = 57946 +const BOOLEAN = 57947 +const LANGUAGE = 57948 +const QUERY = 57949 +const EXPANSION = 57950 +const WITHOUT = 57951 +const VALIDATION = 57952 +const UPGRADE = 57953 +const RETRY = 57954 +const ADDDATE = 57955 +const BIT_AND = 57956 +const BIT_OR = 57957 +const BIT_XOR = 57958 +const CAST = 57959 +const COUNT = 57960 +const APPROX_COUNT = 57961 +const APPROX_COUNT_DISTINCT = 57962 +const SERIAL_EXTRACT = 57963 +const APPROX_PERCENTILE = 57964 +const CURDATE = 57965 +const CURTIME = 57966 +const DATE_ADD = 57967 +const DATE_SUB = 57968 +const EXTRACT = 57969 +const GROUP_CONCAT = 57970 +const MAX = 57971 +const MID = 57972 +const MIN = 57973 +const NOW = 57974 +const POSITION = 57975 +const SESSION_USER = 57976 +const STD = 57977 +const STDDEV = 57978 +const MEDIAN = 57979 +const CLUSTER_CENTERS = 57980 +const KMEANS = 57981 +const STDDEV_POP = 57982 +const STDDEV_SAMP = 57983 +const SUBDATE = 57984 +const SUBSTR = 57985 +const SUBSTRING = 57986 +const SUM = 57987 +const SYSDATE = 57988 +const SYSTEM_USER = 57989 +const TRANSLATE = 57990 +const TRIM = 57991 +const VARIANCE = 57992 +const VAR_POP = 57993 +const VAR_SAMP = 57994 +const AVG = 57995 +const RANK = 57996 +const ROW_NUMBER = 57997 +const DENSE_RANK = 57998 +const CUME_DIST = 57999 +const BIT_CAST = 58000 +const LAG = 58001 +const LEAD = 58002 +const FIRST_VALUE = 58003 +const LAST_VALUE = 58004 +const NTH_VALUE = 58005 +const NTILE = 58006 +const PERCENT_RANK = 58007 +const BITMAP_BIT_POSITION = 58008 +const BITMAP_BUCKET_NUMBER = 58009 +const BITMAP_COUNT = 58010 +const BITMAP_CONSTRUCT_AGG = 58011 +const BITMAP_OR_AGG = 58012 +const JSON_ARRAYAGG = 58013 +const JSON_OBJECTAGG = 58014 +const GET_FORMAT = 58015 +const SRID = 58016 +const NEXTVAL = 58017 +const SETVAL = 58018 +const CURRVAL = 58019 +const LASTVAL = 58020 +const ROW = 58021 +const OUTFILE = 58022 +const HEADER = 58023 +const MAX_FILE_SIZE = 58024 +const FORCE_QUOTE = 58025 +const PARALLEL = 58026 +const STRICT = 58027 +const SPLITSIZE = 58028 +const UNUSED = 58029 +const BINDINGS = 58030 +const GENERATED = 58031 +const ALWAYS = 58032 +const STORED = 58033 +const VIRTUAL = 58034 +const DO = 58035 +const PERFORM = 58036 +const DECLARE = 58037 +const LOOP = 58038 +const WHILE = 58039 +const LEAVE = 58040 +const ITERATE = 58041 +const UNTIL = 58042 +const CALL = 58043 +const PREV = 58044 +const SLIDING = 58045 +const FILL = 58046 +const GAPFILL = 58047 +const SPBEGIN = 58048 +const BACKEND = 58049 +const SERVERS = 58050 +const HANDLER = 58051 +const PERCENT = 58052 +const SAMPLE = 58053 +const MO_TS = 58054 +const PITR = 58055 +const RECOVERY_WINDOW = 58056 +const INTERNAL = 58057 +const CDC_TASK_NAME = 58058 +const CDC = 58059 +const ICEBERG = 58060 +const CATALOG = 58061 +const CATALOGS = 58062 +const NAMESPACE = 58063 +const NAMESPACES = 58064 +const REF = 58065 +const FOR_ICEBERG = 58066 +const MONGODB = 58067 +const MONGODB_PATH = 58068 +const MONGODB_CONVERT = 58069 +const CONNECTIONS = 58070 +const DATASTREAM = 58071 +const GROUPING = 58072 +const SETS = 58073 +const CUBE = 58074 +const ROLLUP = 58075 +const LOGSERVICE = 58076 +const REPLICAS = 58077 +const STORES = 58078 +const SETTINGS = 58079 +const DUMP = 58080 +const METADATA = 58081 +const KILL = 58082 +const BACKUP = 58083 +const FILESYSTEM = 58084 +const PARALLELISM = 58085 +const RESTORE = 58086 +const QUERY_RESULT = 58087 +const RETURNING = 58088 +const ARRAY = 58089 var yyToknames = [...]string{ "$end", @@ -1207,6 +1211,7 @@ var yyToknames = [...]string{ "HNSW", "CAGRA", "IVFPQ", + "BM25", "ZONEMAP", "LEADING", "BOTH", @@ -1231,7 +1236,10 @@ var yyToknames = [...]string{ "KMEANS_TRAIN_PERCENT", "KMEANS_MAX_ITERATION", "MAX_INDEX_CAPACITY", + "MAX_POSTINGS_CAPACITY", "QUANTIZER_TRAIN_LIMIT", + "FULLTEXT2", + "POSITION_FREE", "EXPIRE", "ACCOUNT", "ACCOUNTS", @@ -1580,7 +1588,7 @@ const yyEofCode = 1 const yyErrCode = 2 const yyInitialStackSize = 16 -//line mysql_sql.y:15473 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:15563 //line yacctab:1 var yyExca = [...]int{ @@ -1588,8304 +1596,8361 @@ var yyExca = [...]int{ 1, -1, -2, 0, -1, 150, - 46, 732, - 278, 732, - 305, 739, - 306, 739, - 541, 732, - -2, 770, + 46, 733, + 278, 733, + 305, 740, + 306, 740, + 545, 733, + -2, 771, -1, 169, - 11, 949, - 24, 949, - -2, 942, + 11, 950, + 24, 950, + -2, 943, -1, 195, - 276, 1496, - 278, 1337, - -2, 1408, + 276, 1503, + 278, 1338, + -2, 1415, -1, 269, - 764, 2422, - -2, 606, - -1, 642, - 764, 2548, + 768, 2435, + -2, 607, + -1, 645, + 768, 2561, -2, 447, - -1, 700, - 764, 2607, + -1, 703, + 768, 2620, -2, 445, - -1, 701, - 764, 2608, + -1, 704, + 768, 2621, -2, 446, - -1, 702, - 764, 2609, + -1, 705, + 768, 2622, -2, 448, - -1, 854, - 536, 770, - 537, 770, - -2, 733, - -1, 926, + -1, 857, + 540, 771, + 541, 771, + -2, 734, + -1, 929, 357, 210, - 513, 210, - 514, 210, - -2, 2273, - -1, 995, - 89, 2022, - -2, 2484, - -1, 996, - 89, 2040, - -2, 2453, - -1, 1000, - 89, 2041, - -2, 2483, - -1, 1046, - 89, 1943, - -2, 2701, - -1, 1047, - 89, 1944, - -2, 2700, - -1, 1048, - 89, 1945, - -2, 2690, + 517, 210, + 518, 210, + -2, 2283, + -1, 998, + 89, 2032, + -2, 2497, + -1, 999, + 89, 2050, + -2, 2466, + -1, 1003, + 89, 2051, + -2, 2496, -1, 1049, - 89, 2661, - -2, 2681, + 89, 1953, + -2, 2714, -1, 1050, - 89, 2662, - -2, 2682, + 89, 1954, + -2, 2713, -1, 1051, - 89, 2663, - -2, 2692, + 89, 1955, + -2, 2703, -1, 1052, - 89, 2664, - -2, 2670, + 89, 2674, + -2, 2694, -1, 1053, - 89, 2665, - -2, 2679, + 89, 2675, + -2, 2695, -1, 1054, - 89, 2666, - -2, 2694, + 89, 2676, + -2, 2705, -1, 1055, - 89, 2667, - -2, 2699, + 89, 2677, + -2, 2683, -1, 1056, - 89, 2668, - -2, 2704, + 89, 2678, + -2, 2692, -1, 1057, - 89, 2669, - -2, 2705, + 89, 2679, + -2, 2707, -1, 1058, - 89, 2018, - -2, 2522, + 89, 2680, + -2, 2712, -1, 1059, - 89, 2019, - -2, 2256, + 89, 2681, + -2, 2717, -1, 1060, - 89, 2020, - -2, 2531, + 89, 2682, + -2, 2718, -1, 1061, - 89, 2021, - -2, 2267, - -1, 1063, - 89, 2024, - -2, 2275, - -1, 1065, - 89, 2026, - -2, 2556, - -1, 1067, 89, 2028, - -2, 2299, - -1, 1069, + -2, 2535, + -1, 1062, + 89, 2029, + -2, 2266, + -1, 1063, 89, 2030, - -2, 2568, - -1, 1070, + -2, 2544, + -1, 1064, 89, 2031, - -2, 2567, - -1, 1071, - 89, 2032, - -2, 2378, - -1, 1072, - 89, 2033, - -2, 2479, - -1, 1075, + -2, 2277, + -1, 1066, + 89, 2034, + -2, 2285, + -1, 1068, 89, 2036, - -2, 2579, - -1, 1077, + -2, 2569, + -1, 1070, 89, 2038, - -2, 2582, - -1, 1078, - 89, 2039, - -2, 2584, - -1, 1079, + -2, 2309, + -1, 1072, + 89, 2040, + -2, 2581, + -1, 1073, + 89, 2041, + -2, 2580, + -1, 1074, 89, 2042, - -2, 2591, - -1, 1080, + -2, 2391, + -1, 1075, 89, 2043, - -2, 2462, + -2, 2492, + -1, 1078, + 89, 2046, + -2, 2592, + -1, 1080, + 89, 2048, + -2, 2595, -1, 1081, - 89, 2044, - -2, 2509, + 89, 2049, + -2, 2597, -1, 1082, - 89, 2045, - -2, 2473, + 89, 2052, + -2, 2604, -1, 1083, - 89, 2046, - -2, 2499, - -1, 1094, - 89, 1919, - -2, 2695, - -1, 1095, - 89, 1920, - -2, 2696, - -1, 1096, - 89, 1921, - -2, 2697, - -1, 1220, - 131, 2256, - 142, 2256, - 174, 2256, - -2, 2222, - -1, 1365, - 24, 980, - -2, 921, - -1, 1565, - 11, 949, - 24, 949, - -2, 1774, - -1, 1665, - 24, 980, - -2, 921, - -1, 1967, - 46, 732, - 278, 732, - 305, 739, - 306, 739, - 541, 732, - -2, 770, - -1, 2072, - 89, 2092, - -2, 2481, - -1, 2073, - 89, 2093, - -2, 2482, - -1, 2357, - 11, 949, - 24, 949, - -2, 1118, - -1, 2814, - 90, 1193, - -2, 1199, - -1, 2831, - 114, 1403, - 161, 1403, - 209, 1403, - 212, 1403, - 318, 1403, - -2, 1396, - -1, 3301, - 90, 2208, - 175, 2208, - -2, 2464, - -1, 3302, - 90, 2208, - 175, 2208, - -2, 2463, - -1, 3304, - 90, 2159, - 175, 2159, - -2, 2450, - -1, 3305, - 90, 2160, - 175, 2160, - -2, 2455, + 89, 2053, + -2, 2475, + -1, 1084, + 89, 2054, + -2, 2522, + -1, 1085, + 89, 2055, + -2, 2486, + -1, 1086, + 89, 2056, + -2, 2512, + -1, 1097, + 89, 1929, + -2, 2708, + -1, 1098, + 89, 1930, + -2, 2709, + -1, 1099, + 89, 1931, + -2, 2710, + -1, 1224, + 131, 2266, + 142, 2266, + 174, 2266, + -2, 2232, + -1, 1369, + 24, 981, + -2, 922, + -1, 1569, + 11, 950, + 24, 950, + -2, 1784, + -1, 1669, + 24, 981, + -2, 922, + -1, 1971, + 46, 733, + 278, 733, + 305, 740, + 306, 740, + 545, 733, + -2, 771, + -1, 2076, + 89, 2102, + -2, 2494, + -1, 2077, + 89, 2103, + -2, 2495, + -1, 2361, + 11, 950, + 24, 950, + -2, 1119, + -1, 2818, + 90, 1194, + -2, 1200, + -1, 2835, + 114, 1410, + 161, 1410, + 209, 1410, + 212, 1410, + 318, 1410, + -2, 1403, -1, 3306, - 90, 2161, - 175, 2161, - -2, 2365, + 90, 2218, + 175, 2218, + -2, 2477, -1, 3307, - 90, 2162, - 175, 2162, - -2, 2358, - -1, 3308, - 90, 2163, - 175, 2163, - -2, 2241, + 90, 2218, + 175, 2218, + -2, 2476, -1, 3309, - 90, 2164, - 175, 2164, - -2, 2452, + 90, 2169, + 175, 2169, + -2, 2463, -1, 3310, - 90, 2165, - 175, 2165, - -2, 2363, + 90, 2170, + 175, 2170, + -2, 2468, -1, 3311, - 90, 2166, - 175, 2166, - -2, 2357, + 90, 2171, + 175, 2171, + -2, 2378, -1, 3312, - 90, 2167, - 175, 2167, - -2, 2336, + 90, 2172, + 175, 2172, + -2, 2371, -1, 3313, - 90, 2208, - 175, 2208, - -2, 2337, + 90, 2173, + 175, 2173, + -2, 2251, -1, 3314, - 90, 2208, - 175, 2208, - -2, 2338, + 90, 2174, + 175, 2174, + -2, 2465, -1, 3315, - 90, 2208, - 175, 2208, - -2, 2339, + 90, 2175, + 175, 2175, + -2, 2376, -1, 3316, - 90, 2208, - 175, 2208, - -2, 2340, + 90, 2176, + 175, 2176, + -2, 2370, -1, 3317, - 90, 2208, - 175, 2208, - -2, 2341, + 90, 2177, + 175, 2177, + -2, 2347, -1, 3318, - 90, 2208, - 175, 2208, - -2, 2342, + 90, 2218, + 175, 2218, + -2, 2348, + -1, 3319, + 90, 2218, + 175, 2218, + -2, 2349, -1, 3320, - 90, 2176, - 175, 2176, - -2, 2499, + 90, 2218, + 175, 2218, + -2, 2350, -1, 3321, - 90, 2147, - 175, 2147, - -2, 2484, + 90, 2218, + 175, 2218, + -2, 2351, -1, 3322, - 90, 2206, - 175, 2206, - -2, 2453, + 90, 2218, + 175, 2218, + -2, 2352, -1, 3323, - 90, 2206, - 175, 2206, - -2, 2483, - -1, 3324, - 90, 2206, - 175, 2206, - -2, 2276, + 90, 2218, + 175, 2218, + -2, 2353, -1, 3325, - 90, 2204, - 175, 2204, - -2, 2473, + 90, 2186, + 175, 2186, + -2, 2512, -1, 3326, - 89, 2127, - 90, 2127, - 164, 2127, - 165, 2127, - 167, 2127, - 175, 2127, - -2, 2240, + 90, 2157, + 175, 2157, + -2, 2497, -1, 3327, - 89, 2128, - 90, 2128, - 164, 2128, - 165, 2128, - 167, 2128, - 175, 2128, - -2, 2242, + 90, 2216, + 175, 2216, + -2, 2466, -1, 3328, - 89, 2129, - 90, 2129, - 164, 2129, - 165, 2129, - 167, 2129, - 175, 2129, - -2, 2527, + 90, 2216, + 175, 2216, + -2, 2496, -1, 3329, - 89, 2131, - 90, 2131, - 164, 2131, - 165, 2131, - 167, 2131, - 175, 2131, - -2, 2454, + 90, 2216, + 175, 2216, + -2, 2286, -1, 3330, - 89, 2133, - 90, 2133, - 164, 2133, - 165, 2133, - 167, 2133, - 175, 2133, - -2, 2432, + 90, 2214, + 175, 2214, + -2, 2486, -1, 3331, - 89, 2135, - 90, 2135, - 164, 2135, - 165, 2135, - 167, 2135, - 175, 2135, - -2, 2364, - -1, 3332, 89, 2137, 90, 2137, 164, 2137, 165, 2137, 167, 2137, 175, 2137, - -2, 2324, - -1, 3333, + -2, 2250, + -1, 3332, 89, 2138, 90, 2138, 164, 2138, 165, 2138, 167, 2138, 175, 2138, - -2, 2325, + -2, 2252, + -1, 3333, + 89, 2139, + 90, 2139, + 164, 2139, + 165, 2139, + 167, 2139, + 175, 2139, + -2, 2540, -1, 3334, - 89, 2140, - 90, 2140, - 164, 2140, - 165, 2140, - 167, 2140, - 175, 2140, - -2, 2239, + 89, 2141, + 90, 2141, + 164, 2141, + 165, 2141, + 167, 2141, + 175, 2141, + -2, 2467, -1, 3335, - 90, 2211, - 164, 2211, - 165, 2211, - 167, 2211, - 175, 2211, - -2, 2283, + 89, 2143, + 90, 2143, + 164, 2143, + 165, 2143, + 167, 2143, + 175, 2143, + -2, 2445, -1, 3336, - 90, 2211, - 164, 2211, - 165, 2211, - 167, 2211, - 175, 2211, - -2, 2300, + 89, 2145, + 90, 2145, + 164, 2145, + 165, 2145, + 167, 2145, + 175, 2145, + -2, 2377, -1, 3337, - 90, 2214, - 164, 2214, - 165, 2214, - 167, 2214, - 175, 2214, - -2, 2277, + 89, 2147, + 90, 2147, + 164, 2147, + 165, 2147, + 167, 2147, + 175, 2147, + -2, 2335, -1, 3338, - 90, 2214, - 164, 2214, - 165, 2214, - 167, 2214, - 175, 2214, - -2, 2383, + 89, 2148, + 90, 2148, + 164, 2148, + 165, 2148, + 167, 2148, + 175, 2148, + -2, 2336, -1, 3339, - 90, 2211, - 164, 2211, - 165, 2211, - 167, 2211, - 175, 2211, - -2, 2413, + 89, 2150, + 90, 2150, + 164, 2150, + 165, 2150, + 167, 2150, + 175, 2150, + -2, 2249, -1, 3340, - 90, 2184, - 175, 2184, - -2, 2304, + 90, 2221, + 164, 2221, + 165, 2221, + 167, 2221, + 175, 2221, + -2, 2293, -1, 3341, - 90, 2185, - 175, 2185, - -2, 2398, + 90, 2221, + 164, 2221, + 165, 2221, + 167, 2221, + 175, 2221, + -2, 2310, -1, 3342, - 90, 2186, - 175, 2186, - -2, 2354, + 90, 2224, + 164, 2224, + 165, 2224, + 167, 2224, + 175, 2224, + -2, 2287, -1, 3343, - 90, 2187, - 175, 2187, - -2, 2399, + 90, 2224, + 164, 2224, + 165, 2224, + 167, 2224, + 175, 2224, + -2, 2396, -1, 3344, - 90, 2188, - 175, 2188, - -2, 2305, + 90, 2221, + 164, 2221, + 165, 2221, + 167, 2221, + 175, 2221, + -2, 2426, -1, 3345, - 90, 2189, - 175, 2189, - -2, 2370, - -1, 3346, - 90, 2190, - 175, 2190, - -2, 2369, - -1, 3347, - 90, 2191, - 175, 2191, - -2, 2371, - -1, 3348, - 90, 2192, - 175, 2192, - -2, 2307, - -1, 3349, - 90, 2193, - 175, 2193, - -2, 2306, - -1, 3350, 90, 2194, 175, 2194, - -2, 2308, - -1, 3351, + -2, 2314, + -1, 3346, 90, 2195, 175, 2195, - -2, 2309, - -1, 3352, + -2, 2411, + -1, 3347, 90, 2196, 175, 2196, - -2, 2310, - -1, 3353, + -2, 2367, + -1, 3348, 90, 2197, 175, 2197, - -2, 2311, - -1, 3354, + -2, 2412, + -1, 3349, 90, 2198, 175, 2198, - -2, 2312, - -1, 3355, + -2, 2315, + -1, 3350, 90, 2199, 175, 2199, - -2, 2313, - -1, 3356, + -2, 2383, + -1, 3351, 90, 2200, 175, 2200, - -2, 2314, - -1, 3357, + -2, 2382, + -1, 3352, 90, 2201, 175, 2201, - -2, 2315, - -1, 3393, - 114, 1403, - 161, 1403, - 209, 1403, - 212, 1403, - 318, 1403, - -2, 1397, - -1, 3433, - 87, 836, - 175, 836, - -2, 1640, - -1, 3927, - 212, 1403, - 342, 1737, - -2, 1703, - -1, 3972, - 11, 949, - 24, 949, - -2, 1774, - -1, 4156, - 114, 1403, - 161, 1403, - 209, 1403, - 212, 1403, - -2, 1572, - -1, 4163, - 114, 1403, - 161, 1403, - 209, 1403, - 212, 1403, - -2, 1572, - -1, 4178, - 87, 836, - 175, 836, - -2, 1640, - -1, 4215, - 212, 1403, - 342, 1737, - -2, 1704, - -1, 4411, - 114, 1403, - 161, 1403, - 209, 1403, - 212, 1403, - -2, 1573, - -1, 4456, - 90, 1514, - 175, 1514, - -2, 1403, - -1, 4661, - 90, 1514, - 175, 1514, - -2, 1403, - -1, 4888, - 90, 1518, - 175, 1518, - -2, 1403, - -1, 4946, - 90, 1519, - 175, 1519, - -2, 1403, + -2, 2384, + -1, 3353, + 90, 2202, + 175, 2202, + -2, 2317, + -1, 3354, + 90, 2203, + 175, 2203, + -2, 2316, + -1, 3355, + 90, 2204, + 175, 2204, + -2, 2318, + -1, 3356, + 90, 2205, + 175, 2205, + -2, 2319, + -1, 3357, + 90, 2206, + 175, 2206, + -2, 2320, + -1, 3358, + 90, 2207, + 175, 2207, + -2, 2321, + -1, 3359, + 90, 2208, + 175, 2208, + -2, 2322, + -1, 3360, + 90, 2209, + 175, 2209, + -2, 2323, + -1, 3361, + 90, 2210, + 175, 2210, + -2, 2324, + -1, 3362, + 90, 2211, + 175, 2211, + -2, 2325, + -1, 3398, + 114, 1410, + 161, 1410, + 209, 1410, + 212, 1410, + 318, 1410, + -2, 1404, + -1, 3438, + 87, 837, + 175, 837, + -2, 1647, + -1, 3933, + 212, 1410, + 342, 1746, + -2, 1712, + -1, 3980, + 11, 950, + 24, 950, + -2, 1784, + -1, 4164, + 114, 1410, + 161, 1410, + 209, 1410, + 212, 1410, + -2, 1579, + -1, 4171, + 114, 1410, + 161, 1410, + 209, 1410, + 212, 1410, + -2, 1579, + -1, 4186, + 87, 837, + 175, 837, + -2, 1647, + -1, 4223, + 212, 1410, + 342, 1746, + -2, 1713, + -1, 4421, + 114, 1410, + 161, 1410, + 209, 1410, + 212, 1410, + -2, 1580, + -1, 4466, + 90, 1521, + 175, 1521, + -2, 1410, + -1, 4678, + 90, 1521, + 175, 1521, + -2, 1410, + -1, 4915, + 90, 1525, + 175, 1525, + -2, 1410, + -1, 4973, + 90, 1526, + 175, 1526, + -2, 1410, } const yyPrivate = 57344 -const yyLast = 77288 +const yyLast = 77849 var yyAct = [...]int{ - 961, 2845, 937, 5003, 4985, 963, 1916, 4969, 4995, 3485, - 257, 4898, 2380, 4200, 4892, 1915, 3605, 2052, 4308, 4261, - 4902, 3950, 4903, 4891, 4661, 3035, 3913, 4789, 3789, 4735, - 946, 4844, 4550, 4017, 4488, 4231, 3088, 3280, 3479, 4595, - 2001, 4726, 4303, 4018, 3791, 1603, 4645, 2119, 4660, 4765, - 1811, 4131, 992, 4401, 4015, 4439, 755, 3482, 4625, 3276, - 1452, 1858, 4317, 3641, 864, 2121, 244, 5, 44, 4139, - 4736, 4738, 4145, 4216, 777, 2106, 1458, 4426, 4183, 3436, - 2913, 1366, 4408, 4091, 3203, 793, 803, 813, 4413, 3841, - 813, 3278, 939, 3922, 3407, 2055, 3204, 3859, 3078, 2841, - 4384, 1406, 168, 3999, 1423, 3606, 2471, 2358, 2474, 1371, - 3845, 4129, 3574, 889, 3508, 3942, 3931, 3469, 3604, 3924, - 3123, 3601, 2351, 2330, 4164, 3463, 3288, 3296, 3467, 3969, - 2526, 4083, 2639, 2125, 2568, 2102, 3379, 3821, 3085, 3592, - 3810, 3823, 3461, 884, 3860, 3930, 1804, 3862, 3861, 3870, - 2861, 1925, 2237, 880, 809, 2842, 3771, 2849, 1908, 3394, - 2847, 1903, 2776, 3122, 2564, 929, 3058, 2672, 2531, 2467, - 2775, 934, 930, 242, 2561, 3857, 2635, 3008, 3294, 2634, - 3510, 1139, 2006, 2370, 1899, 2914, 1714, 2352, 3490, 3410, - 3367, 2359, 3361, 2360, 1904, 2892, 3440, 793, 1180, 2860, - 2916, 1921, 2831, 4, 3439, 1427, 253, 7, 2051, 252, - 6, 2103, 2301, 2120, 2668, 1928, 1288, 2047, 938, 2636, - 2848, 2851, 1859, 776, 1834, 1751, 1789, 2822, 1783, 1213, - 1820, 755, 2909, 2778, 928, 2336, 2113, 2320, 2088, 2038, - 2825, 1404, 1387, 26, 2548, 1866, 2005, 877, 2292, 2296, - 947, 2046, 1409, 1212, 1231, 1441, 16, 257, 1788, 257, - 1462, 1278, 1279, 38, 2300, 1785, 1179, 1744, 1105, 793, - 1689, 797, 1104, 3009, 1102, 2126, 1229, 77, 930, 887, - 42, 1230, 27, 1453, 886, 754, 77, 239, 28, 243, - 19, 32, 10, 812, 233, 1219, 794, 1437, 1177, 1258, - 1663, 1162, 883, 151, 849, 2615, 1604, 4750, 5, 1172, - 4621, 80, 79, 76, 1756, 2340, 2344, 2345, 2346, 2341, - 2350, 2342, 2348, 3248, 3248, 2343, 1530, 2349, 1531, 1529, - 1530, 3248, 1531, 1529, 2363, 2261, 1530, 1225, 1531, 1529, - 1275, 4181, 807, 4036, 3891, 1461, 3781, 3780, 3660, 805, - 3659, 2651, 17, 1690, 4364, 1372, 4148, 1373, 4010, 3153, - 3064, 3061, 810, 3062, 1691, 3059, 2250, 1873, 1869, 1270, - 1271, 241, 785, 3089, 4184, 779, 2774, 808, 1684, 3077, - 1787, 780, 2575, 817, 1419, 1420, 1421, 4712, 4162, 781, - 782, 783, 1101, 1644, 1418, 4771, 1929, 4041, 2604, 4772, - 2605, 2600, 1838, 2576, 1766, 1718, 4162, 3036, 1934, 1271, - 15, 4348, 3782, 3778, 2789, 1271, 2781, 2257, 1693, 1372, - 3758, 3755, 4981, 1478, 9, 2244, 2770, 1680, 4301, 1311, - 1274, 3636, 1276, 3634, 2536, 1126, 4481, 1123, 804, 1530, - 4024, 1531, 1529, 1099, 1530, 1269, 1531, 1529, 7, 4721, - 1417, 6, 4558, 3756, 4551, 3753, 4900, 4899, 4304, 4016, - 2560, 1098, 1598, 4741, 2844, 1103, 3083, 3725, 3808, 2555, - 2593, 2954, 5009, 4734, 1114, 4978, 3764, 3762, 4566, 2988, - 1980, 4353, 4732, 4607, 4564, 4120, 3181, 2796, 4801, 1410, - 1835, 1699, 1871, 2462, 1697, 4351, 806, 1696, 4115, 3811, - 918, 2812, 1224, 920, 1127, 1124, 1223, 1234, 919, 3723, - 1089, 1740, 1088, 1090, 1091, 882, 1092, 1093, 77, 3599, - 3766, 2826, 1886, 2430, 935, 3016, 4609, 3029, 3015, 1526, - 3640, 3017, 1311, 77, 2649, 77, 3030, 2270, 3611, 3612, - 3610, 2268, 1121, 2451, 2452, 2484, 2275, 2276, 2450, 1099, - 918, 1506, 1414, 920, 1507, 1932, 2994, 2993, 919, 2104, - 2105, 1790, 3917, 1792, 1329, 1330, 1291, 1098, 1474, 1706, - 3788, 1475, 3079, 3915, 873, 1457, 1931, 2749, 1449, 1456, - 1459, 1460, 1509, 1459, 1460, 2054, 1115, 1319, 1323, 1325, - 1327, 1332, 2020, 1337, 1333, 1334, 1335, 1336, 1253, 1519, - 1314, 1315, 1316, 1317, 1289, 1290, 1320, 936, 1292, 1524, - 1293, 1295, 1296, 1297, 1298, 1294, 1299, 1300, 1301, 1302, - 1303, 1310, 1312, 1304, 1305, 1306, 1307, 1308, 1309, 1338, - 1339, 1340, 1341, 1342, 1343, 1344, 1345, 1347, 1346, 1348, - 1349, 1350, 1351, 1352, 1353, 1354, 1355, 1322, 1324, 1326, - 1328, 1331, 1415, 1222, 1416, 3757, 1127, 3754, 1124, 1198, - 1221, 4745, 918, 4744, 1977, 920, 4743, 1329, 1330, 1291, - 919, 4742, 1477, 1280, 2881, 4932, 2345, 2346, 4871, 4906, - 4907, 4335, 1254, 197, 240, 196, 229, 198, 1313, 1998, - 1319, 1323, 1325, 1327, 1332, 4724, 1337, 1333, 1334, 1335, - 1336, 4846, 1504, 1314, 1315, 1316, 1317, 1289, 1290, 1320, - 3642, 1292, 4849, 1293, 1295, 1296, 1297, 1298, 1294, 1299, - 1300, 1301, 1302, 1303, 1310, 1312, 1304, 1305, 1306, 1307, - 1308, 1309, 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1345, - 1347, 1346, 1348, 1349, 1350, 1351, 1352, 1353, 1354, 1355, - 1322, 1324, 1326, 1328, 1331, 1872, 1870, 3767, 234, 4973, - 4974, 4745, 4859, 4019, 1505, 4019, 3275, 4846, 4554, 4744, - 4858, 3112, 1247, 3647, 1239, 1243, 1245, 2347, 1125, 793, - 1122, 197, 240, 196, 229, 198, 4743, 4857, 793, 4742, - 4856, 1313, 3643, 793, 3644, 1375, 2161, 2058, 1839, 1118, - 1250, 1767, 3363, 2946, 1242, 197, 240, 196, 229, 198, - 1447, 3271, 3364, 1975, 1719, 813, 813, 1401, 1376, 793, - 793, 197, 240, 196, 229, 198, 197, 240, 196, 229, - 198, 2650, 2271, 4355, 1191, 1189, 2269, 1190, 2653, 1776, - 1996, 3273, 2482, 2483, 2039, 4423, 1508, 2043, 3370, 2468, - 1517, 1518, 1773, 4130, 1499, 1248, 234, 1501, 4334, 3529, - 1374, 4761, 3362, 1476, 4045, 1194, 4336, 809, 809, 809, - 2645, 2042, 1382, 4905, 1119, 3837, 1683, 1251, 3003, 2458, - 234, 3835, 4393, 2058, 1252, 1502, 3268, 2260, 1940, 4727, - 4728, 4729, 4730, 1379, 4137, 4749, 234, 3593, 4620, 2996, - 3685, 234, 2034, 2820, 1225, 4244, 790, 3285, 3765, 1455, - 4873, 4048, 3689, 4611, 4612, 3587, 4026, 3272, 3683, 3247, - 1521, 4352, 3145, 226, 1240, 1522, 1523, 2952, 793, 1373, - 1494, 4302, 1373, 2577, 1426, 3832, 3833, 3635, 1199, 2999, - 3000, 2998, 1435, 1373, 4617, 1725, 1573, 1567, 1249, 1445, - 1120, 3834, 4390, 2057, 2056, 1975, 2461, 1464, 1465, 4260, - 1467, 1468, 3269, 1168, 1470, 1281, 2987, 2428, 2990, 1723, - 931, 4349, 3661, 3843, 3250, 3842, 3658, 2614, 3006, 2989, - 1516, 3831, 2677, 775, 1997, 1195, 1241, 3919, 2064, 2067, - 2068, 1271, 4687, 1271, 1375, 2044, 1271, 1271, 1469, 2065, - 4132, 1271, 1271, 1390, 1393, 1495, 3900, 1373, 4437, 1459, - 1460, 1854, 3946, 1321, 3947, 3949, 3948, 1698, 2652, 2041, - 1459, 1460, 1695, 4565, 4753, 1486, 4598, 4417, 3060, 4610, - 4256, 1497, 2146, 1874, 4365, 3944, 3945, 1429, 1385, 2057, - 2056, 3943, 4154, 1225, 1500, 1503, 1395, 1197, 4545, 2657, - 2659, 2660, 1472, 1473, 1224, 807, 807, 807, 1364, 1608, - 4003, 3274, 805, 805, 805, 1103, 3856, 1496, 1246, 1411, - 1378, 1380, 1383, 1448, 1394, 810, 810, 810, 1691, 3871, - 921, 922, 923, 924, 925, 3366, 1607, 1365, 4354, 4782, - 808, 808, 808, 4774, 1117, 1933, 1691, 1368, 1443, 1444, - 2429, 1438, 1442, 1442, 1442, 785, 3270, 4651, 1399, 1400, - 3829, 4636, 1236, 3411, 780, 1705, 1321, 1724, 1686, 1688, - 4095, 1692, 781, 782, 783, 3763, 3843, 1438, 1438, 1196, - 921, 922, 923, 924, 925, 1511, 2347, 1710, 1512, 2040, - 4097, 1713, 1128, 4357, 4358, 4359, 815, 2833, 1722, 1498, - 814, 804, 804, 804, 3597, 1286, 1463, 2828, 1483, 1466, - 4249, 3772, 4872, 4784, 1479, 1480, 1514, 4766, 4201, 4790, - 1170, 1976, 1171, 1193, 793, 793, 793, 3914, 3484, 1180, - 4208, 811, 4109, 1661, 1485, 2807, 1666, 1765, 1436, 1701, - 3480, 3481, 793, 3484, 1772, 4604, 3952, 1778, 4373, 1574, - 4106, 793, 3801, 4760, 2986, 755, 755, 3843, 4265, 806, - 806, 806, 4477, 2919, 5020, 755, 755, 2964, 3406, 1815, - 1815, 1975, 793, 1255, 4998, 1272, 1273, 1235, 1237, 1703, - 1277, 4466, 3369, 793, 2963, 1238, 4320, 1397, 1392, 1391, - 3376, 4472, 921, 922, 923, 924, 925, 78, 1991, 813, - 4108, 777, 1800, 2469, 2142, 1799, 3838, 1862, 1286, 1813, - 1813, 2139, 3002, 1484, 3920, 2141, 2138, 2140, 2144, 2145, - 1817, 1434, 257, 2143, 1384, 2984, 2985, 3594, 1433, 811, - 1432, 755, 3686, 2066, 1192, 4791, 1510, 3373, 3374, 1187, - 1451, 1450, 2919, 2922, 4626, 4613, 1188, 4652, 1381, 4665, - 3923, 4637, 3372, 811, 4890, 1822, 1926, 3747, 1774, 1829, - 1831, 1619, 1620, 3530, 3243, 3531, 3532, 2955, 1745, 811, - 4165, 4394, 2912, 2645, 811, 3558, 1515, 1729, 2932, 1716, - 1717, 197, 240, 1720, 2912, 2935, 4299, 1728, 1730, 1731, - 1732, 1733, 2459, 1735, 2658, 78, 1715, 1739, 1513, 2340, - 2344, 2345, 2346, 2341, 2350, 2342, 2348, 1912, 1562, 2343, - 1670, 2349, 1917, 1809, 1810, 2035, 882, 1224, 3830, 78, - 4843, 1659, 1786, 1930, 4092, 1694, 3944, 3945, 4999, 1777, - 1755, 2918, 1233, 3620, 3621, 78, 2920, 1669, 3973, 1726, - 78, 3939, 2934, 1667, 1660, 2894, 2896, 1953, 2192, 2194, - 2193, 1564, 1563, 1133, 1956, 1665, 234, 3141, 4739, 1734, - 4171, 1491, 1978, 3021, 2992, 2950, 2779, 4575, 3951, 4576, - 2642, 2149, 2150, 2151, 2152, 2153, 2154, 2147, 2148, 2457, - 4573, 2445, 1815, 1918, 1794, 1796, 1712, 1700, 1778, 4575, - 2921, 4576, 4275, 803, 1807, 1808, 1920, 4123, 3408, 1881, - 809, 1727, 2922, 809, 809, 2923, 1169, 4569, 2933, 4664, - 2918, 2912, 2917, 2019, 2915, 2920, 1137, 1966, 3988, 3581, - 1778, 1135, 1134, 1979, 1851, 2028, 2028, 1750, 1778, 1826, - 1778, 1778, 3975, 1746, 1747, 4578, 2191, 1798, 2253, 3257, - 3688, 1815, 2832, 1815, 1375, 1910, 1830, 1832, 1845, 1738, - 1901, 1902, 1737, 2808, 240, 1736, 1825, 4578, 4473, 4474, - 1875, 778, 1823, 4889, 4468, 4577, 1490, 1200, 4467, 2921, - 1983, 1986, 1987, 1990, 785, 819, 818, 4996, 4997, 1907, - 2434, 2432, 1911, 4084, 2669, 2433, 1157, 4577, 2003, 2004, - 1702, 1704, 1849, 2023, 240, 3940, 2654, 2655, 77, 1569, - 1570, 1571, 1572, 1181, 1233, 2013, 2014, 1863, 788, 2053, - 789, 1864, 4480, 1136, 2929, 1815, 3527, 2340, 2344, 2345, - 2346, 2341, 2350, 2342, 2348, 4035, 2025, 2343, 234, 2349, - 3110, 3438, 1375, 2123, 1183, 1184, 1185, 1754, 1390, 1393, - 1715, 3265, 1567, 1232, 2491, 1889, 2895, 874, 1890, 2174, - 2164, 2165, 2166, 2155, 2156, 2107, 2800, 2160, 3559, 3561, - 3562, 3563, 3560, 2180, 2923, 2175, 2181, 1709, 234, 2802, - 2801, 1971, 2278, 1938, 1707, 1708, 1941, 1906, 2182, 1992, - 2184, 2279, 2185, 2186, 2187, 2200, 2201, 1140, 807, 4099, - 1999, 807, 807, 2252, 3852, 805, 2799, 2258, 805, 805, - 1923, 2011, 2024, 1939, 1129, 1960, 1942, 1943, 810, 1394, - 2031, 810, 810, 2030, 2976, 2232, 2277, 2018, 1130, 2050, - 2021, 2022, 1131, 808, 4172, 810, 808, 808, 4427, 1965, - 3011, 1963, 1375, 4544, 5022, 3995, 4034, 1958, 1225, 1962, - 1964, 1957, 876, 1438, 2262, 875, 3549, 3550, 2265, 2048, - 1428, 5035, 2012, 793, 793, 793, 2069, 1442, 1530, 4853, - 1531, 1529, 2280, 2282, 2017, 2283, 2523, 2285, 2286, 1442, - 2641, 2289, 2235, 2158, 777, 2293, 2032, 2033, 2641, 2297, - 2002, 1815, 2303, 2304, 804, 2306, 1778, 804, 804, 2008, - 793, 2233, 1905, 1815, 3434, 2254, 3258, 2246, 2027, 2029, - 1180, 1961, 1528, 2331, 3415, 874, 1424, 1975, 1367, 2173, - 1425, 2354, 2354, 1765, 2354, 1232, 3720, 2364, 2365, 793, - 793, 2049, 2427, 2238, 2045, 1244, 2297, 2438, 3990, 3941, - 1815, 2442, 2443, 1201, 3994, 1428, 1765, 2007, 755, 2009, - 2010, 3120, 806, 1491, 4570, 806, 806, 2713, 4737, 3291, - 2712, 3853, 755, 2016, 1815, 1232, 2928, 2090, 3890, 1959, - 2926, 2347, 1884, 2322, 1887, 1888, 4570, 2641, 2766, 1133, - 4571, 2887, 5041, 1392, 1391, 2036, 2525, 1891, 1892, 1893, - 1894, 1895, 1896, 874, 3292, 3293, 3548, 2501, 2503, 2919, - 2922, 793, 2297, 1815, 1528, 2510, 2686, 793, 880, 880, - 876, 2293, 1138, 875, 2517, 2887, 2519, 2520, 2521, 2382, - 2824, 1530, 2527, 1531, 1529, 1225, 1219, 2241, 5037, 257, - 197, 240, 257, 257, 5030, 257, 1530, 2305, 1531, 1529, - 2949, 1491, 1132, 2485, 2356, 1528, 2362, 1135, 1134, 3140, - 2486, 4157, 2492, 2447, 1428, 2436, 3435, 2188, 2189, 1530, - 3435, 1531, 1529, 1532, 4080, 2287, 2288, 1528, 5013, 197, - 240, 1566, 2294, 2295, 1489, 5011, 2477, 2478, 2307, 3719, - 1576, 4030, 1528, 2308, 2309, 2310, 4047, 3140, 876, 1224, - 3956, 875, 2623, 2234, 2685, 5005, 2583, 2236, 3954, 2302, - 3995, 2454, 2506, 2456, 2463, 234, 1586, 2291, 2512, 4992, - 2242, 2316, 4948, 1528, 2475, 2476, 2251, 3224, 2255, 3418, - 1233, 2037, 4918, 2259, 3383, 3389, 3390, 3391, 3384, 3388, - 3385, 3387, 3386, 793, 2331, 3908, 793, 3905, 2640, 2446, - 2470, 2559, 2523, 2597, 234, 2599, 3817, 2509, 2441, 2603, - 3814, 2823, 3770, 5014, 2570, 4915, 4914, 2608, 2609, 2610, - 2647, 2613, 2923, 1778, 1367, 1950, 3420, 2918, 2912, 2917, - 2887, 2915, 2920, 809, 2326, 2327, 2328, 2448, 1488, 2332, - 5006, 1947, 1948, 2907, 1530, 2435, 1531, 1529, 4908, 2347, - 1662, 1778, 1993, 1994, 4949, 3244, 3084, 4949, 1815, 2638, - 2578, 2640, 2535, 4886, 2440, 2538, 2539, 4919, 2541, 3995, - 2449, 2048, 2453, 2905, 2455, 2464, 4489, 4490, 4491, 4495, - 4493, 4494, 4496, 4497, 4498, 4492, 2921, 2074, 2075, 2076, - 2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 4836, - 4916, 2647, 2622, 197, 240, 2099, 2100, 2495, 4835, 2500, - 4811, 2507, 2545, 4785, 2508, 2513, 2514, 2591, 2662, 2515, - 2552, 197, 240, 2571, 2773, 2607, 3405, 1489, 2763, 2762, - 2124, 2532, 2489, 2687, 2496, 2159, 2722, 1491, 2488, 4679, - 2494, 2624, 4678, 2721, 2720, 3401, 1952, 1530, 4887, 1531, - 1529, 2627, 2572, 2589, 2567, 1951, 2480, 2629, 2183, 2631, - 964, 974, 2444, 2550, 1749, 77, 2110, 1972, 77, 77, - 965, 77, 966, 970, 973, 969, 967, 968, 2569, 1801, - 4677, 4676, 2086, 2087, 1528, 4655, 2097, 2098, 1662, 4654, - 2101, 1232, 4389, 1528, 4623, 2687, 3399, 4136, 2647, 1107, - 1108, 1109, 1110, 4592, 2585, 2761, 5007, 4707, 2587, 2598, - 3010, 4538, 2601, 2602, 1225, 3750, 2606, 1107, 1108, 1109, - 1110, 807, 2723, 2724, 2687, 2726, 2544, 2687, 805, 2617, - 2618, 4181, 2733, 2632, 2590, 3626, 2586, 971, 2331, 2620, - 2621, 810, 1530, 4589, 1531, 1529, 3402, 1861, 2340, 2344, - 2345, 2346, 2341, 2350, 2342, 2348, 808, 3437, 2343, 3253, - 2349, 1530, 4443, 1531, 1529, 2687, 2687, 4442, 972, 2616, - 2647, 3143, 3142, 2780, 2647, 2782, 3111, 2784, 2785, 2687, - 2816, 2788, 4375, 1828, 2708, 2691, 4374, 4536, 1528, 793, - 1778, 793, 1778, 2675, 3748, 4270, 2633, 4210, 2630, 2628, - 3751, 2588, 2803, 2689, 1442, 2757, 4195, 4192, 2809, 2810, - 4073, 929, 2644, 4069, 2764, 793, 793, 804, 3964, 2683, - 2821, 1530, 3708, 1531, 1529, 3656, 3653, 3424, 2858, 793, - 793, 793, 1530, 2670, 1531, 1529, 2765, 2748, 2750, 2751, - 2752, 2661, 2754, 2755, 2174, 2174, 2853, 4444, 2530, 3422, - 3417, 2663, 2858, 2862, 1765, 1765, 1765, 1765, 1765, 1765, - 1765, 2745, 2256, 2090, 1935, 2572, 1112, 3908, 1582, 2679, - 1530, 3905, 1531, 1529, 2354, 806, 1396, 1361, 2619, 3749, - 1975, 1562, 4211, 2625, 1112, 3222, 2626, 1356, 3210, 4324, - 2758, 3908, 3905, 2674, 2673, 4074, 3202, 3155, 4070, 2897, - 2941, 1564, 1563, 3965, 3115, 3074, 3072, 1528, 3070, 4268, - 3423, 3418, 3425, 3068, 2888, 2322, 2322, 2857, 2767, 1995, - 3895, 2729, 2728, 2479, 1564, 1563, 2711, 1544, 2756, 197, - 240, 196, 229, 198, 3423, 3418, 3680, 2768, 4638, 2702, - 4292, 2195, 2196, 2197, 2198, 2015, 1225, 2202, 2203, 2204, - 2205, 2207, 2208, 2209, 2210, 2211, 2212, 2213, 2214, 2215, - 2216, 2217, 2218, 2219, 5023, 793, 2028, 2948, 2701, 5015, - 2858, 3404, 2793, 1528, 2795, 2700, 4977, 1266, 1267, 1268, - 3872, 1528, 1528, 1879, 1878, 4775, 755, 755, 2688, 2858, - 3075, 3073, 2646, 3069, 1375, 1944, 2947, 4428, 3069, 2887, - 1815, 793, 2858, 2766, 234, 2850, 1528, 1528, 1045, 2783, - 1955, 1528, 1265, 2787, 1439, 1262, 4323, 2163, 2162, 2060, - 2061, 2062, 2063, 1978, 1528, 3037, 3038, 1803, 4168, 4639, - 4751, 4776, 793, 2163, 2162, 1471, 4166, 777, 1988, 4702, - 3027, 2911, 2910, 4429, 1862, 2813, 1765, 2863, 1424, 3049, - 3671, 3051, 1425, 1528, 257, 2533, 4622, 4562, 4507, 1608, - 1528, 2886, 3873, 4470, 2111, 2991, 4469, 3045, 2115, 2116, - 2117, 2118, 4453, 2687, 4169, 4640, 1566, 2647, 4397, 2157, - 1945, 3018, 4167, 3019, 2854, 2855, 1607, 3059, 2168, 1984, - 2174, 3091, 2174, 793, 3096, 1424, 1805, 2885, 4175, 1425, - 2096, 3107, 3024, 3025, 2882, 4147, 3996, 1806, 3874, 3113, - 3986, 3978, 3966, 2904, 3910, 3012, 2093, 2095, 2092, 3907, - 2094, 3118, 3847, 3590, 3589, 3121, 3124, 2924, 2925, 4884, - 2930, 3126, 1802, 3040, 2206, 1815, 1440, 3426, 3421, 1424, - 2666, 2667, 2224, 1425, 2226, 2227, 2228, 2229, 2230, 3381, - 2199, 3137, 3249, 3048, 2638, 2239, 3152, 3023, 1424, 2786, - 1989, 1815, 1425, 1815, 2581, 2580, 2579, 1742, 1375, 2864, - 2865, 2866, 2867, 2868, 2869, 3128, 3154, 1741, 1377, 4008, - 3031, 195, 227, 238, 228, 3162, 3053, 3090, 3001, 2114, - 3790, 2680, 1225, 3007, 3793, 4779, 3013, 1867, 3283, 2533, - 2114, 1259, 1260, 1261, 1264, 1967, 1263, 1815, 1375, 1424, - 2347, 1985, 3184, 1425, 3627, 1794, 1796, 1531, 1529, 3185, - 2284, 2664, 2665, 1530, 3117, 1531, 1529, 3054, 3119, 3193, - 4855, 3028, 4009, 3149, 1815, 1530, 3033, 1531, 1529, 3793, - 4646, 3129, 1530, 4591, 1531, 1529, 4011, 1813, 3046, 4590, - 3134, 3135, 2850, 3063, 3080, 5019, 1530, 3179, 1531, 1529, - 3042, 1530, 3138, 1531, 1529, 3164, 3047, 1547, 1548, 1549, - 1550, 1551, 1544, 3790, 1813, 1529, 3127, 4484, 4483, 4951, - 3875, 3519, 3517, 3082, 3194, 1545, 1546, 1547, 1548, 1549, - 1550, 1551, 1544, 4923, 1530, 2239, 1531, 1529, 3496, 3494, - 2239, 2239, 3251, 3055, 4459, 4885, 1530, 3255, 1531, 1529, - 3259, 3710, 3093, 4880, 3105, 3095, 3792, 793, 793, 793, - 1530, 5018, 1531, 1529, 4830, 3199, 3200, 4829, 3114, 1868, - 77, 4827, 4828, 3205, 3206, 3695, 1375, 3358, 3165, 3211, - 3167, 3192, 3146, 3151, 1815, 3130, 3239, 1778, 3182, 1530, - 3133, 1531, 1529, 1778, 2438, 4681, 4682, 4184, 2534, 4398, - 4399, 2537, 4826, 3160, 2540, 4825, 3234, 2542, 3235, 2048, - 3226, 4824, 3227, 3139, 3229, 3709, 3231, 3232, 3144, 3430, - 3433, 1535, 1536, 1537, 1538, 1539, 1540, 1541, 1533, 4823, - 1927, 3177, 3178, 4822, 2178, 3240, 3413, 4820, 3171, 3156, - 3157, 3299, 1530, 2695, 1531, 1529, 3086, 3087, 1765, 2179, - 1765, 3427, 3428, 4819, 1530, 2565, 1531, 1529, 1584, 3465, - 3471, 3180, 3169, 3400, 4818, 1337, 1333, 1334, 1335, 1336, - 1375, 1583, 3176, 4817, 3175, 3174, 3172, 4391, 3493, 1530, - 3395, 1531, 1529, 5, 4816, 1375, 1375, 1375, 2028, 4815, - 4895, 1375, 3170, 3503, 3504, 3505, 3506, 1375, 3513, 3238, - 3514, 3515, 4798, 3516, 2684, 3518, 1530, 4134, 1531, 1529, - 4583, 3099, 3398, 3570, 1867, 4813, 3513, 1530, 4339, 1531, - 1529, 3098, 4812, 1530, 4777, 1531, 1529, 3277, 2354, 1530, - 3241, 1531, 1529, 78, 3136, 3488, 2682, 1530, 4392, 1531, - 1529, 1530, 3571, 1531, 1529, 1530, 3568, 1531, 1529, 4338, - 3488, 3499, 3500, 4668, 2382, 4658, 3502, 4641, 4616, 755, - 3173, 3566, 3509, 3555, 1968, 237, 1969, 2438, 4135, 3377, - 4588, 1375, 1375, 2704, 3569, 3396, 1530, 4552, 1531, 1529, - 4479, 4431, 1815, 1530, 4430, 1531, 1529, 3298, 3608, 4202, - 4170, 4133, 2302, 4116, 3836, 3664, 3375, 3403, 3639, 3638, - 793, 3579, 1375, 3359, 3360, 3623, 3576, 3567, 3553, 3552, - 3412, 3551, 3543, 3537, 3487, 1530, 257, 1531, 1529, 3432, - 3429, 257, 3565, 7, 3554, 3445, 6, 3536, 3535, 3498, - 3443, 3444, 3534, 3245, 3116, 3076, 3607, 3607, 3020, 3452, - 2772, 2676, 2594, 3453, 2703, 2681, 2554, 3454, 2553, 3476, - 1862, 2551, 3473, 2690, 2547, 3457, 3458, 3459, 4327, 3489, - 2546, 2498, 166, 3492, 2267, 3501, 3582, 3299, 3495, 2264, - 1936, 1530, 3663, 1531, 1529, 3474, 3491, 1682, 4326, 3609, - 3491, 3124, 3380, 4325, 4140, 1530, 3102, 1531, 1529, 3104, - 4146, 3822, 2699, 77, 3103, 5010, 3533, 4614, 4615, 5008, - 2706, 1815, 1359, 3595, 3682, 1530, 4309, 1531, 1529, 4975, - 1530, 3652, 1531, 1529, 4938, 3545, 4868, 2340, 2344, 2345, - 2346, 2341, 2350, 2342, 2348, 4051, 4052, 2343, 2725, 2349, - 4866, 4596, 2333, 2730, 2731, 2732, 3655, 4841, 2735, 2736, - 2737, 2738, 2739, 2740, 2741, 2742, 2743, 2744, 3585, 2746, - 2747, 2953, 3591, 3616, 2956, 2957, 2958, 2959, 2960, 2961, - 2962, 3613, 1358, 2965, 2966, 2967, 2968, 2969, 2970, 2971, - 2972, 2973, 2974, 2975, 4763, 2977, 2978, 2979, 2980, 2981, - 3622, 2982, 3188, 3666, 3630, 3629, 3669, 3670, 3588, 3628, - 4402, 4757, 4748, 3617, 3632, 4746, 4253, 4731, 4722, 4696, - 3195, 3676, 4695, 3673, 4686, 4685, 4667, 4666, 4619, 3649, - 4603, 3776, 4601, 3298, 3779, 4587, 4553, 4463, 4462, 3783, - 4461, 793, 1778, 1530, 4406, 1531, 1529, 4395, 4380, 4379, - 3795, 3797, 3798, 3800, 3713, 3802, 3803, 3159, 4377, 3684, - 4370, 4368, 3645, 4061, 3668, 3679, 3726, 3727, 2889, 4054, - 4347, 1375, 3728, 3729, 3730, 3731, 4346, 3732, 3733, 3734, - 3735, 3736, 3737, 3738, 3739, 3740, 3741, 3742, 3743, 4345, - 1530, 4341, 1531, 1529, 4340, 4314, 1530, 3678, 1531, 1529, - 3752, 4311, 3707, 3665, 3691, 3667, 4307, 4305, 3721, 4281, - 3692, 4278, 77, 1797, 3715, 3672, 3700, 77, 3702, 5039, - 3674, 3675, 3714, 4272, 3575, 3703, 3704, 1530, 4128, 1531, - 1529, 4118, 3711, 4101, 4085, 1530, 3815, 1531, 1529, 4060, - 3701, 1530, 4058, 1531, 1529, 4025, 3825, 3827, 3984, 1530, - 3983, 1531, 1529, 3981, 3980, 3967, 3962, 3840, 3769, 1530, - 3961, 1531, 1529, 793, 1543, 1542, 1552, 1553, 1554, 1555, - 1545, 1546, 1547, 1548, 1549, 1550, 1551, 1544, 3855, 3848, - 1375, 3223, 3812, 793, 3773, 793, 2438, 1375, 3804, 3794, - 1375, 3774, 3221, 3784, 3760, 2715, 3777, 4810, 3775, 2174, - 2777, 2174, 3690, 3687, 3885, 2239, 3886, 2239, 1530, 3662, - 1531, 1529, 3637, 3615, 3583, 1765, 2862, 3220, 3894, 1530, - 3759, 1531, 1529, 3580, 3786, 3577, 2239, 2239, 1552, 1553, - 1554, 1555, 1545, 1546, 1547, 1548, 1549, 1550, 1551, 1544, - 3851, 3564, 3556, 3465, 1530, 3488, 1531, 1529, 3844, 3471, - 3863, 1763, 3488, 3546, 3219, 3488, 3544, 3540, 3539, 2941, - 3538, 3448, 3395, 3284, 3266, 3254, 3880, 3246, 1045, 1044, - 5021, 3929, 3097, 3932, 1861, 3932, 3932, 1910, 3081, 3043, - 1375, 1530, 1225, 1531, 1529, 2804, 3854, 3828, 1901, 1902, - 3218, 3818, 2791, 976, 169, 4808, 3217, 2790, 3957, 3882, - 2656, 4963, 2558, 169, 3398, 2238, 1815, 1815, 3953, 2557, - 3884, 1907, 3216, 2549, 1911, 2299, 2266, 1530, 2263, 1531, - 1529, 2249, 3892, 1530, 3912, 1531, 1529, 2248, 2026, 1937, - 3106, 1615, 3109, 3850, 1611, 1610, 791, 3916, 3918, 1530, - 1363, 1531, 1529, 1116, 4991, 3488, 1813, 1813, 4793, 3878, - 4593, 3876, 4582, 793, 197, 240, 3958, 3959, 3883, 3881, - 4581, 2850, 3867, 3889, 3215, 3869, 3893, 3825, 4990, 4955, - 4567, 786, 4563, 3131, 3132, 4378, 4344, 3909, 4321, 169, - 1778, 3214, 4273, 2438, 2438, 3906, 4173, 3928, 4163, 3213, - 4161, 1530, 4156, 1531, 1529, 4081, 3212, 4079, 3937, 2347, - 4076, 4075, 4072, 3911, 3365, 2911, 2910, 3184, 1530, 3209, - 1531, 1529, 4071, 3208, 4059, 4012, 1530, 4057, 1531, 1529, - 3933, 3934, 3207, 1530, 3938, 1531, 1529, 4659, 3955, 234, - 5017, 3163, 4014, 4013, 3166, 3201, 1530, 4002, 1531, 1529, - 1530, 3998, 1531, 1529, 3899, 3888, 3186, 3187, 1176, 1530, - 3816, 1531, 1529, 3813, 3963, 3190, 3191, 3768, 3717, 3705, - 1100, 3971, 1530, 3935, 1531, 1529, 3697, 3696, 3694, 793, - 3189, 3196, 3197, 3198, 3698, 3699, 3460, 3071, 3067, 3066, - 3927, 1543, 1542, 1552, 1553, 1554, 1555, 1545, 1546, 1547, - 1548, 1549, 1550, 1551, 1544, 3183, 4033, 1530, 3968, 1531, - 1529, 3065, 4031, 3976, 3991, 3992, 3034, 3228, 3977, 3230, - 3161, 3982, 3233, 3985, 2060, 3979, 2760, 2239, 3032, 2734, - 791, 2727, 1530, 2719, 1531, 1529, 2718, 2759, 1226, 4022, - 2717, 2753, 3525, 3526, 4032, 169, 2716, 1530, 2714, 1531, - 1529, 2710, 2109, 1530, 2709, 1531, 1529, 3541, 3542, 2707, - 169, 2698, 169, 4038, 1530, 2694, 1531, 1529, 1530, 4087, - 1531, 1529, 3989, 4088, 2693, 2527, 1100, 2556, 4029, 1530, - 2225, 1531, 1529, 2223, 2222, 197, 240, 4102, 2221, 4104, - 3586, 2220, 2177, 2176, 4110, 2167, 3465, 3471, 1827, 197, - 240, 3993, 4044, 240, 3997, 2324, 3486, 4962, 1375, 4942, - 4098, 4043, 4922, 4834, 4797, 1888, 1605, 4062, 4792, 4716, - 4111, 4049, 4713, 4694, 4675, 4669, 1891, 1892, 1893, 1894, - 1895, 1896, 881, 793, 2438, 4064, 2321, 4066, 4547, 4068, - 4546, 240, 196, 229, 198, 4502, 4482, 4155, 4476, 4105, - 3879, 4107, 4452, 4077, 4251, 4250, 4247, 4160, 4246, 4209, - 2323, 4206, 4037, 4204, 4149, 4039, 4040, 240, 196, 229, - 198, 4100, 4042, 3299, 234, 4096, 3901, 234, 4126, 4127, - 2354, 1765, 4178, 3898, 199, 1906, 4093, 1909, 1883, 2331, - 1900, 1897, 4187, 1885, 4082, 4086, 3465, 1880, 3124, 3471, - 3809, 4197, 3971, 3806, 4144, 4090, 3805, 3706, 3578, 3572, - 3497, 3416, 3409, 1752, 4212, 234, 3242, 1375, 3225, 3041, - 3022, 2983, 2874, 2856, 2815, 4113, 3929, 2814, 4153, 4117, - 1375, 4121, 2771, 2091, 4141, 4124, 234, 2573, 4112, 2325, - 2245, 234, 1973, 1898, 1681, 1375, 1664, 4267, 1658, 1657, - 1656, 1815, 4078, 1655, 1654, 4143, 1653, 4262, 4263, 4264, - 1652, 1651, 1650, 1649, 4180, 1648, 4276, 1647, 1646, 4174, - 1645, 1644, 1643, 2239, 1642, 1641, 1640, 1639, 1638, 793, - 1637, 2438, 4213, 1636, 1635, 1765, 1375, 1634, 4177, 1633, - 1632, 1813, 1631, 1630, 4293, 4255, 1629, 1628, 4245, 1627, - 257, 4269, 4176, 4295, 1626, 1625, 4238, 1624, 1623, 1622, - 3509, 1621, 4185, 1618, 1617, 1616, 4194, 4122, 4193, 3298, - 3631, 4191, 3633, 4312, 4289, 1614, 4199, 1613, 1612, 1609, - 1602, 1601, 4316, 1599, 1598, 1597, 1596, 1595, 1594, 1593, - 1592, 1591, 4252, 1590, 1589, 2565, 4257, 4282, 4254, 1588, - 1861, 3607, 1587, 1581, 1580, 1579, 1578, 1577, 1493, 4266, - 4337, 1360, 3441, 3442, 4806, 4285, 4804, 4248, 4271, 2830, - 2612, 1402, 4953, 4904, 3974, 3584, 4279, 4274, 3382, 4277, - 4283, 3039, 2840, 1492, 1422, 2872, 4280, 2331, 4179, 2884, - 4360, 4287, 4286, 2879, 4366, 4288, 2871, 4362, 2880, 4284, - 4372, 4319, 2877, 2875, 3903, 81, 3451, 2878, 2876, 3450, - 3449, 3446, 4310, 2883, 3904, 2870, 4854, 4733, 4711, 4290, - 4313, 4542, 5036, 4457, 3654, 3693, 3419, 1743, 2318, 2319, - 2899, 3846, 4328, 4159, 4329, 3651, 4330, 2313, 2314, 2315, - 1375, 1815, 1815, 4027, 4028, 4407, 2951, 3925, 1375, 3926, - 4000, 2901, 2900, 3716, 4258, 4125, 4369, 2419, 4371, 4412, - 1876, 3414, 1375, 1922, 3150, 3902, 4412, 4350, 2798, 4001, - 4291, 2797, 4342, 4300, 1919, 4441, 1186, 2805, 4432, 2516, - 3521, 1813, 2107, 784, 2431, 3744, 1487, 4403, 3522, 3523, - 3524, 4404, 4672, 4363, 4376, 1375, 4966, 1375, 3086, 3087, - 4446, 3820, 3868, 3819, 3475, 3100, 4436, 2903, 4383, 2838, - 4356, 2329, 4449, 3488, 4451, 4409, 1815, 4405, 4386, 2290, - 4943, 4396, 2163, 2162, 1677, 1678, 4671, 3607, 1675, 1676, - 1673, 1674, 4415, 3960, 4315, 793, 4425, 1375, 1375, 3004, - 1362, 1375, 1375, 2997, 4424, 4414, 4419, 4422, 4180, 1176, - 2898, 4438, 1671, 1672, 1370, 2439, 2107, 1482, 1481, 4416, - 2053, 1520, 2053, 3657, 2582, 4504, 4537, 4445, 3456, 4506, - 2806, 2584, 1982, 1431, 4499, 1430, 77, 1413, 2578, 4458, - 1405, 1407, 1454, 2596, 4486, 4487, 4245, 4388, 4500, 4501, - 3148, 4464, 4548, 4549, 4238, 4455, 4387, 4929, 4927, 3147, - 4874, 4851, 169, 169, 169, 1226, 4850, 4848, 4767, 4717, - 1815, 4447, 4306, 4190, 786, 4189, 4063, 4021, 4020, 4006, - 2562, 2936, 2906, 1924, 4005, 4382, 3625, 1428, 4957, 4956, - 4882, 4883, 1367, 4367, 4103, 4089, 4540, 3677, 3261, 4584, - 4585, 3260, 793, 4539, 3252, 3044, 2696, 2059, 1398, 1369, - 1813, 4956, 4957, 4478, 4574, 4944, 4597, 4933, 4599, 4879, - 4561, 3281, 3279, 4385, 4555, 1107, 1108, 1109, 1110, 3648, - 1367, 1914, 4434, 4435, 787, 1446, 89, 2239, 1107, 1108, - 1109, 1110, 2239, 2, 4600, 4560, 4602, 3887, 4979, 1527, - 4980, 1, 3761, 2769, 4568, 4572, 2243, 1679, 1111, 3896, - 1106, 1791, 3897, 3014, 2481, 1819, 2247, 1113, 4631, 2890, - 2891, 4556, 3455, 2893, 4629, 2499, 4513, 1408, 3267, 2643, - 3596, 2995, 2819, 4605, 3839, 4579, 4580, 1753, 1862, 1182, - 4203, 4441, 4205, 4606, 2169, 4648, 1949, 1389, 1946, 1388, - 1386, 2112, 2190, 1375, 1565, 978, 2843, 3573, 3547, 4624, - 4186, 4965, 5002, 4921, 4968, 1970, 4618, 962, 3936, 4663, - 4657, 4842, 4643, 4023, 3646, 4723, 4925, 4725, 4630, 4627, - 4559, 2648, 1525, 3877, 4634, 4632, 845, 4319, 1022, 990, - 1600, 2566, 4541, 3724, 3722, 989, 4647, 4138, 3371, 4644, - 4543, 3619, 4635, 1375, 846, 4653, 2543, 4720, 4557, 1877, - 1882, 4512, 2902, 4650, 4788, 4456, 4642, 3921, 2053, 3483, - 1913, 4783, 4207, 4333, 4331, 4332, 888, 2460, 753, 1210, - 4503, 4670, 2839, 2611, 4508, 4674, 1815, 1155, 1557, 4708, - 1561, 4119, 2829, 1156, 1148, 3393, 3392, 2070, 1534, 3970, - 4680, 2089, 3745, 4714, 4715, 3746, 1558, 1560, 1556, 1575, - 1559, 1543, 1542, 1552, 1553, 1554, 1555, 1545, 1546, 1547, - 1548, 1549, 1550, 1551, 1544, 933, 1813, 4709, 4710, 2678, - 3368, 4232, 3614, 88, 87, 86, 4705, 85, 265, 981, - 264, 4747, 2692, 4594, 4400, 4837, 4970, 959, 958, 4752, - 4740, 957, 956, 955, 954, 2338, 2339, 2337, 4759, 2335, - 4718, 2334, 1760, 1100, 1759, 3624, 4004, 2522, 2524, 1668, - 3512, 4259, 3507, 2371, 2369, 1782, 2931, 4754, 2938, 4755, - 2368, 4901, 4050, 4322, 4799, 4800, 4475, 3557, 4318, 2312, - 2927, 4768, 2388, 4764, 3528, 1176, 1176, 1748, 2385, 2384, - 3520, 4471, 4465, 2416, 5027, 4150, 4151, 4152, 4986, 4984, - 4878, 4421, 4196, 1770, 4756, 3470, 4420, 4411, 4214, 4215, - 4221, 1318, 1784, 2837, 4787, 4762, 1375, 1287, 1282, 1284, - 1285, 4053, 1283, 4770, 4773, 3168, 3987, 2908, 4814, 3858, - 4055, 4056, 3290, 1821, 4781, 1375, 4803, 4805, 4807, 4809, - 1764, 4509, 3289, 4778, 1833, 3287, 3286, 1815, 4832, 1721, - 4786, 4758, 4821, 4870, 4381, 3297, 4795, 4838, 3295, 1412, - 4065, 1974, 4067, 4802, 1403, 1357, 2852, 3787, 1687, 1685, - 3447, 2873, 2563, 3650, 4454, 1761, 4839, 1758, 1824, 1757, - 1257, 1256, 2493, 786, 4460, 1228, 235, 1813, 2487, 1227, - 236, 1097, 4094, 4158, 4867, 54, 3598, 4831, 3005, 3101, - 2053, 4840, 4719, 3464, 4608, 2317, 1149, 4847, 4845, 2827, - 1815, 124, 48, 142, 123, 4864, 4860, 4862, 215, 69, - 3970, 4505, 214, 4869, 68, 169, 20, 140, 213, 67, - 4865, 4888, 4861, 4863, 53, 52, 211, 4896, 119, 118, - 117, 116, 4514, 4515, 139, 210, 4875, 66, 249, 4877, - 1813, 248, 251, 250, 247, 4881, 3056, 3057, 4510, 4511, - 4876, 4518, 4517, 4516, 4530, 4531, 4532, 4519, 4520, 4523, - 4525, 4524, 4521, 4522, 4526, 4528, 4529, 4527, 246, 4909, - 1865, 4910, 4533, 4911, 245, 4912, 4852, 4913, 4418, 4833, - 4917, 51, 50, 4534, 1542, 1552, 1553, 1554, 1555, 1545, - 1546, 1547, 1548, 1549, 1550, 1551, 1544, 4928, 216, 4930, - 4931, 49, 125, 4920, 70, 1375, 47, 4926, 4924, 46, - 2595, 3807, 1981, 4114, 3282, 4934, 4740, 2811, 45, 40, - 4935, 14, 4936, 12, 4937, 169, 43, 41, 169, 169, - 25, 4663, 4941, 24, 1954, 23, 31, 30, 29, 13, - 39, 37, 169, 4946, 4947, 4945, 36, 4950, 161, 160, - 4964, 35, 159, 4972, 4954, 4952, 158, 157, 4971, 4958, - 4959, 4960, 4961, 156, 155, 154, 153, 34, 22, 61, - 4939, 1375, 60, 4987, 1375, 59, 58, 57, 4976, 56, - 2239, 8, 4182, 146, 145, 4983, 147, 143, 4982, 4787, - 4989, 138, 136, 4994, 4993, 33, 5000, 2239, 137, 5004, - 134, 135, 128, 127, 126, 120, 4294, 100, 5001, 99, - 98, 113, 112, 111, 110, 109, 108, 106, 107, 844, - 97, 2174, 5016, 5012, 96, 95, 94, 93, 4450, 133, - 132, 129, 115, 122, 121, 104, 2053, 4972, 5025, 3607, - 114, 105, 4971, 103, 5024, 4987, 5028, 102, 101, 5026, - 92, 91, 90, 131, 5031, 5004, 5032, 130, 144, 217, - 71, 194, 1768, 1769, 193, 1771, 5038, 192, 1775, 191, - 1779, 1780, 1781, 190, 188, 189, 5040, 187, 186, 185, - 184, 4343, 1543, 1542, 1552, 1553, 1554, 1555, 1545, 1546, - 1547, 1548, 1549, 1550, 1551, 1544, 183, 182, 62, 63, - 64, 65, 4535, 206, 205, 207, 3158, 219, 1836, 1837, - 218, 209, 1840, 1841, 1842, 1843, 1844, 212, 1846, 1847, - 1848, 208, 1850, 203, 1853, 201, 1855, 1856, 1857, 1565, - 1543, 1542, 1552, 1553, 1554, 1555, 1545, 1546, 1547, 1548, - 1549, 1550, 1551, 1544, 900, 899, 906, 896, 204, 202, - 200, 82, 11, 4448, 141, 21, 18, 903, 904, 3, - 905, 909, 0, 1557, 890, 1561, 0, 0, 0, 0, - 0, 0, 0, 0, 914, 0, 4433, 0, 0, 0, - 0, 1558, 1560, 1556, 0, 1559, 1543, 1542, 1552, 1553, - 1554, 1555, 1545, 1546, 1547, 1548, 1549, 1550, 1551, 1544, - 0, 0, 0, 0, 2272, 2273, 2274, 1543, 1542, 1552, - 1553, 1554, 1555, 1545, 1546, 1547, 1548, 1549, 1550, 1551, - 1544, 918, 0, 0, 920, 0, 0, 0, 0, 919, - 0, 0, 0, 0, 0, 3718, 0, 0, 0, 0, - 0, 2311, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 3712, 0, 0, 0, 4633, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2697, - 2366, 2367, 1543, 1542, 1552, 1553, 1554, 1555, 1545, 1546, - 1547, 1548, 1549, 1550, 1551, 1544, 0, 0, 169, 1543, - 1542, 1552, 1553, 1554, 1555, 1545, 1546, 1547, 1548, 1549, - 1550, 1551, 1544, 0, 0, 0, 2357, 1543, 1542, 1552, - 1553, 1554, 1555, 1545, 1546, 1547, 1548, 1549, 1550, 1551, - 1544, 0, 0, 0, 0, 0, 0, 0, 0, 1764, - 0, 0, 0, 0, 0, 1311, 0, 0, 0, 0, - 0, 0, 1407, 4683, 4684, 0, 0, 0, 1407, 0, - 4688, 4689, 4690, 4691, 4692, 4693, 2671, 0, 0, 4697, - 4698, 4699, 4700, 4701, 0, 0, 0, 4703, 4704, 0, - 4706, 0, 0, 0, 2490, 0, 2497, 0, 0, 0, - 1543, 1542, 1552, 1553, 1554, 1555, 1545, 1546, 1547, 1548, - 1549, 1550, 1551, 1544, 0, 891, 893, 892, 0, 0, - 197, 240, 196, 229, 198, 0, 0, 898, 0, 0, - 0, 0, 169, 0, 0, 169, 169, 0, 169, 902, - 230, 0, 0, 0, 0, 0, 917, 222, 0, 0, - 0, 231, 0, 895, 0, 0, 0, 885, 1861, 0, - 0, 0, 0, 0, 199, 0, 0, 0, 0, 0, - 0, 167, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4769, 0, 152, 0, 0, 169, - 1329, 1330, 1291, 0, 1405, 234, 0, 2592, 0, 0, - 0, 0, 0, 4673, 0, 0, 0, 0, 169, 0, - 0, 0, 0, 1319, 1323, 1325, 1327, 1332, 0, 1337, - 1333, 1334, 1335, 1336, 0, 4796, 1314, 1315, 1316, 1317, - 1289, 1290, 1320, 0, 1292, 0, 1293, 1295, 1296, 1297, - 1298, 1294, 1299, 1300, 1301, 1302, 1303, 1310, 1312, 1304, - 1305, 1306, 1307, 1308, 1309, 1338, 1339, 1340, 1341, 1342, - 1343, 1344, 1345, 1347, 1346, 1348, 1349, 1350, 1351, 1352, - 1353, 1354, 1355, 1322, 1324, 1326, 1328, 1331, 0, 0, - 0, 0, 0, 0, 176, 177, 0, 178, 179, 0, - 0, 0, 180, 0, 0, 181, 0, 0, 0, 1586, - 0, 0, 0, 897, 901, 907, 0, 908, 910, 0, - 0, 911, 912, 913, 1313, 0, 0, 915, 916, 1543, - 1542, 1552, 1553, 1554, 1555, 1545, 1546, 1547, 1548, 1549, - 1550, 1551, 1544, 4240, 0, 0, 0, 0, 0, 4219, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1565, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4780, 0, 0, - 0, 0, 195, 227, 238, 228, 83, 148, 0, 0, - 0, 4233, 0, 0, 0, 0, 833, 0, 0, 0, - 4794, 0, 0, 0, 4222, 0, 150, 221, 220, 0, - 0, 0, 0, 84, 0, 4217, 0, 0, 0, 0, - 4242, 4243, 0, 0, 0, 0, 4218, 0, 0, 0, - 0, 175, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2504, 2505, 0, 0, 0, 0, 0, 2511, 0, - 0, 0, 0, 0, 0, 0, 0, 2518, 149, 0, - 0, 0, 0, 0, 0, 0, 4225, 0, 2146, 829, - 830, 0, 0, 0, 223, 224, 225, 0, 0, 0, - 874, 0, 0, 894, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2792, 0, 2794, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 4893, 0, 0, 2817, 2818, 4897, 0, - 2574, 921, 922, 923, 924, 925, 0, 0, 0, 0, - 2834, 2835, 2836, 0, 0, 232, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 162, 0, 0, 0, - 226, 0, 163, 0, 0, 876, 0, 0, 875, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 4241, 0, - 2917, 0, 0, 0, 0, 0, 0, 1226, 0, 0, - 169, 169, 0, 0, 0, 0, 0, 1764, 1764, 1764, - 1764, 1764, 1764, 1764, 0, 4229, 0, 0, 0, 0, - 0, 4893, 0, 0, 0, 0, 0, 0, 164, 0, - 0, 0, 861, 0, 0, 0, 0, 4226, 4230, 4228, - 4227, 75, 834, 0, 0, 0, 0, 0, 0, 1321, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 836, - 0, 0, 0, 0, 0, 0, 1784, 0, 0, 0, - 4893, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2142, 0, 0, 0, 78, 4236, 4237, 2139, 0, 0, - 833, 2141, 2138, 2140, 2144, 2145, 0, 0, 0, 2143, - 0, 0, 1821, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 173, 237, 174, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 72, 0, 0, 1407, 0, 0, 857, 0, 859, 856, - 0, 0, 0, 860, 5034, 0, 0, 0, 0, 0, - 0, 0, 0, 4244, 0, 0, 0, 0, 0, 0, - 1170, 0, 1171, 829, 830, 0, 4220, 0, 0, 4235, - 0, 0, 0, 0, 874, 0, 0, 0, 0, 0, - 0, 1286, 0, 0, 855, 0, 0, 0, 0, 1764, - 0, 0, 0, 0, 3094, 0, 828, 169, 0, 0, - 0, 1153, 0, 0, 0, 0, 0, 835, 869, 0, - 0, 0, 165, 166, 55, 1165, 0, 1161, 0, 0, - 73, 0, 0, 0, 0, 9, 0, 0, 0, 865, - 0, 0, 0, 1226, 0, 0, 2127, 2128, 2129, 2130, - 2131, 2132, 2133, 2134, 2135, 2136, 2137, 2149, 2150, 2151, - 2152, 2153, 2154, 2147, 2148, 0, 0, 74, 0, 170, - 171, 0, 0, 172, 0, 866, 870, 821, 0, 876, - 0, 0, 875, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1142, 852, 0, 850, 854, - 873, 0, 0, 0, 851, 848, 847, 0, 853, 838, - 839, 837, 0, 827, 840, 841, 842, 843, 822, 0, - 0, 871, 0, 872, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 867, 868, 861, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 834, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 863, 836, 0, 0, 0, 0, 0, 0, - 0, 823, 0, 0, 0, 1167, 0, 1160, 0, 0, - 858, 0, 4234, 0, 0, 0, 1164, 1163, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 4239, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1154, 0, 0, - 0, 900, 899, 906, 896, 0, 0, 0, 3262, 3263, - 3264, 0, 0, 0, 903, 904, 0, 905, 909, 0, - 0, 890, 0, 0, 4223, 4224, 900, 899, 906, 896, - 857, 914, 859, 856, 0, 0, 1169, 860, 0, 903, - 904, 1159, 905, 909, 0, 1158, 890, 0, 0, 0, - 0, 1147, 0, 0, 0, 0, 914, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 862, 0, 0, 0, - 1152, 3431, 831, 832, 0, 0, 820, 824, 855, 0, - 0, 0, 0, 0, 825, 0, 0, 0, 169, 0, - 828, 0, 0, 826, 0, 0, 0, 0, 0, 0, - 0, 835, 869, 918, 0, 0, 920, 0, 0, 0, - 169, 919, 0, 0, 0, 0, 0, 1150, 0, 0, - 0, 0, 0, 865, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1764, 0, 1764, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1168, 0, 0, 866, - 870, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1151, - 852, 0, 850, 854, 873, 0, 0, 0, 851, 848, - 847, 0, 853, 838, 839, 837, 0, 827, 840, 841, - 842, 843, 822, 0, 0, 871, 0, 872, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 867, 868, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2417, 0, 0, 0, 0, 2378, 0, 0, 2425, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3618, 891, 893, 892, 0, 863, 0, 0, 0, - 0, 1166, 0, 0, 898, 823, 0, 0, 0, 2419, - 2387, 0, 0, 0, 858, 0, 902, 891, 893, 892, - 2420, 2421, 0, 917, 0, 0, 0, 0, 0, 898, - 895, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 902, 0, 0, 0, 1146, 2386, 0, 917, 169, - 0, 0, 0, 0, 169, 895, 0, 0, 0, 0, - 0, 0, 0, 0, 2394, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 862, 0, 0, 0, 0, 0, 831, 832, 0, 0, - 820, 824, 0, 0, 0, 0, 0, 0, 825, 0, - 0, 0, 0, 0, 0, 0, 0, 826, 0, 0, - 0, 0, 0, 0, 2410, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1143, 0, 0, 0, - 1141, 1144, 0, 0, 0, 0, 0, 0, 1145, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 897, 901, 907, 0, 908, 910, 0, 0, 911, 912, - 913, 0, 0, 0, 915, 916, 0, 0, 0, 0, - 0, 1530, 0, 1531, 1529, 897, 901, 907, 0, 908, - 910, 0, 3785, 911, 912, 913, 0, 0, 0, 915, - 916, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2377, 2379, 2376, 0, 0, 0, 2373, 0, + 964, 1920, 940, 5030, 966, 5012, 4996, 2849, 3490, 5022, + 257, 2384, 4919, 4925, 2056, 3611, 4318, 4929, 4208, 4930, + 4918, 1919, 4270, 4678, 3040, 4811, 3919, 3795, 949, 3958, + 4757, 4871, 4566, 4025, 4499, 4239, 3285, 3484, 3093, 2005, + 3797, 1815, 4612, 4747, 4313, 4026, 2123, 1607, 4787, 4662, + 4139, 4677, 4411, 995, 3281, 3487, 758, 4023, 4449, 1370, + 1431, 4327, 4642, 3647, 867, 4758, 1862, 4760, 44, 4147, + 1462, 2110, 1456, 4153, 780, 2917, 4224, 4436, 3928, 4191, + 3441, 3283, 4418, 244, 5, 796, 806, 816, 3412, 2059, + 816, 1375, 3847, 4423, 942, 1427, 3865, 2334, 3083, 4099, + 4394, 2362, 168, 3851, 1410, 4007, 242, 3612, 4137, 2478, + 3580, 892, 3514, 3930, 2845, 3949, 3208, 3938, 3474, 4172, + 3128, 2355, 3607, 3468, 3610, 938, 3301, 3472, 3293, 3977, + 2530, 2643, 2572, 2475, 3829, 2106, 4091, 3827, 3816, 3466, + 1925, 3384, 3866, 3937, 3598, 3876, 887, 2129, 3868, 2107, + 2865, 1718, 1929, 883, 812, 2853, 3867, 3090, 1808, 2846, + 3399, 2241, 2780, 3863, 3777, 932, 2851, 933, 1912, 2676, + 3063, 2010, 3127, 2639, 2535, 2568, 1907, 3299, 2471, 2638, + 1908, 1142, 3372, 3366, 1903, 937, 3516, 2565, 2918, 3209, + 2356, 2779, 3013, 2374, 3415, 2896, 2363, 796, 1184, 3444, + 2835, 3445, 2364, 2864, 253, 7, 2305, 1233, 77, 2124, + 252, 6, 1292, 2051, 4, 2055, 2640, 77, 2672, 1755, + 941, 1932, 2855, 3495, 1863, 1838, 779, 1824, 1793, 2852, + 1787, 758, 2324, 2826, 931, 2340, 2782, 2117, 2092, 2042, + 1217, 2829, 2913, 1445, 2125, 1391, 2009, 1408, 950, 26, + 2552, 1870, 880, 2296, 2050, 1413, 1760, 257, 1234, 257, + 16, 1282, 1283, 1466, 2300, 239, 32, 1216, 1183, 796, + 2304, 38, 1792, 933, 1235, 1789, 1748, 1693, 1105, 243, + 797, 889, 42, 2130, 3014, 1262, 27, 1457, 1108, 1181, + 28, 2921, 19, 813, 10, 1223, 757, 1176, 17, 890, + 815, 233, 1165, 1107, 1667, 1608, 4772, 886, 4638, 151, + 852, 80, 3253, 79, 1441, 2619, 76, 2344, 2348, 2349, + 2350, 2345, 2354, 2346, 2352, 5, 3253, 2347, 1534, 2353, + 1535, 1533, 1534, 3253, 1535, 1533, 1534, 1229, 1535, 1533, + 2367, 1279, 4189, 4044, 3787, 1465, 810, 3897, 3786, 2265, + 3666, 3665, 811, 4374, 1376, 4156, 2655, 808, 1694, 15, + 1377, 4018, 3158, 1278, 3066, 1280, 3069, 3067, 1695, 800, + 3064, 2254, 1877, 1275, 1102, 939, 1873, 1274, 241, 788, + 4192, 3094, 782, 2778, 807, 1688, 3082, 783, 1791, 784, + 820, 785, 4733, 1104, 786, 1648, 1315, 1423, 1424, 1425, + 4170, 1422, 2579, 1933, 2608, 4049, 2609, 4793, 2604, 1842, + 1770, 4794, 1275, 1722, 1129, 1126, 4170, 1421, 3041, 4358, + 1275, 3788, 3784, 2580, 2793, 1376, 2785, 2261, 1697, 3762, + 3759, 3764, 3761, 5008, 1482, 9, 2248, 2774, 1684, 4311, + 3642, 3640, 2540, 4927, 4926, 809, 7, 4491, 4032, 77, + 3770, 3768, 6, 1875, 4742, 1534, 1273, 1535, 1533, 1534, + 4574, 1535, 1533, 4567, 77, 4314, 77, 4024, 1101, 2564, + 4763, 1602, 2848, 1106, 3088, 3731, 2993, 3814, 2559, 1938, + 1102, 2597, 2959, 5036, 4756, 5005, 4583, 1984, 4753, 4580, + 4624, 4128, 1315, 3186, 3772, 2800, 4823, 2165, 1839, 921, + 1703, 4123, 923, 4363, 1701, 1700, 3817, 922, 1117, 1414, + 2816, 1130, 1127, 1238, 2434, 3729, 2653, 4361, 885, 1890, + 1744, 3605, 1228, 2830, 3034, 1530, 2108, 2109, 4626, 3617, + 3618, 1333, 1334, 1295, 2455, 2456, 1092, 1227, 1091, 1093, + 1094, 2274, 1095, 1096, 2272, 3646, 2488, 3035, 197, 240, + 196, 229, 198, 3616, 1323, 1327, 1329, 1331, 1336, 1418, + 1341, 1337, 1338, 1339, 1340, 2279, 2280, 1318, 1319, 1320, + 1321, 1293, 1294, 1324, 1101, 1296, 1124, 1297, 1299, 1300, + 1301, 1302, 1298, 1303, 1304, 1305, 1306, 1307, 1314, 1316, + 1308, 1309, 1310, 1311, 1312, 1313, 1342, 1343, 1344, 1345, + 1346, 1347, 1348, 1349, 1351, 1350, 1352, 1353, 1354, 1355, + 1356, 1357, 1358, 1359, 1326, 1328, 1330, 1332, 1335, 1419, + 1118, 1420, 1478, 234, 2002, 1479, 1936, 1333, 1334, 1295, + 2454, 3763, 3760, 1284, 1794, 2999, 1796, 1710, 1202, 1130, + 1127, 2998, 1453, 1463, 1464, 2043, 3794, 1935, 2047, 3280, + 1323, 1327, 1329, 1331, 1336, 1317, 1341, 1337, 1338, 1339, + 1340, 4933, 4934, 1318, 1319, 1320, 1321, 1293, 1294, 1324, + 3084, 1296, 2046, 1297, 1299, 1300, 1301, 1302, 1298, 1303, + 1304, 1305, 1306, 1307, 1314, 1316, 1308, 1309, 1310, 1311, + 1312, 1313, 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349, + 1351, 1350, 1352, 1353, 1354, 1355, 1356, 1357, 1358, 1359, + 1326, 1328, 1330, 1332, 1335, 2466, 1876, 1874, 197, 240, + 196, 229, 198, 2951, 3278, 3923, 1481, 3021, 3368, 4248, + 3020, 3773, 876, 3022, 1523, 4227, 2058, 2024, 3369, 1528, + 921, 1226, 1510, 923, 1225, 1511, 4345, 1981, 922, 1461, + 2753, 1317, 3921, 1460, 1463, 1464, 4767, 3276, 4767, 4886, + 4766, 1128, 1125, 4766, 4885, 4765, 4884, 197, 240, 196, + 229, 198, 921, 1513, 4765, 923, 4745, 4241, 4764, 2000, + 922, 4959, 796, 2351, 2885, 4898, 2349, 2350, 3367, 4873, + 4230, 796, 4876, 234, 4764, 4883, 796, 1048, 1379, 1959, + 2048, 4225, 5000, 5001, 3277, 1389, 4250, 4251, 3648, 4570, + 4027, 4582, 4226, 1195, 1193, 3117, 1194, 2654, 816, 816, + 1405, 4027, 796, 796, 2045, 4873, 1843, 197, 240, 196, + 229, 198, 3273, 1121, 1386, 197, 240, 196, 229, 198, + 2275, 1771, 234, 2273, 1198, 1723, 1780, 2486, 2487, 1380, + 3653, 3649, 4233, 3650, 2657, 4365, 4783, 4053, 2472, 4932, + 2062, 4433, 197, 240, 196, 229, 198, 2462, 4138, 2038, + 812, 812, 812, 2649, 1451, 3843, 4145, 3001, 2344, 2348, + 2349, 2350, 2345, 2354, 2346, 2352, 3771, 1687, 2347, 3535, + 2353, 2824, 3443, 3599, 1944, 1433, 1979, 1508, 4771, 2062, + 4637, 793, 234, 4403, 4056, 2264, 1285, 1229, 1122, 3691, + 234, 3375, 3274, 4748, 4749, 4750, 4751, 1203, 3695, 3290, + 4900, 1480, 4252, 2001, 1459, 3252, 4034, 4344, 1430, 1383, + 1777, 796, 1526, 1527, 1377, 4346, 1439, 234, 3841, 1377, + 226, 2957, 3008, 1449, 2044, 1571, 1377, 4362, 3279, 1577, + 3689, 1468, 1469, 1525, 1471, 1472, 3150, 2581, 1474, 1509, + 1498, 1729, 2432, 4312, 3641, 4628, 4629, 2992, 1199, 2995, + 195, 227, 238, 228, 4249, 3593, 2922, 3837, 3667, 4634, + 2994, 3003, 3664, 4400, 1325, 1727, 2681, 1979, 1123, 1521, + 1522, 2618, 3838, 3839, 1971, 1394, 1397, 1379, 1275, 3004, + 3005, 4237, 3848, 1275, 1275, 4359, 1275, 3849, 3840, 813, + 813, 813, 3906, 1275, 1275, 3255, 3011, 1377, 4447, 1520, + 2061, 2060, 778, 4234, 4238, 4236, 4235, 2656, 4269, 4265, + 1201, 1490, 4581, 4705, 1503, 4627, 1473, 1505, 1858, 3925, + 4140, 1512, 1378, 3951, 3952, 1388, 1229, 1702, 3065, 3950, + 814, 1699, 1878, 4775, 4561, 1399, 3275, 1476, 1477, 2061, + 2060, 4615, 810, 810, 810, 1506, 1398, 4427, 811, 811, + 811, 4804, 4375, 808, 808, 808, 4162, 1228, 1106, 4011, + 1325, 4244, 4245, 924, 925, 926, 927, 928, 1372, 1447, + 1448, 1611, 1368, 1415, 1369, 2433, 1695, 3862, 3371, 4668, + 807, 807, 807, 3769, 4653, 1695, 3835, 1442, 1446, 1446, + 1446, 3877, 1200, 4796, 4364, 788, 78, 3416, 1403, 1404, + 4103, 1690, 1692, 783, 1696, 784, 1290, 785, 4105, 1131, + 786, 818, 1120, 1442, 1442, 817, 3603, 1728, 2832, 4258, + 1714, 1452, 3778, 2937, 1717, 2661, 2663, 2664, 1705, 2916, + 2940, 1726, 4788, 4252, 4806, 3920, 1197, 3849, 4367, 4368, + 4369, 809, 809, 809, 1665, 1467, 4228, 1670, 1470, 4243, + 1937, 1382, 1384, 1387, 4209, 4812, 1463, 1464, 796, 796, + 796, 1487, 2465, 1184, 1483, 1484, 2837, 1709, 1707, 1499, + 3489, 1769, 1489, 4216, 3485, 3486, 796, 3489, 1776, 1440, + 1578, 1782, 4117, 1463, 1464, 796, 1174, 2939, 1175, 758, + 758, 3960, 4621, 4383, 3953, 1501, 3954, 3956, 3955, 758, + 758, 2811, 1290, 1819, 1819, 4114, 796, 3807, 1504, 1507, + 2991, 2068, 2071, 2072, 1396, 1395, 4275, 796, 2924, 5047, + 4782, 1612, 2069, 3849, 1515, 4487, 3957, 1516, 3411, 4476, + 2351, 1500, 2969, 816, 2968, 780, 4330, 1401, 1980, 3564, + 3844, 1866, 1817, 1817, 1385, 1821, 2473, 1196, 3381, 814, + 4116, 1995, 1191, 4899, 2938, 1518, 257, 3600, 4813, 1192, + 1804, 1979, 2989, 2990, 1803, 758, 78, 1826, 1488, 4669, + 4173, 1833, 1835, 1438, 4654, 3692, 1437, 1720, 1721, 3374, + 1436, 1724, 5025, 1455, 1454, 1732, 1734, 1735, 1736, 1737, + 1778, 1739, 4643, 1623, 1624, 1743, 3926, 1972, 237, 1973, + 1573, 1574, 1575, 1576, 924, 925, 926, 927, 928, 814, + 3007, 4682, 3929, 1502, 2463, 78, 2039, 814, 1930, 4917, + 3753, 3248, 2960, 1749, 2351, 2916, 4404, 3536, 4309, 3537, + 3538, 4630, 4482, 1733, 3378, 3379, 924, 925, 926, 927, + 928, 1916, 1719, 885, 814, 2934, 1921, 1673, 3836, 3377, + 4870, 4100, 1781, 4179, 1671, 3951, 3952, 1934, 1790, 1813, + 1814, 1698, 4242, 1674, 2898, 2900, 1738, 3981, 1495, 4761, + 1228, 2649, 3946, 1759, 3146, 78, 1704, 4247, 4592, 1514, + 4593, 1957, 3026, 78, 1664, 1663, 2923, 1960, 1566, 2997, + 3262, 2925, 1669, 2955, 2783, 166, 1982, 2344, 2348, 2349, + 2350, 2345, 2354, 2346, 2352, 1730, 2927, 2347, 1924, 2353, + 78, 2646, 2337, 3959, 4231, 4232, 1819, 2461, 2449, 1519, + 1922, 2662, 1782, 3626, 3627, 1716, 4285, 806, 3996, 1798, + 1800, 1568, 1567, 3983, 812, 1731, 5026, 812, 812, 1811, + 1812, 1517, 3694, 1849, 1742, 2926, 4595, 2023, 1885, 1741, + 1740, 1970, 1754, 781, 1782, 1750, 1751, 1204, 3413, 2032, + 2032, 1855, 1782, 77, 1782, 1782, 822, 1829, 1996, 1983, + 821, 4681, 1173, 1494, 1827, 1819, 4594, 1819, 1379, 1706, + 1708, 4131, 3587, 1830, 1905, 1906, 2196, 2198, 2197, 1914, + 2015, 1802, 3947, 2257, 4490, 1987, 1990, 1991, 1994, 1911, + 1834, 1836, 1915, 2438, 2436, 1879, 2022, 2070, 2437, 2025, + 2026, 2836, 4478, 2007, 2008, 788, 4477, 3533, 791, 2812, + 792, 2027, 3565, 3567, 3568, 3569, 3566, 1867, 4916, 1160, + 2017, 2018, 1185, 1853, 4092, 1868, 2658, 2659, 2344, 2348, + 2349, 2350, 2345, 2354, 2346, 2352, 3115, 877, 2347, 1819, + 2353, 2029, 4500, 4501, 4502, 4506, 4504, 4505, 4507, 4508, + 4509, 4510, 4503, 1758, 2673, 2899, 1379, 2127, 2928, 1893, + 1910, 1571, 1894, 813, 2195, 3270, 813, 813, 2804, 2168, + 2169, 2170, 4043, 2178, 1713, 5023, 5024, 2933, 2111, 2282, + 813, 2931, 2184, 2159, 2160, 2185, 1719, 2164, 2283, 4483, + 4484, 1187, 1188, 1189, 1143, 2179, 4107, 1975, 2806, 2805, + 1711, 1712, 2034, 1942, 2204, 2205, 1945, 4180, 2186, 2803, + 2188, 2262, 2189, 2190, 2191, 2028, 810, 3555, 3556, 810, + 810, 2003, 811, 2035, 2281, 811, 811, 808, 1927, 1132, + 808, 808, 879, 1964, 2236, 878, 4437, 3263, 2256, 1968, + 2054, 2981, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, + 2086, 2087, 2088, 2089, 807, 1969, 1379, 807, 807, 1967, + 2103, 2104, 1229, 1962, 2016, 1966, 1133, 1961, 2266, 5049, + 1442, 1965, 2269, 4003, 5068, 1909, 2021, 796, 796, 796, + 2052, 4002, 1432, 4042, 1446, 2128, 2284, 2286, 1134, 2287, + 2163, 2289, 2290, 2237, 2073, 2293, 1446, 2012, 780, 2297, + 2036, 2037, 4880, 2301, 4560, 1819, 2307, 2308, 2239, 2310, + 1782, 2162, 2057, 2187, 796, 809, 3948, 1819, 809, 809, + 2527, 2006, 197, 240, 1184, 1943, 1532, 2335, 1946, 1947, + 1432, 1432, 1963, 2031, 2033, 2358, 2358, 1769, 2358, 2177, + 2258, 2368, 2369, 796, 796, 2049, 2431, 3420, 2242, 4587, + 2301, 2442, 1205, 4759, 1819, 2446, 2447, 3554, 1248, 1532, + 1769, 2011, 758, 2013, 2014, 2053, 1136, 197, 240, 3296, + 1979, 5064, 2645, 4038, 1997, 1998, 758, 2020, 1819, 2094, + 2245, 5057, 5040, 1888, 2040, 1891, 1892, 2326, 3388, 3394, + 3395, 3396, 3389, 3393, 3390, 3392, 3391, 234, 1895, 1896, + 1897, 1898, 1899, 1900, 3297, 3298, 3998, 1495, 240, 197, + 240, 2505, 2507, 3016, 3410, 796, 2301, 1819, 1237, 2514, + 2770, 796, 883, 883, 240, 2297, 197, 240, 2521, 1140, + 2523, 2524, 2525, 3406, 1138, 1137, 2531, 4003, 2386, 1229, + 1223, 4165, 234, 257, 2291, 2292, 257, 257, 877, 257, + 2645, 2298, 2299, 2351, 2309, 1954, 1532, 2360, 1237, 2366, + 3756, 2451, 2312, 2313, 2314, 1532, 3423, 5041, 1141, 2489, + 4088, 1951, 1952, 1493, 2495, 2440, 2490, 1237, 2496, 2192, + 2193, 2311, 234, 5062, 3404, 2891, 4003, 1534, 877, 1535, + 1533, 197, 240, 4055, 2717, 3858, 934, 2716, 234, 3145, + 2250, 234, 3896, 5038, 2090, 2091, 5032, 2645, 2101, 2102, + 2587, 2575, 2105, 3964, 5019, 2240, 4975, 2481, 2482, 2510, + 2306, 2467, 3962, 1228, 2295, 2516, 1139, 2458, 2246, 2460, + 4945, 3754, 2320, 1495, 3407, 2954, 1371, 2255, 2238, 2259, + 2479, 2480, 2571, 879, 2263, 3757, 878, 796, 2335, 2539, + 796, 2474, 2542, 2543, 2891, 2545, 3914, 2601, 1534, 2603, + 1535, 1533, 2450, 2607, 2513, 3911, 2573, 2563, 2574, 2445, + 2828, 2612, 2613, 2614, 4942, 2617, 1956, 1782, 1136, 2761, + 1371, 2150, 1532, 879, 2576, 1955, 878, 812, 2651, 3823, + 3440, 5033, 2332, 1492, 2351, 3820, 2582, 2330, 2331, 4976, + 3776, 4976, 2452, 2336, 2549, 1782, 1534, 2759, 1535, 1533, + 3439, 3145, 1819, 2642, 2439, 4946, 3755, 4590, 2602, 3425, + 2891, 2605, 2606, 1666, 2444, 2610, 4592, 3249, 4593, 1236, + 2924, 2927, 2052, 2529, 1534, 3089, 1535, 1533, 2621, 2622, + 2457, 1135, 2459, 2468, 4586, 2595, 1138, 1137, 2624, 2625, + 77, 4941, 3859, 77, 77, 2627, 77, 4935, 4913, 4943, + 2556, 4863, 2644, 2493, 2762, 2500, 2504, 2666, 2644, 1236, + 2492, 2512, 2498, 2453, 2517, 2518, 2909, 2611, 2519, 2777, + 2511, 2499, 1493, 1394, 1397, 1236, 2628, 2631, 1236, 3015, + 5034, 2536, 2760, 2633, 4595, 2635, 2199, 2200, 2201, 2202, + 1532, 2827, 2206, 2207, 2208, 2209, 2211, 2212, 2213, 2214, + 2215, 2216, 2217, 2218, 2219, 2220, 2221, 2222, 2223, 4862, + 1270, 1271, 1272, 2554, 4594, 4834, 813, 2344, 2348, 2349, + 2350, 2345, 2354, 2346, 2352, 2767, 2651, 2347, 2041, 2353, + 2766, 2726, 2691, 4914, 1666, 2690, 1532, 1534, 2725, 1535, + 1533, 1276, 1277, 2576, 1398, 1269, 1281, 2724, 1266, 3409, + 1110, 1111, 1112, 1113, 2589, 2591, 3440, 1534, 1229, 1535, + 1533, 2727, 2728, 4807, 2730, 1534, 2636, 1535, 1533, 810, + 1534, 2737, 1535, 1533, 1534, 811, 1535, 1533, 4697, 2527, + 808, 4696, 2335, 2928, 2594, 2670, 2671, 4695, 2923, 2916, + 2922, 4694, 2919, 2925, 1532, 2626, 1534, 2146, 1535, 1533, + 2691, 4693, 4672, 1534, 2143, 1535, 1533, 807, 2145, 2142, + 2144, 2148, 2149, 2620, 2593, 3726, 2147, 2784, 2484, 2786, + 4728, 2788, 2789, 2689, 4671, 2792, 4640, 2448, 3725, 1753, + 4609, 2632, 3229, 796, 1782, 796, 1782, 2114, 4606, 2679, + 2637, 1110, 1111, 1112, 1113, 1428, 2807, 2926, 2651, 1429, + 4554, 1446, 2813, 2814, 2765, 932, 4453, 2648, 2769, 796, + 796, 2687, 2693, 2691, 2825, 4452, 2691, 4385, 809, 4384, + 2768, 4280, 2691, 796, 796, 796, 2691, 4218, 2590, 2749, + 3125, 2674, 1976, 2665, 1495, 1805, 2691, 2651, 2178, 2178, + 2857, 4189, 3632, 4203, 3442, 2667, 3258, 2866, 1769, 1769, + 1769, 1769, 1769, 1769, 1769, 2094, 3148, 4200, 4081, 2651, + 4077, 2691, 1396, 1395, 2683, 1532, 3147, 1115, 2358, 3972, + 3116, 3714, 2623, 2862, 2820, 1832, 2712, 2629, 3662, 2695, + 2630, 2634, 2592, 2920, 1263, 1264, 1265, 1268, 2534, 1267, + 4399, 4454, 3659, 3429, 2946, 2260, 1939, 3427, 1586, 2901, + 2862, 4144, 3914, 2548, 3911, 3422, 1979, 3227, 3215, 2326, + 2326, 4552, 4219, 1400, 2153, 2154, 2155, 2156, 2157, 2158, + 2151, 2152, 3207, 3160, 2752, 2754, 2755, 2756, 3914, 2758, + 3182, 3183, 1365, 3120, 1360, 4278, 3079, 3176, 3077, 3075, + 1229, 2483, 3911, 4082, 2772, 4078, 1548, 4587, 3901, 3073, + 2892, 4588, 2861, 3686, 3973, 2797, 1532, 2799, 1115, 796, + 2032, 2953, 2771, 3428, 1341, 1337, 1338, 1339, 1340, 2733, + 2732, 3181, 4302, 3180, 3179, 3177, 2715, 3423, 3430, 2706, + 758, 758, 3428, 2668, 2669, 2705, 967, 977, 1379, 4016, + 3423, 3175, 2862, 1532, 1819, 796, 968, 5050, 969, 973, + 976, 972, 970, 971, 2704, 2854, 2787, 1532, 1532, 5042, + 2791, 2692, 1568, 1567, 2650, 1948, 4797, 1982, 2862, 3042, + 3043, 3080, 1566, 3078, 3074, 4438, 796, 2678, 2677, 1883, + 1882, 780, 3878, 3032, 3074, 2891, 2915, 2862, 1866, 2817, + 1769, 2167, 2166, 3054, 2914, 3056, 4176, 2770, 257, 1443, + 2867, 1807, 2167, 2166, 1532, 1532, 4655, 2996, 2890, 3178, + 3050, 1532, 4798, 974, 1532, 1568, 1567, 4174, 1428, 5004, + 1532, 4439, 1429, 2858, 2859, 1551, 1552, 1553, 1554, 1555, + 1548, 2952, 1611, 4773, 2178, 2889, 2178, 796, 3101, 1532, + 1992, 3096, 4177, 3064, 975, 3112, 2691, 3051, 2886, 2651, + 1949, 2908, 3023, 3118, 3024, 2868, 2869, 2870, 2871, 2872, + 2873, 1475, 4334, 4175, 3879, 3123, 2924, 2927, 4722, 3126, + 3129, 3017, 2100, 3029, 3030, 3131, 1809, 4721, 4639, 1819, + 3045, 2929, 2930, 2351, 2935, 4578, 1988, 1810, 2097, 2099, + 2096, 3053, 2098, 4519, 4480, 3142, 4479, 4656, 2642, 1428, + 4463, 3677, 4407, 1429, 3059, 1819, 1806, 1819, 2210, 3167, + 3880, 3633, 1379, 4183, 4155, 4004, 3994, 3986, 3133, 2203, + 3159, 1444, 1547, 1546, 1556, 1557, 1558, 1559, 1549, 1550, + 1551, 1552, 1553, 1554, 1555, 1548, 1229, 3036, 3006, 3012, + 3974, 3916, 3018, 4657, 3135, 3913, 3095, 3853, 3596, 3138, + 3595, 1819, 1379, 2719, 3431, 3426, 3189, 3058, 3190, 3122, + 1428, 3386, 1993, 3124, 1429, 1428, 3087, 3254, 3157, 1429, + 1798, 1800, 3033, 3198, 2537, 3028, 3134, 2790, 1819, 3796, + 2585, 3038, 1612, 2584, 2583, 3139, 3140, 1746, 3143, 4333, + 1817, 1745, 3184, 1549, 1550, 1551, 1552, 1553, 1554, 1555, + 1548, 1381, 2854, 4911, 3799, 77, 1428, 3047, 1989, 4801, + 1429, 3085, 3052, 1534, 3288, 1535, 1533, 1817, 1534, 3199, + 1535, 1533, 2118, 3132, 4019, 2288, 3799, 4017, 4882, 2928, + 1534, 3197, 1535, 1533, 2923, 2916, 2922, 2699, 2919, 2925, + 1534, 3068, 1535, 1533, 1535, 1533, 3256, 3204, 3205, 3169, + 2911, 3260, 3716, 2118, 3264, 2684, 1871, 4663, 2537, 4608, + 3100, 796, 796, 796, 3110, 3098, 1539, 1540, 1541, 1542, + 1543, 1544, 1545, 1537, 3119, 4607, 1534, 1533, 1535, 1533, + 1379, 3363, 3170, 3796, 3172, 3060, 3151, 3193, 1819, 3091, + 3092, 1782, 1534, 2926, 1535, 1533, 4469, 1782, 2442, 4494, + 4493, 1872, 3244, 3798, 1534, 3200, 1535, 1533, 3156, 3210, + 3211, 3187, 4192, 3881, 3144, 3216, 3715, 2708, 3149, 3525, + 2052, 3165, 1536, 3435, 3438, 1534, 3523, 1535, 1533, 1534, + 1570, 1535, 1533, 1871, 4401, 1534, 3502, 1535, 1533, 1580, + 3245, 3500, 3418, 1534, 4950, 1535, 1533, 3161, 3162, 4853, + 4854, 4978, 1769, 3701, 1769, 4922, 3154, 3432, 3433, 4851, + 4852, 3185, 3174, 3470, 3476, 1590, 1534, 3405, 1535, 1533, + 4699, 4700, 2182, 3246, 1379, 4408, 4409, 5046, 1534, 2920, + 1535, 1533, 1534, 3499, 1535, 1533, 4912, 2183, 2707, 3400, + 1379, 1379, 1379, 2032, 4907, 4402, 1379, 4820, 3509, 3510, + 3511, 3512, 1379, 3519, 5, 3520, 3521, 3231, 3522, 3232, + 3524, 3234, 4600, 3236, 3237, 1534, 3243, 1535, 1533, 4349, + 3104, 3519, 3403, 1588, 1534, 4857, 1535, 1533, 4348, 3303, + 3103, 4856, 1534, 2358, 1535, 1533, 1587, 4855, 3141, 1534, + 4337, 1535, 1533, 5045, 4336, 4850, 1534, 3577, 1535, 1533, + 4849, 4335, 3417, 4848, 4142, 1534, 4262, 1535, 1533, 3576, + 4847, 4069, 3382, 2386, 758, 4846, 4062, 1534, 3401, 1535, + 1533, 1534, 2442, 1535, 1533, 4845, 1379, 1379, 1534, 4843, + 1535, 1533, 4842, 1534, 3758, 1535, 1533, 1819, 1534, 2688, + 1535, 1533, 3614, 1534, 3380, 1535, 1533, 3574, 3239, 3408, + 3240, 3364, 3365, 2306, 3572, 796, 3582, 1379, 4841, 3164, + 3629, 1534, 3561, 1535, 1533, 4143, 3492, 3479, 4840, 4839, + 3575, 257, 4838, 3727, 3304, 7, 257, 3437, 77, 3448, + 3449, 6, 3504, 3498, 3450, 3434, 1556, 1557, 1558, 1559, + 1549, 1550, 1551, 1552, 1553, 1554, 1555, 1548, 3457, 2686, + 1534, 3459, 1535, 1533, 4836, 1866, 3458, 1534, 3573, 1535, + 1533, 3478, 4835, 3481, 4799, 3571, 3494, 3282, 1534, 3588, + 1535, 1533, 4685, 3560, 4675, 2958, 4658, 3669, 2961, 2962, + 2963, 2964, 2965, 2966, 2967, 3507, 3129, 2970, 2971, 2972, + 2973, 2974, 2975, 2976, 2977, 2978, 2979, 2980, 4633, 2982, + 2983, 2984, 2985, 2986, 3497, 2987, 1819, 3539, 3501, 3688, + 4605, 3615, 3721, 3601, 4568, 4489, 3385, 4441, 3493, 3661, + 4440, 3658, 4210, 3551, 4178, 4141, 4124, 3634, 1534, 3842, + 1535, 1533, 3638, 3670, 3493, 3505, 3506, 3623, 3645, 1534, + 3508, 1535, 1533, 3671, 3591, 3673, 3515, 1865, 3720, 3597, + 3462, 3463, 3464, 3644, 3585, 3678, 3303, 3559, 3717, 3558, + 3680, 3681, 3228, 1931, 3557, 3549, 3619, 3622, 3543, 3542, + 3541, 3496, 3496, 3226, 3540, 1534, 3496, 1535, 1533, 3672, + 3225, 3250, 3675, 3676, 3628, 1534, 3651, 1535, 1533, 1534, + 3679, 1535, 1533, 3636, 3121, 5048, 3635, 3682, 3081, 3025, + 1534, 2776, 1535, 1533, 3594, 2598, 2558, 1534, 2557, 1535, + 1533, 2555, 2551, 2550, 2502, 3224, 3782, 2271, 2268, 3785, + 3613, 3613, 1940, 1686, 3789, 3655, 796, 1782, 77, 4148, + 3223, 4154, 3828, 77, 3222, 3801, 3803, 3804, 3806, 3221, + 3808, 3809, 1534, 3690, 1535, 1533, 3220, 3107, 4631, 4632, + 3109, 3304, 5037, 3685, 1363, 3108, 1379, 1534, 3674, 1535, + 1533, 1534, 5035, 1535, 1533, 4319, 1534, 5002, 1535, 1533, + 4965, 3719, 4895, 1534, 4893, 1535, 1533, 4059, 4060, 3684, + 4613, 4868, 4785, 3732, 3733, 4412, 4779, 4770, 4768, 3734, + 3735, 3736, 3737, 3219, 3738, 3739, 3740, 3741, 3742, 3743, + 3744, 3745, 3746, 3747, 3748, 3749, 3713, 3218, 4752, 3698, + 3697, 3217, 3709, 3710, 1362, 4743, 3706, 4714, 3708, 1999, + 1534, 4713, 1535, 1533, 3214, 3707, 1767, 4704, 4703, 3213, + 4684, 3831, 3833, 4683, 1534, 4636, 1535, 1533, 1534, 4620, + 1535, 1533, 3846, 4618, 3765, 2019, 4604, 4569, 796, 4473, + 4472, 1534, 3212, 1535, 1533, 4471, 1534, 3775, 1535, 1533, + 4416, 4405, 4390, 3861, 4389, 1379, 3206, 4387, 796, 4380, + 796, 2442, 1379, 4378, 4357, 1379, 3780, 3779, 3194, 1534, + 4356, 1535, 1533, 3188, 2178, 4355, 2178, 4351, 3166, 3891, + 4350, 3892, 3766, 1534, 4324, 1535, 1533, 2764, 3792, 4321, + 1769, 2866, 2763, 3900, 4317, 1534, 4315, 1535, 1533, 4291, + 1534, 794, 1535, 1533, 3824, 1534, 2757, 1535, 1533, 2064, + 2065, 2066, 2067, 4288, 1534, 3857, 1535, 1533, 3470, 1534, + 4282, 1535, 1533, 3581, 3476, 3869, 3850, 4136, 4126, 4109, + 4093, 4068, 4066, 1534, 2946, 1535, 1533, 4033, 3992, 3991, + 3989, 3886, 3400, 3860, 3988, 3975, 3935, 3970, 3935, 3939, + 2113, 3939, 3939, 3969, 2115, 2893, 1379, 1229, 2119, 2120, + 2121, 2122, 1914, 1905, 1906, 3854, 1570, 3834, 3818, 2161, + 3810, 3800, 1911, 3790, 3965, 1915, 3783, 1534, 2172, 1535, + 1533, 2242, 1819, 1819, 3781, 3403, 3890, 2781, 3696, 3888, + 3821, 3898, 3961, 3693, 3668, 3643, 3621, 3589, 3370, 3918, + 3586, 3583, 3570, 3562, 3552, 3550, 3922, 3924, 3546, 3856, + 3545, 3544, 3453, 1180, 3289, 3271, 3259, 3251, 1048, 1047, + 1801, 1817, 1817, 3966, 3967, 3873, 3102, 3086, 3875, 796, + 3882, 3889, 2228, 3884, 2230, 2231, 2232, 2233, 2234, 3895, + 3887, 3899, 2854, 3831, 3048, 2243, 2808, 2795, 2794, 2660, + 2562, 3915, 2561, 2553, 2303, 2270, 1782, 3912, 2267, 2442, + 2442, 2253, 2252, 3934, 2030, 3936, 1941, 1619, 3944, 1615, + 1614, 1367, 1119, 4990, 3917, 240, 196, 229, 198, 4815, + 4610, 2915, 4599, 3189, 5018, 794, 4598, 4584, 4579, 2914, + 4388, 4020, 4354, 3933, 3940, 3941, 197, 240, 3945, 3493, + 240, 196, 229, 198, 3942, 4331, 3493, 4283, 5017, 3493, + 4181, 4171, 4169, 4164, 4089, 4087, 4084, 4083, 199, 979, + 169, 4080, 4001, 3963, 4079, 4005, 4067, 3531, 3532, 169, + 4065, 4022, 4021, 3971, 1561, 4010, 1565, 3979, 197, 240, + 4006, 3905, 3547, 3548, 3894, 796, 3704, 3705, 3822, 234, + 4833, 3819, 1562, 1564, 1560, 3774, 1563, 1547, 1546, 1556, + 1557, 1558, 1559, 1549, 1550, 1551, 1552, 1553, 1554, 1555, + 1548, 234, 3976, 3723, 234, 3592, 3711, 3985, 4039, 3984, + 3703, 3999, 4000, 4045, 4041, 2243, 4047, 4048, 2696, 3885, + 2243, 2243, 3990, 4050, 3993, 197, 240, 789, 3702, 3700, + 3493, 3465, 3076, 3987, 3072, 169, 3071, 4030, 3070, 3039, + 3037, 2738, 2731, 234, 5066, 2328, 2723, 2722, 2721, 2720, + 4046, 2718, 2714, 2713, 2711, 4040, 2702, 3997, 2698, 2697, + 2560, 2229, 2227, 2226, 2225, 4095, 2224, 2181, 2180, 4096, + 2171, 2531, 4037, 1831, 240, 5044, 2325, 3491, 2538, 4989, + 4969, 2541, 4949, 4110, 2544, 4112, 4861, 2546, 4819, 1609, + 4118, 4070, 3470, 3476, 4814, 4737, 4734, 4052, 4712, 4692, + 2327, 4686, 4563, 4562, 1379, 4106, 4514, 1892, 4492, 4486, + 4051, 4462, 4260, 4259, 4256, 4119, 1103, 4057, 1895, 1896, + 1897, 1898, 1899, 1900, 4255, 4254, 4217, 4214, 4212, 796, + 2442, 4157, 4108, 4104, 3907, 2569, 3904, 1910, 1913, 1887, + 4113, 1904, 4115, 4163, 1901, 1889, 1884, 884, 234, 3815, + 3812, 4085, 3811, 4168, 3712, 3584, 3578, 3503, 3421, 3414, + 1756, 3247, 3230, 3046, 4101, 3027, 2988, 2878, 2860, 2819, + 2818, 2775, 2095, 234, 4134, 4135, 2358, 1769, 4186, 2577, + 2329, 2249, 1977, 1902, 1230, 2335, 1685, 4086, 4195, 1668, + 1662, 169, 3470, 1661, 3129, 3476, 4094, 4205, 3979, 1660, + 4152, 4098, 1659, 1658, 4090, 1657, 169, 1656, 169, 1655, + 4220, 1654, 1653, 1379, 1652, 1651, 1650, 1649, 1648, 1647, + 4121, 1646, 1103, 3935, 4072, 4125, 4074, 1379, 4076, 4129, + 4132, 4120, 1645, 4149, 1644, 1643, 1642, 1641, 1640, 4187, + 4161, 1639, 1638, 1379, 1637, 4277, 1636, 1635, 1634, 1819, + 1633, 4130, 4151, 3303, 1632, 1631, 1630, 4271, 4272, 4273, + 4274, 4188, 1629, 1628, 4286, 1627, 1626, 1625, 1622, 1621, + 1620, 1618, 1617, 1616, 1613, 1606, 1605, 796, 4185, 2442, + 4182, 1603, 4193, 1769, 1379, 1602, 1601, 1600, 1817, 4253, + 4279, 1599, 4303, 1598, 1597, 1596, 4305, 4184, 257, 1595, + 1594, 1593, 1592, 1591, 1585, 1584, 1583, 4246, 1582, 1581, + 1497, 1364, 4831, 4202, 4201, 4199, 4322, 4207, 3446, 3447, + 4829, 2680, 4826, 4257, 4299, 2685, 2834, 4211, 2616, 4213, + 4326, 1406, 4982, 2694, 4980, 4931, 3982, 3590, 3387, 4263, + 4261, 4295, 3044, 4266, 2844, 4292, 1496, 1426, 3304, 2876, + 2888, 2883, 2881, 4347, 2879, 4276, 2884, 2882, 81, 2880, + 2875, 3456, 3455, 3454, 4281, 3451, 2887, 2874, 4881, 4755, + 4754, 4732, 2703, 4284, 4287, 4558, 5063, 4290, 4467, 3660, + 2710, 4293, 3424, 3909, 4296, 2335, 1747, 4325, 4370, 3852, + 4372, 4297, 4376, 3910, 4298, 4167, 4300, 3657, 4382, 4329, + 3931, 4289, 3932, 4294, 4310, 2322, 2323, 2956, 2729, 4267, + 4320, 4035, 4036, 2734, 2735, 2736, 4008, 3874, 2739, 2740, + 2741, 2742, 2743, 2744, 2745, 2746, 2747, 2748, 1366, 2750, + 2751, 4133, 2423, 4323, 3419, 4009, 787, 1180, 1379, 1819, + 1819, 1880, 1374, 4417, 3908, 1926, 1379, 4301, 3155, 4379, + 2802, 4381, 2317, 2318, 2319, 2903, 4422, 4221, 3091, 3092, + 1379, 2801, 4338, 4422, 4339, 1923, 4340, 1190, 1409, 1411, + 2809, 4264, 4352, 4451, 2520, 4442, 2905, 2904, 1817, 2111, + 4414, 4360, 4366, 2435, 4413, 4426, 1491, 3515, 4689, 4386, + 3527, 4373, 3826, 1379, 3825, 1379, 3480, 4456, 3528, 3529, + 3530, 3105, 2907, 2842, 2333, 77, 4446, 4459, 4393, 4461, + 2294, 4415, 4993, 4419, 1819, 4970, 4396, 2167, 2166, 1681, + 1682, 4688, 4406, 1679, 1680, 4444, 4445, 3968, 3613, 1677, + 1678, 3009, 4434, 796, 4435, 1379, 1379, 1379, 4429, 4432, + 1379, 1379, 4424, 1675, 1676, 3002, 4425, 4188, 2902, 2443, + 4448, 1486, 1485, 2111, 1524, 3663, 2586, 3461, 2582, 2810, + 2588, 4392, 1986, 1435, 1434, 4455, 4553, 1417, 1458, 2600, + 4956, 4518, 4516, 4511, 4954, 4398, 4901, 1531, 4465, 4253, + 4468, 4496, 4497, 4498, 4397, 4474, 4512, 4513, 4878, 3153, + 4877, 4875, 4564, 4565, 4789, 4738, 4457, 4246, 3152, 4316, + 4198, 4197, 4071, 4029, 4028, 4014, 2566, 2941, 2910, 1928, + 1819, 4013, 3631, 1432, 4984, 4983, 4909, 4910, 4960, 4377, + 4111, 4097, 3683, 4464, 3266, 3265, 3257, 3049, 2700, 2063, + 1402, 1373, 4983, 4470, 4984, 4488, 4971, 4557, 4906, 4601, + 4602, 3286, 796, 3284, 4555, 4395, 3654, 1918, 4556, 1817, + 1371, 4577, 4591, 790, 1450, 89, 4614, 2, 4616, 1110, + 1111, 1112, 1113, 5006, 4571, 2243, 5007, 2243, 1, 3767, + 2773, 4517, 1110, 1111, 1112, 1113, 2247, 1371, 1683, 4576, + 1114, 1109, 1795, 4617, 3019, 4619, 2243, 2243, 2485, 4585, + 3493, 4589, 1823, 2251, 1116, 2894, 2895, 3460, 2897, 2503, + 1412, 3272, 4572, 2647, 3613, 3602, 3000, 2823, 4648, 3845, + 1757, 1186, 2173, 4646, 1953, 1393, 1950, 1392, 4622, 1390, + 2116, 2194, 981, 2847, 3579, 3553, 4194, 4992, 1866, 5029, + 4948, 4451, 4995, 1974, 1865, 4665, 965, 2057, 4869, 2057, + 4031, 3652, 4744, 1379, 4952, 4746, 4575, 2652, 4641, 1529, + 4623, 169, 169, 169, 1230, 3883, 4635, 4674, 4680, 848, + 1025, 993, 1604, 789, 4660, 2570, 3730, 3728, 4647, 4644, + 992, 4146, 3376, 4559, 4649, 3625, 4652, 4329, 4651, 1561, + 849, 1565, 2547, 4741, 4659, 4573, 1881, 1886, 2906, 4664, + 3111, 4667, 3114, 4670, 4661, 1379, 4810, 1562, 1564, 1560, + 4466, 1563, 1547, 1546, 1556, 1557, 1558, 1559, 1549, 1550, + 1551, 1552, 1553, 1554, 1555, 1548, 3927, 3488, 4596, 4597, + 1917, 4805, 4687, 4215, 1180, 1180, 1752, 4343, 4341, 4342, + 891, 2464, 1819, 3136, 3137, 4729, 756, 1214, 4515, 2843, + 2615, 4698, 1774, 4520, 4735, 4736, 4691, 1158, 4127, 2833, + 1159, 1788, 1151, 3398, 3397, 2074, 1538, 2093, 3751, 3752, + 1579, 4158, 4159, 4160, 936, 2682, 3373, 4240, 3620, 88, + 87, 1817, 1825, 4726, 86, 4730, 4731, 85, 265, 984, + 264, 4611, 4410, 1837, 4864, 4997, 962, 961, 4769, 960, + 959, 3168, 958, 1569, 3171, 4762, 4774, 957, 2342, 2343, + 2341, 2339, 2338, 1764, 1763, 4781, 3191, 3192, 3630, 4739, + 4012, 2526, 2528, 4740, 3518, 3195, 3196, 4268, 3513, 2375, + 2373, 1786, 2936, 4776, 2943, 4777, 2372, 4928, 4058, 4332, + 4821, 3201, 3202, 3203, 4822, 4485, 3563, 4790, 4328, 2316, + 2932, 2392, 3534, 4786, 2389, 2388, 3526, 4481, 4475, 2420, + 5054, 5013, 5011, 4905, 4431, 4204, 4778, 3475, 4430, 4421, + 4222, 4223, 4229, 1322, 2841, 1291, 4676, 3233, 1286, 3235, + 4809, 4784, 3238, 1379, 2064, 1288, 1289, 2243, 1287, 4792, + 4795, 3173, 3995, 2912, 3864, 3295, 4837, 2057, 3294, 3292, + 3291, 1725, 4780, 1379, 4803, 4897, 4825, 4827, 4828, 4830, + 4832, 4391, 3302, 3300, 1416, 1978, 1407, 4844, 1819, 4859, + 4808, 1361, 4800, 2856, 3793, 4817, 1691, 1689, 4865, 4824, + 1547, 1546, 1556, 1557, 1558, 1559, 1549, 1550, 1551, 1552, + 1553, 1554, 1555, 1548, 3452, 4866, 2877, 2567, 3656, 1765, + 1762, 1761, 1261, 1260, 2497, 1232, 235, 1817, 2491, 4858, + 1231, 236, 1103, 1100, 4102, 4166, 4894, 54, 1672, 3604, + 3010, 3106, 3469, 4625, 4867, 2321, 4460, 1152, 2831, 124, + 4874, 4872, 1819, 48, 142, 123, 4891, 4887, 4889, 215, + 69, 214, 68, 20, 4896, 140, 213, 67, 53, 52, + 4888, 4890, 211, 4915, 119, 4892, 118, 117, 116, 4923, + 139, 210, 66, 249, 248, 251, 250, 4902, 247, 3061, + 4904, 1817, 3062, 4903, 246, 1869, 245, 4879, 4428, 4908, + 1547, 1546, 1556, 1557, 1558, 1559, 1549, 1550, 1551, 1552, + 1553, 1554, 1555, 1548, 4860, 51, 50, 216, 49, 125, + 1768, 4936, 70, 47, 4937, 46, 4938, 2599, 4939, 3813, + 4940, 1985, 4122, 3287, 2815, 45, 40, 14, 12, 43, + 41, 25, 24, 4944, 1958, 23, 31, 30, 29, 13, + 39, 37, 4955, 36, 4957, 4958, 161, 160, 1828, 35, + 4947, 159, 1379, 789, 4953, 4951, 158, 157, 156, 155, + 154, 4762, 153, 4961, 34, 22, 4962, 61, 4963, 60, + 4964, 59, 58, 57, 56, 8, 4190, 4680, 146, 4968, + 145, 147, 143, 138, 136, 33, 137, 134, 4974, 4973, + 4972, 135, 128, 127, 2243, 169, 126, 4991, 4981, 4979, + 4999, 120, 4977, 100, 4998, 99, 98, 2057, 113, 4985, + 4986, 4987, 4988, 112, 111, 110, 109, 108, 1379, 106, + 5014, 1379, 107, 5003, 847, 97, 96, 95, 94, 93, + 133, 132, 5009, 129, 115, 4809, 5016, 5010, 122, 5020, + 121, 3637, 5021, 3639, 5027, 104, 5031, 114, 105, 103, + 102, 101, 4551, 92, 91, 90, 131, 130, 144, 217, + 71, 5028, 194, 193, 192, 191, 2569, 190, 2178, 5039, + 188, 1865, 189, 187, 1174, 5043, 1175, 186, 4458, 185, + 184, 183, 182, 62, 4999, 5052, 63, 64, 4998, 65, + 5051, 206, 5014, 5055, 205, 207, 5058, 5053, 219, 218, + 209, 212, 5031, 5059, 208, 169, 203, 201, 169, 169, + 204, 1772, 1773, 5065, 1775, 1156, 202, 1779, 5067, 1783, + 1784, 1785, 169, 2276, 2277, 2278, 200, 82, 11, 1169, + 141, 1164, 1547, 1546, 1556, 1557, 1558, 1559, 1549, 1550, + 1551, 1552, 1553, 1554, 1555, 1548, 3699, 21, 18, 3, + 0, 0, 0, 0, 0, 0, 0, 1840, 1841, 0, + 2315, 1844, 1845, 1846, 1847, 1848, 0, 1850, 1851, 1852, + 0, 1854, 0, 1857, 3722, 1859, 1860, 1861, 0, 0, + 3724, 0, 197, 240, 196, 229, 198, 0, 0, 2370, + 2371, 0, 0, 0, 0, 0, 4966, 0, 0, 1145, + 0, 0, 230, 0, 0, 0, 3750, 4650, 0, 222, + 0, 0, 0, 231, 3718, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 199, 0, 0, 0, + 0, 0, 0, 167, 1547, 1546, 1556, 1557, 1558, 1559, + 1549, 1550, 1551, 1552, 1553, 1554, 1555, 1548, 152, 0, + 0, 0, 0, 0, 0, 0, 0, 234, 0, 0, + 0, 1411, 2057, 0, 0, 3613, 0, 1411, 1547, 1546, + 1556, 1557, 1558, 1559, 1549, 1550, 1551, 1552, 1553, 1554, + 1555, 1548, 0, 0, 0, 0, 0, 0, 0, 1171, + 0, 1163, 0, 0, 0, 4701, 4702, 0, 0, 0, + 1168, 1166, 4706, 4707, 4708, 4709, 4710, 4711, 0, 1569, + 0, 4715, 4716, 4717, 4718, 4719, 4720, 0, 0, 0, + 0, 1157, 4723, 4724, 4725, 2701, 4727, 0, 1547, 1546, + 1556, 1557, 1558, 1559, 1549, 1550, 1551, 1552, 1553, 1554, + 1555, 1548, 0, 0, 0, 1257, 176, 177, 0, 178, + 179, 0, 3163, 0, 180, 0, 0, 181, 0, 0, + 1173, 0, 0, 0, 0, 1162, 0, 0, 0, 1161, + 0, 0, 0, 0, 0, 1150, 1547, 1546, 1556, 1557, + 1558, 1559, 1549, 1550, 1551, 1552, 1553, 1554, 1555, 1548, + 0, 0, 0, 1409, 1155, 0, 2596, 0, 2243, 0, + 0, 0, 0, 2243, 0, 0, 0, 0, 3893, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3902, 4791, 0, 3903, 0, 0, 0, 0, 0, 1258, + 0, 0, 0, 0, 195, 227, 238, 228, 83, 148, + 0, 0, 1167, 0, 0, 1153, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 150, 221, + 220, 0, 4818, 2675, 0, 84, 0, 0, 169, 1534, + 0, 1535, 1533, 0, 0, 0, 0, 0, 0, 0, + 3943, 0, 0, 175, 1172, 0, 2361, 1547, 1546, 1556, + 1557, 1558, 1559, 1549, 1550, 1551, 1552, 1553, 1554, 1555, + 1548, 0, 0, 0, 0, 0, 0, 1154, 0, 1768, + 149, 0, 0, 0, 0, 0, 0, 0, 0, 1251, + 0, 1243, 1247, 1249, 0, 0, 223, 224, 225, 1547, + 1546, 1556, 1557, 1558, 1559, 1549, 1550, 1551, 1552, 1553, + 1554, 1555, 1548, 0, 0, 0, 0, 1254, 0, 0, + 0, 1246, 0, 0, 2494, 0, 2501, 0, 0, 0, + 2150, 3978, 0, 0, 0, 0, 0, 0, 0, 0, + 903, 902, 909, 899, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 906, 907, 0, 908, 912, 0, 1170, + 893, 0, 169, 0, 0, 169, 169, 0, 169, 0, + 917, 232, 1252, 1546, 1556, 1557, 1558, 1559, 1549, 1550, + 1551, 1552, 1553, 1554, 1555, 1548, 0, 0, 0, 0, + 0, 0, 162, 0, 1255, 0, 226, 0, 163, 0, + 0, 1256, 0, 1149, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 921, 0, 169, + 923, 0, 0, 0, 0, 922, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 169, 0, + 0, 0, 0, 0, 0, 1244, 0, 0, 0, 0, + 0, 0, 0, 0, 164, 0, 0, 0, 0, 2796, + 0, 2798, 0, 4061, 0, 0, 0, 75, 0, 1253, + 0, 0, 4063, 4064, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1172, 2821, 2822, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2838, + 2839, 2840, 4073, 0, 4075, 0, 0, 1245, 0, 0, + 0, 0, 0, 0, 1146, 0, 0, 0, 1144, 1147, + 78, 0, 0, 0, 0, 0, 1148, 0, 0, 0, + 2508, 2509, 0, 0, 0, 0, 0, 2515, 0, 0, + 0, 0, 0, 0, 0, 0, 2522, 0, 0, 0, + 0, 173, 237, 174, 0, 0, 2146, 0, 0, 0, + 0, 0, 3978, 2143, 0, 0, 72, 2145, 2142, 2144, + 2148, 2149, 0, 0, 0, 2147, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1569, 0, 0, 0, 1250, + 0, 894, 896, 895, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 901, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 905, 0, 0, 0, 2578, + 0, 0, 920, 0, 0, 0, 0, 0, 0, 898, + 0, 0, 0, 1240, 0, 1788, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 165, 166, + 55, 0, 0, 0, 0, 0, 73, 0, 0, 0, + 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1825, 0, 0, 0, 0, 0, 0, 1315, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 74, 0, 170, 171, 0, 0, 172, + 0, 0, 1411, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2131, 2132, 2133, 2134, 2135, 2136, 2137, 2138, + 2139, 2140, 2141, 2153, 2154, 2155, 2156, 2157, 2158, 2151, + 2152, 2150, 0, 0, 2243, 0, 0, 0, 836, 0, + 0, 0, 0, 0, 1259, 0, 0, 0, 1239, 1241, + 0, 2243, 0, 0, 0, 0, 1242, 0, 0, 0, + 4304, 0, 0, 3099, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 900, 904, 910, 0, 911, 913, 0, + 0, 914, 915, 916, 0, 0, 0, 918, 919, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 832, 833, 1333, 1334, 1295, 0, 1230, 0, 0, + 169, 169, 877, 0, 0, 0, 0, 1768, 1768, 1768, + 1768, 1768, 1768, 1768, 0, 4353, 1323, 1327, 1329, 1331, + 1336, 0, 1341, 1337, 1338, 1339, 1340, 0, 0, 1318, + 1319, 1320, 1321, 1293, 1294, 1324, 0, 1296, 0, 1297, + 1299, 1300, 1301, 1302, 1298, 1303, 1304, 1305, 1306, 1307, + 1314, 1316, 1308, 1309, 1310, 1311, 1312, 1313, 1342, 1343, + 1344, 1345, 1346, 1347, 1348, 1349, 1351, 1350, 1352, 1353, + 1354, 1355, 1356, 1357, 1358, 1359, 1326, 1328, 1330, 1332, + 1335, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 879, 0, 0, + 878, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4443, 0, 0, 0, 0, 0, 0, 1317, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 897, 0, 0, 0, 2146, 0, 0, + 0, 0, 836, 0, 2143, 0, 0, 0, 2145, 2142, + 2144, 2148, 2149, 0, 864, 0, 2147, 0, 0, 0, + 0, 0, 0, 0, 837, 0, 0, 3267, 3268, 3269, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 924, 925, 926, 927, 928, 0, 0, 0, 0, + 0, 839, 0, 0, 0, 0, 0, 0, 0, 1768, + 0, 0, 0, 0, 0, 0, 0, 169, 0, 0, + 0, 0, 0, 0, 0, 832, 833, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 877, 0, 0, 0, + 3436, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1230, 0, 0, 0, 0, 0, 903, + 902, 909, 899, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 906, 907, 0, 908, 912, 0, 860, 893, + 862, 859, 0, 0, 0, 863, 0, 0, 0, 917, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2131, 2132, 2133, 2134, 2135, 2136, 2137, + 2138, 2139, 2140, 2141, 2153, 2154, 2155, 2156, 2157, 2158, + 2151, 2152, 0, 0, 0, 0, 0, 0, 0, 0, + 858, 879, 0, 0, 878, 0, 921, 0, 0, 923, + 0, 0, 831, 0, 922, 0, 0, 0, 0, 0, + 0, 0, 0, 838, 872, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 868, 0, 0, 0, 0, + 0, 0, 0, 0, 1865, 0, 0, 0, 864, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 837, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 869, 873, 824, 0, 0, 0, 0, 0, 0, + 0, 3624, 0, 0, 0, 839, 0, 0, 0, 4690, + 0, 0, 855, 0, 853, 857, 876, 0, 0, 0, + 854, 851, 850, 0, 856, 841, 842, 840, 0, 830, + 843, 844, 845, 846, 825, 0, 1325, 874, 0, 875, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 870, 871, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 903, 902, 909, 899, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 906, 907, 0, 908, + 912, 0, 860, 893, 862, 859, 0, 0, 866, 863, + 894, 896, 895, 917, 0, 0, 0, 826, 169, 0, + 2421, 0, 901, 0, 0, 2382, 861, 0, 2429, 0, + 0, 0, 1590, 0, 905, 0, 0, 0, 0, 0, + 169, 920, 0, 0, 0, 0, 0, 0, 898, 0, + 0, 0, 888, 0, 858, 0, 0, 0, 2423, 2391, + 0, 0, 0, 0, 0, 0, 831, 0, 0, 2424, + 2425, 1768, 0, 1768, 0, 0, 0, 838, 872, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2390, 0, 0, 1290, 868, + 4802, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2398, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2404, 0, 0, 0, 0, 0, - 0, 0, 2389, 0, 2372, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2392, 2426, 0, 0, 2393, 2395, - 2397, 0, 2399, 2400, 2401, 2405, 2406, 2407, 2409, 2412, - 2413, 2414, 2146, 0, 0, 0, 0, 0, 0, 2402, - 2411, 2403, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 2381, 0, 2417, 0, 0, 0, 0, 2378, 0, - 0, 2425, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3849, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 894, 2419, 2387, 2418, 3864, 0, 3865, 0, 0, 0, - 0, 0, 2420, 2421, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 894, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2386, 0, - 0, 0, 0, 1226, 0, 169, 0, 0, 0, 0, - 2374, 2375, 0, 0, 0, 0, 2394, 0, 0, 0, - 0, 169, 0, 0, 0, 0, 0, 0, 2415, 0, - 0, 0, 0, 921, 922, 923, 924, 925, 1764, 0, - 0, 0, 0, 0, 0, 0, 2391, 0, 0, 0, - 2390, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2408, 0, 0, 0, 0, 169, - 0, 0, 2417, 2396, 0, 0, 0, 0, 0, 0, - 197, 240, 0, 0, 0, 0, 2410, 2423, 2422, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4410, 0, 0, 0, 0, 0, - 2419, 0, 0, 0, 2142, 0, 0, 0, 0, 0, - 0, 2139, 0, 0, 1407, 2141, 2138, 2140, 2144, 2145, - 0, 0, 0, 2143, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2383, 0, 0, 0, 0, 0, - 0, 0, 0, 2417, 0, 234, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2394, 0, 0, 0, 0, - 0, 0, 3972, 0, 2377, 3478, 2376, 0, 0, 0, - 3477, 0, 0, 0, 0, 2398, 0, 2424, 0, 0, - 0, 2419, 0, 0, 0, 0, 2404, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2392, 2426, 0, 0, - 2393, 2395, 2397, 0, 2399, 2400, 2401, 2405, 2406, 2407, - 2409, 2412, 2413, 2414, 0, 0, 4662, 0, 0, 0, - 4046, 2402, 2411, 2403, 0, 2410, 2394, 169, 0, 0, - 0, 0, 0, 2381, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2127, 2128, 2129, 2130, 2131, 2132, 2133, 2134, 2135, 2136, - 2137, 2149, 2150, 2151, 2152, 2153, 2154, 2147, 2148, 0, - 0, 0, 0, 0, 0, 2418, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 2410, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2374, 2375, 2398, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2404, 0, 2417, 0, 0, - 2415, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2392, 2426, 0, 2391, 2393, - 2395, 2397, 2390, 2399, 2400, 2401, 2405, 2406, 2407, 2409, - 2412, 2413, 2414, 0, 1407, 2419, 0, 0, 0, 0, - 2402, 2411, 2403, 3972, 0, 0, 2408, 0, 0, 0, - 0, 0, 0, 169, 169, 2396, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2398, 0, 0, 0, 2423, - 2422, 0, 0, 0, 0, 0, 2404, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2394, 0, 0, 0, 2418, 0, 2392, 2426, 0, 0, - 2393, 2395, 2397, 0, 2399, 2400, 2401, 2405, 2406, 2407, - 2409, 2412, 2413, 2414, 0, 0, 0, 0, 0, 0, - 0, 2402, 2411, 2403, 1764, 0, 2383, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 2415, - 0, 0, 0, 0, 0, 0, 4628, 0, 0, 2424, - 2410, 0, 0, 0, 0, 2418, 0, 2391, 0, 0, - 1407, 2390, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 865, 4816, 0, 0, 0, 0, 834, 835, + 0, 0, 823, 827, 0, 869, 873, 0, 0, 0, + 828, 0, 3791, 0, 0, 0, 0, 0, 0, 829, + 0, 0, 0, 0, 0, 0, 855, 0, 853, 857, + 876, 0, 0, 0, 854, 851, 850, 0, 856, 841, + 842, 840, 0, 830, 843, 844, 845, 846, 825, 0, + 0, 874, 0, 875, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2414, 870, 871, 0, 0, 0, 0, + 0, 0, 900, 904, 910, 0, 911, 913, 0, 0, + 914, 915, 916, 0, 0, 0, 918, 919, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 866, 0, 894, 896, 895, 0, 0, 0, + 169, 826, 0, 0, 0, 169, 901, 0, 0, 0, + 861, 4920, 0, 0, 0, 0, 4924, 0, 905, 0, + 0, 0, 0, 0, 3855, 920, 0, 0, 0, 0, + 0, 0, 898, 0, 0, 0, 0, 0, 0, 0, + 0, 2381, 2383, 2380, 3870, 0, 3871, 2377, 0, 0, + 0, 0, 2402, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2408, 0, 0, 0, 0, 0, 0, + 0, 2393, 0, 2376, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2396, 2430, 0, 0, 2397, 2399, 2401, + 0, 2403, 2404, 2405, 2409, 2410, 2411, 2413, 2416, 2417, + 2418, 0, 0, 0, 0, 0, 865, 0, 2406, 2415, + 2407, 0, 834, 835, 4920, 0, 823, 827, 0, 0, + 2385, 0, 0, 0, 828, 0, 0, 0, 0, 2421, + 0, 0, 897, 829, 2382, 0, 0, 2429, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2422, 0, 0, 0, 0, 2423, 2391, 0, + 0, 0, 0, 4920, 0, 0, 0, 0, 2424, 2425, + 924, 925, 926, 927, 928, 0, 900, 904, 910, 0, + 911, 913, 0, 0, 914, 915, 916, 0, 0, 0, + 918, 919, 0, 0, 2390, 0, 0, 0, 0, 0, + 0, 0, 0, 2378, 2379, 1411, 0, 0, 0, 0, + 0, 0, 2398, 0, 2421, 0, 0, 0, 0, 0, + 0, 2419, 197, 240, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5061, 0, 2395, + 0, 0, 0, 2394, 0, 0, 4420, 0, 0, 0, + 0, 0, 2423, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2412, 0, 0, + 0, 0, 0, 0, 0, 0, 2400, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2427, 2426, 2414, 0, 0, 0, 0, 234, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2398, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2408, 0, 0, 0, 0, - 0, 0, 0, 0, 2396, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1764, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2415, 0, 0, 169, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2391, 0, - 0, 0, 2390, 0, 0, 0, 0, 0, 0, 2398, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2404, 0, 0, 0, 0, 0, 2408, 0, 0, 0, - 0, 0, 0, 0, 0, 2396, 0, 0, 0, 0, - 2392, 2426, 0, 0, 2393, 2395, 2397, 0, 2399, 2400, - 2401, 2405, 2406, 2407, 2409, 2412, 2413, 2414, 0, 0, - 0, 0, 0, 0, 0, 2402, 2411, 2403, 0, 0, - 0, 0, 0, 0, 3972, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 197, 240, 997, 0, 0, 0, 0, 0, 0, 0, - 0, 485, 0, 0, 626, 660, 649, 736, 614, 2418, - 0, 0, 0, 0, 0, 948, 0, 0, 0, 386, - 169, 0, 449, 664, 645, 656, 646, 491, 631, 632, - 633, 640, 398, 634, 768, 636, 606, 637, 607, 638, - 639, 1568, 663, 613, 526, 467, 0, 680, 0, 0, - 1068, 1076, 0, 0, 0, 0, 0, 0, 0, 0, - 1064, 0, 0, 0, 0, 940, 4485, 0, 977, 1045, - 1044, 964, 974, 0, 2415, 357, 263, 608, 732, 610, - 609, 965, 0, 966, 970, 973, 969, 967, 968, 0, - 1059, 0, 2391, 0, 0, 0, 2390, 932, 944, 0, - 949, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2408, 0, 0, 0, 0, 0, 941, 942, 0, 2396, - 0, 0, 998, 0, 943, 0, 0, 0, 0, 0, - 527, 555, 0, 568, 0, 423, 424, 993, 971, 975, - 0, 0, 0, 0, 342, 534, 552, 358, 521, 566, - 363, 529, 545, 353, 484, 518, 0, 0, 344, 550, - 528, 463, 343, 4586, 511, 383, 400, 380, 482, 972, - 0, 996, 1000, 379, 1082, 994, 560, 346, 0, 559, - 481, 455, 546, 551, 464, 457, 0, 345, 548, 462, - 456, 435, 390, 1083, 436, 437, 438, 439, 440, 441, - 404, 497, 453, 498, 405, 469, 468, 470, 406, 407, + 0, 4054, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1230, 0, 169, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 897, 2387, 0, 0, + 0, 0, 169, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 4525, 0, 1768, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2381, 3483, 2380, 0, 0, 0, 3482, 2414, 0, 0, + 2428, 2402, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2408, 0, 0, 0, 0, 0, 0, 0, + 169, 4546, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2396, 2430, 0, 0, 2397, 2399, 2401, 0, + 2403, 2404, 2405, 2409, 2410, 2411, 2413, 2416, 2417, 2418, + 0, 0, 0, 0, 0, 0, 0, 2406, 2415, 2407, + 0, 0, 4524, 0, 0, 0, 0, 0, 0, 2385, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1411, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2402, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2408, 0, 0, + 0, 2422, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3980, 0, 0, 2396, 2430, 0, + 0, 2397, 2399, 2401, 0, 2403, 2404, 2405, 2409, 2410, + 2411, 2413, 2416, 2417, 2418, 0, 0, 0, 0, 0, + 0, 0, 2406, 2415, 2407, 0, 0, 0, 0, 0, + 0, 0, 2378, 2379, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2419, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2395, 169, + 0, 0, 2394, 0, 0, 0, 2422, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 2412, 0, 0, 0, + 0, 0, 0, 1411, 0, 2400, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2427, + 2426, 0, 4521, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2419, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2395, 0, 0, 0, 2394, 0, 0, + 0, 0, 0, 0, 0, 0, 2387, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2412, 0, 0, 0, 0, 0, 0, 0, 0, + 2400, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2428, + 0, 0, 0, 4526, 4527, 3980, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 169, 169, 0, 0, 0, + 4522, 4523, 0, 4530, 4529, 4528, 4544, 4545, 4547, 4531, + 4532, 4535, 4537, 4536, 4533, 4534, 4538, 4540, 4541, 4542, + 4539, 0, 4543, 0, 0, 0, 0, 4548, 0, 0, + 0, 0, 0, 0, 4550, 0, 0, 0, 4549, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1768, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 197, 240, + 1000, 0, 0, 0, 0, 0, 0, 0, 0, 488, + 0, 0, 629, 663, 652, 739, 617, 0, 0, 0, + 0, 0, 0, 951, 0, 0, 0, 386, 0, 4495, + 452, 667, 648, 659, 649, 494, 634, 635, 636, 643, + 398, 637, 771, 639, 609, 640, 610, 641, 642, 1572, + 666, 616, 529, 470, 0, 683, 0, 0, 1071, 1079, + 0, 0, 0, 0, 0, 0, 0, 0, 1067, 0, + 0, 0, 0, 943, 0, 0, 980, 1048, 1047, 967, + 977, 0, 1768, 357, 263, 611, 735, 613, 612, 968, + 0, 969, 973, 976, 972, 970, 971, 169, 1062, 0, + 0, 0, 0, 0, 0, 935, 947, 0, 952, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 944, 945, 0, 0, 4603, 0, + 1001, 0, 946, 0, 0, 0, 0, 0, 530, 558, + 0, 571, 0, 424, 425, 996, 974, 978, 0, 0, + 0, 0, 342, 537, 555, 358, 524, 569, 363, 532, + 548, 353, 487, 521, 0, 0, 344, 553, 531, 466, + 343, 0, 514, 383, 400, 380, 485, 975, 3980, 999, + 1003, 379, 1085, 997, 563, 346, 0, 562, 484, 458, + 549, 554, 467, 460, 0, 345, 551, 465, 459, 436, + 390, 1086, 437, 438, 439, 440, 441, 442, 404, 500, + 456, 501, 405, 472, 471, 473, 406, 407, 408, 409, + 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, + 0, 593, 594, 0, 169, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 727, 994, + 1236, 732, 0, 565, 0, 0, 1069, 0, 0, 0, + 535, 0, 0, 443, 0, 0, 0, 998, 0, 517, + 490, 1082, 0, 0, 515, 454, 550, 502, 556, 538, + 564, 509, 503, 336, 539, 382, 468, 354, 356, 760, + 384, 387, 391, 392, 478, 479, 496, 523, 542, 543, + 544, 381, 368, 516, 369, 401, 370, 337, 373, 372, + 374, 525, 375, 339, 497, 547, 0, 397, 512, 463, + 340, 462, 498, 546, 545, 355, 573, 580, 581, 671, + 0, 586, 774, 775, 776, 595, 0, 504, 351, 348, + 0, 0, 0, 377, 499, 364, 366, 367, 365, 493, + 495, 600, 601, 602, 604, 0, 605, 606, 0, 0, + 0, 0, 607, 672, 688, 656, 625, 588, 680, 622, + 626, 627, 418, 419, 420, 421, 691, 0, 0, 0, + 579, 444, 445, 0, 389, 388, 464, 341, 0, 0, + 433, 417, 505, 347, 385, 435, 423, 446, 447, 448, + 449, 450, 0, 451, 395, 331, 332, 768, 1066, 486, + 693, 730, 731, 618, 0, 1081, 1061, 1063, 1064, 1068, + 1072, 1073, 1074, 1075, 1076, 1078, 1080, 1084, 767, 0, + 673, 687, 772, 686, 764, 492, 0, 522, 684, 631, + 0, 677, 650, 651, 0, 678, 646, 682, 0, 620, + 0, 589, 592, 621, 706, 707, 708, 338, 591, 710, + 711, 712, 713, 714, 715, 716, 709, 1083, 654, 630, + 657, 570, 633, 632, 0, 0, 668, 1002, 669, 670, + 474, 475, 476, 477, 1070, 694, 362, 590, 507, 508, + 169, 655, 0, 0, 0, 0, 0, 0, 0, 0, + 660, 661, 658, 777, 0, 717, 718, 0, 0, 584, + 585, 394, 0, 603, 402, 361, 491, 396, 568, 426, + 0, 596, 662, 597, 510, 511, 721, 726, 722, 723, + 725, 746, 481, 416, 422, 526, 434, 455, 513, 567, + 489, 518, 359, 557, 528, 461, 647, 675, 1092, 1065, + 1091, 1093, 1094, 1090, 1095, 1096, 1077, 956, 0, 1009, + 1010, 1088, 1087, 1089, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 702, 701, 700, 699, 698, + 697, 696, 695, 0, 0, 644, 376, 765, 759, 766, + 747, 750, 749, 724, 963, 333, 624, 453, 506, 393, + 689, 690, 0, 745, 1055, 1018, 1019, 1020, 953, 1021, + 1015, 1016, 954, 1017, 1056, 1007, 1052, 1053, 982, 1012, + 1022, 1051, 1023, 1054, 983, 1057, 1097, 1098, 1029, 1013, + 296, 1099, 1026, 1058, 1050, 1049, 1024, 1008, 1059, 1060, + 990, 985, 1027, 1028, 1014, 1035, 1036, 1037, 1040, 955, + 1041, 1042, 1043, 1044, 1045, 1039, 1038, 1004, 1005, 1006, + 1030, 1031, 1033, 1034, 1011, 536, 986, 987, 988, 989, + 0, 0, 574, 575, 576, 599, 0, 577, 559, 623, + 403, 334, 540, 566, 762, 763, 0, 0, 0, 0, + 0, 0, 0, 674, 685, 719, 720, 0, 733, 734, + 736, 738, 1046, 740, 533, 534, 748, 0, 0, 427, + 349, 350, 482, 483, 520, 0, 428, 429, 430, 432, + 431, 1032, 743, 744, 741, 457, 519, 541, 527, 761, + 469, 0, 769, 614, 615, 770, 729, 728, 335, 1000, + 948, 0, 0, 0, 0, 0, 0, 0, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 951, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 991, 666, + 616, 529, 470, 0, 683, 0, 0, 1071, 1079, 0, + 0, 0, 0, 0, 0, 0, 0, 1067, 0, 0, + 0, 0, 943, 0, 0, 980, 1048, 1047, 967, 977, + 0, 0, 357, 263, 611, 735, 613, 612, 968, 0, + 969, 973, 976, 972, 970, 971, 0, 1062, 0, 0, + 0, 0, 0, 0, 935, 947, 0, 952, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 944, 945, 0, 0, 0, 0, 1001, + 0, 946, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 996, 974, 978, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 975, 0, 999, 1003, + 379, 1085, 997, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 1086, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 994, 0, + 732, 0, 565, 0, 0, 1069, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 998, 0, 517, 490, + 1082, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 2175, 2174, 2176, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 1066, 486, 693, + 730, 731, 618, 0, 1081, 1061, 1063, 1064, 1068, 1072, + 1073, 1074, 1075, 1076, 1078, 1080, 1084, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 1083, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 1002, 669, 670, 474, + 475, 476, 477, 1070, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 1092, 1065, 1091, + 1093, 1094, 1090, 1095, 1096, 1077, 956, 0, 1009, 1010, + 1088, 1087, 1089, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 963, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 1055, 1018, 1019, 1020, 953, 1021, 1015, + 1016, 954, 1017, 1056, 1007, 1052, 1053, 982, 1012, 1022, + 1051, 1023, 1054, 983, 1057, 1097, 1098, 1029, 1013, 296, + 1099, 1026, 1058, 1050, 1049, 1024, 1008, 1059, 1060, 990, + 985, 1027, 1028, 1014, 1035, 1036, 1037, 1040, 955, 1041, + 1042, 1043, 1044, 1045, 1039, 1038, 1004, 1005, 1006, 1030, + 1031, 1033, 1034, 1011, 536, 986, 987, 988, 989, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 1046, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 1032, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 0, 948, + 197, 240, 1000, 0, 0, 0, 0, 0, 0, 0, + 0, 488, 0, 0, 629, 663, 652, 739, 617, 0, + 0, 0, 0, 0, 0, 951, 0, 0, 0, 386, + 0, 0, 452, 667, 648, 659, 649, 494, 634, 635, + 636, 643, 398, 637, 771, 639, 609, 640, 610, 641, + 642, 991, 666, 616, 529, 470, 0, 683, 0, 0, + 1071, 1079, 0, 0, 0, 0, 0, 0, 0, 0, + 1067, 0, 0, 0, 0, 943, 0, 0, 980, 1048, + 1047, 967, 977, 0, 0, 357, 263, 611, 735, 613, + 612, 968, 0, 969, 973, 976, 972, 970, 971, 0, + 1062, 0, 0, 0, 0, 0, 0, 935, 947, 0, + 952, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 944, 945, 0, 0, + 0, 0, 1001, 0, 946, 0, 0, 0, 0, 0, + 530, 558, 0, 571, 0, 424, 425, 996, 974, 978, + 0, 0, 0, 0, 342, 537, 555, 358, 524, 569, + 363, 532, 548, 353, 487, 521, 0, 0, 344, 553, + 531, 466, 343, 0, 514, 383, 400, 380, 485, 975, + 0, 999, 1003, 379, 1085, 997, 563, 346, 0, 562, + 484, 458, 549, 554, 467, 460, 0, 345, 551, 465, + 459, 436, 390, 1086, 437, 438, 439, 440, 441, 442, + 404, 500, 456, 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, - 0, 0, 0, 590, 591, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 724, 991, 1232, 729, 0, 562, 0, 0, 1066, 0, - 0, 0, 532, 0, 0, 442, 0, 0, 0, 995, - 0, 514, 487, 1079, 169, 0, 512, 451, 547, 499, - 553, 535, 561, 506, 500, 336, 536, 382, 465, 354, - 356, 757, 384, 387, 391, 392, 475, 476, 493, 520, - 539, 540, 541, 381, 368, 513, 369, 401, 370, 337, - 373, 372, 374, 522, 375, 339, 494, 544, 0, 397, - 509, 460, 340, 459, 495, 543, 542, 355, 570, 577, - 578, 668, 0, 583, 771, 772, 773, 592, 0, 501, - 351, 348, 0, 0, 0, 377, 496, 364, 366, 367, - 365, 490, 492, 597, 598, 599, 601, 0, 602, 603, - 0, 0, 0, 0, 604, 669, 685, 653, 622, 585, - 677, 619, 623, 624, 418, 419, 420, 688, 0, 0, - 0, 576, 443, 444, 0, 389, 388, 461, 341, 0, - 0, 432, 417, 502, 347, 385, 434, 422, 445, 446, - 447, 448, 395, 331, 332, 765, 1063, 483, 690, 727, - 728, 615, 0, 1078, 1058, 1060, 1061, 1065, 1069, 1070, - 1071, 1072, 1073, 1075, 1077, 1081, 764, 0, 670, 684, - 769, 683, 761, 489, 0, 519, 681, 628, 0, 674, - 647, 648, 0, 675, 643, 679, 0, 617, 0, 586, - 589, 618, 703, 704, 705, 338, 588, 707, 708, 709, - 710, 711, 712, 713, 706, 1080, 651, 627, 654, 567, - 630, 629, 0, 0, 665, 999, 666, 667, 471, 472, - 473, 474, 1067, 691, 362, 587, 504, 505, 0, 652, - 0, 0, 0, 0, 0, 0, 0, 0, 657, 658, - 655, 774, 0, 714, 715, 0, 0, 581, 582, 394, - 0, 600, 402, 361, 488, 396, 565, 425, 0, 593, - 659, 594, 507, 508, 718, 723, 719, 720, 722, 743, - 478, 416, 421, 523, 433, 452, 510, 564, 486, 515, - 359, 554, 525, 458, 644, 672, 1089, 1062, 1088, 1090, - 1091, 1087, 1092, 1093, 1074, 953, 0, 1006, 1007, 1085, - 1084, 1086, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 699, 698, 697, 696, 695, 694, 693, - 692, 0, 0, 641, 376, 762, 756, 763, 744, 747, - 746, 721, 960, 333, 621, 450, 503, 393, 686, 687, - 0, 742, 1052, 1015, 1016, 1017, 950, 1018, 1012, 1013, - 951, 1014, 1053, 1004, 1049, 1050, 979, 1009, 1019, 1048, - 1020, 1051, 980, 1054, 1094, 1095, 1026, 1010, 296, 1096, - 1023, 1055, 1047, 1046, 1021, 1005, 1056, 1057, 987, 982, - 1024, 1025, 1011, 1032, 1033, 1034, 1037, 952, 1038, 1039, - 1040, 1041, 1042, 1036, 1035, 1001, 1002, 1003, 1027, 1028, - 1030, 1031, 1008, 533, 983, 984, 985, 986, 0, 0, - 571, 572, 573, 596, 0, 574, 556, 620, 403, 334, - 537, 563, 759, 760, 0, 0, 0, 0, 0, 0, - 0, 671, 682, 716, 717, 0, 730, 731, 733, 735, - 1043, 737, 530, 531, 745, 0, 0, 426, 349, 350, - 479, 480, 517, 0, 427, 428, 429, 431, 430, 1029, - 740, 741, 738, 454, 516, 538, 524, 758, 466, 0, - 766, 611, 612, 767, 726, 725, 335, 997, 945, 0, - 0, 0, 0, 0, 0, 0, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 948, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 988, 663, 613, 526, - 467, 0, 680, 0, 0, 1068, 1076, 0, 0, 0, - 0, 0, 0, 0, 0, 1064, 0, 0, 0, 0, - 940, 0, 0, 977, 1045, 1044, 964, 974, 0, 0, - 357, 263, 608, 732, 610, 609, 965, 0, 966, 970, - 973, 969, 967, 968, 0, 1059, 0, 0, 0, 0, - 0, 0, 932, 944, 0, 949, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 941, 942, 0, 0, 0, 0, 998, 0, 943, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 993, 971, 975, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 972, 0, 996, 1000, 379, 1082, - 994, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 1083, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 991, 0, 729, 0, - 562, 0, 0, 1066, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 995, 0, 514, 487, 1079, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 2171, 2170, 2172, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 1063, 483, 690, 727, 728, 615, 0, 1078, 1058, - 1060, 1061, 1065, 1069, 1070, 1071, 1072, 1073, 1075, 1077, - 1081, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 1080, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 999, 666, 667, 471, 472, 473, 474, 1067, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 1089, 1062, 1088, 1090, 1091, 1087, 1092, 1093, 1074, - 953, 0, 1006, 1007, 1085, 1084, 1086, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 960, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 1052, 1015, 1016, - 1017, 950, 1018, 1012, 1013, 951, 1014, 1053, 1004, 1049, - 1050, 979, 1009, 1019, 1048, 1020, 1051, 980, 1054, 1094, - 1095, 1026, 1010, 296, 1096, 1023, 1055, 1047, 1046, 1021, - 1005, 1056, 1057, 987, 982, 1024, 1025, 1011, 1032, 1033, - 1034, 1037, 952, 1038, 1039, 1040, 1041, 1042, 1036, 1035, - 1001, 1002, 1003, 1027, 1028, 1030, 1031, 1008, 533, 983, - 984, 985, 986, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 1043, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 1029, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 0, 945, 197, 240, 997, 0, 0, 0, - 0, 0, 0, 0, 0, 485, 0, 0, 626, 660, - 649, 736, 614, 0, 0, 0, 0, 0, 0, 948, - 0, 0, 0, 386, 0, 0, 449, 664, 645, 656, - 646, 491, 631, 632, 633, 640, 398, 634, 768, 636, - 606, 637, 607, 638, 639, 988, 663, 613, 526, 467, - 0, 680, 0, 0, 1068, 1076, 0, 0, 0, 0, - 0, 0, 0, 0, 1064, 0, 0, 0, 0, 940, - 0, 0, 977, 1045, 1044, 964, 974, 0, 0, 357, - 263, 608, 732, 610, 609, 965, 0, 966, 970, 973, - 969, 967, 968, 0, 1059, 0, 0, 0, 0, 0, - 0, 932, 944, 0, 949, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 941, 942, 0, 0, 0, 0, 998, 0, 943, 0, - 0, 0, 0, 0, 527, 555, 0, 568, 0, 423, - 424, 993, 971, 975, 0, 0, 0, 0, 342, 534, - 552, 358, 521, 566, 363, 529, 545, 353, 484, 518, - 0, 0, 344, 550, 528, 463, 343, 0, 511, 383, - 400, 380, 482, 972, 0, 996, 1000, 379, 1082, 994, - 560, 346, 0, 559, 481, 455, 546, 551, 464, 457, - 0, 345, 548, 462, 456, 435, 390, 1083, 436, 437, - 438, 439, 440, 441, 404, 497, 453, 498, 405, 469, - 468, 470, 406, 407, 408, 409, 410, 411, 412, 413, - 414, 415, 0, 0, 0, 0, 0, 590, 591, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 724, 991, 0, 729, 0, 562, - 0, 0, 1066, 0, 0, 0, 532, 0, 0, 442, - 0, 0, 0, 995, 0, 514, 487, 1079, 0, 0, - 512, 451, 547, 499, 553, 535, 561, 506, 500, 336, - 536, 382, 465, 354, 356, 757, 384, 387, 391, 392, - 475, 476, 493, 520, 539, 540, 541, 381, 368, 513, - 369, 401, 370, 337, 373, 372, 374, 522, 375, 339, - 494, 544, 0, 397, 509, 460, 340, 459, 495, 543, - 542, 355, 570, 577, 578, 668, 0, 583, 771, 772, - 773, 592, 0, 501, 351, 348, 0, 0, 0, 377, - 496, 364, 366, 367, 365, 490, 492, 597, 598, 599, - 601, 0, 602, 603, 0, 0, 0, 0, 604, 669, - 685, 653, 622, 585, 677, 619, 623, 624, 418, 419, - 420, 688, 0, 0, 0, 576, 443, 444, 0, 389, - 388, 461, 341, 0, 0, 432, 417, 502, 347, 385, - 434, 422, 445, 446, 447, 448, 395, 331, 332, 765, - 1063, 483, 690, 727, 728, 615, 0, 1078, 1058, 1060, - 1061, 1065, 1069, 1070, 1071, 1072, 1073, 1075, 1077, 1081, - 764, 0, 670, 684, 769, 683, 761, 489, 0, 519, - 681, 628, 0, 674, 647, 648, 0, 675, 643, 679, - 0, 617, 0, 586, 589, 618, 703, 704, 705, 338, - 588, 707, 708, 709, 710, 711, 712, 713, 706, 1080, - 651, 627, 654, 567, 630, 629, 0, 0, 665, 999, - 666, 667, 471, 472, 473, 474, 1067, 691, 362, 587, - 504, 505, 0, 652, 0, 0, 0, 0, 0, 0, - 0, 0, 657, 658, 655, 774, 0, 714, 715, 0, - 0, 581, 582, 394, 0, 600, 402, 361, 488, 396, - 565, 425, 0, 593, 659, 594, 507, 508, 718, 723, - 719, 720, 722, 743, 478, 416, 421, 523, 433, 452, - 510, 564, 486, 515, 359, 554, 525, 458, 644, 672, - 1089, 1062, 1088, 1090, 1091, 1087, 1092, 1093, 1074, 953, - 0, 1006, 1007, 1085, 1084, 1086, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 699, 698, 697, - 696, 695, 694, 693, 692, 0, 0, 641, 376, 762, - 756, 763, 744, 747, 746, 721, 960, 333, 621, 450, - 503, 393, 686, 687, 0, 742, 1052, 1015, 1016, 1017, - 950, 1018, 1012, 1013, 951, 1014, 1053, 1004, 1049, 1050, - 979, 1009, 1019, 1048, 1020, 1051, 980, 1054, 1094, 1095, - 1026, 1010, 296, 1096, 1023, 1055, 1047, 1046, 1021, 1005, - 1056, 1057, 987, 982, 1024, 1025, 1011, 1032, 1033, 1034, - 1037, 952, 1038, 1039, 1040, 1041, 1042, 1036, 1035, 1001, - 1002, 1003, 1027, 1028, 1030, 1031, 1008, 533, 983, 984, - 985, 986, 0, 0, 571, 572, 573, 596, 0, 574, - 556, 620, 403, 334, 537, 563, 759, 760, 0, 0, - 0, 0, 0, 0, 0, 671, 682, 716, 717, 0, - 730, 731, 733, 735, 1043, 737, 530, 531, 745, 0, - 0, 426, 349, 350, 479, 480, 517, 0, 427, 428, - 429, 431, 430, 1029, 740, 741, 738, 454, 516, 538, - 524, 758, 466, 0, 766, 611, 612, 767, 726, 725, - 335, 997, 945, 0, 0, 0, 0, 0, 0, 0, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 948, 0, 0, 0, 386, 2240, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 988, 663, 613, 526, 467, 0, 680, 0, 0, 1068, - 1076, 0, 0, 0, 0, 0, 0, 0, 0, 1064, - 0, 2472, 0, 0, 940, 0, 0, 977, 1045, 1044, - 964, 974, 0, 0, 357, 263, 608, 732, 610, 609, - 965, 0, 966, 970, 973, 969, 967, 968, 0, 1059, - 0, 0, 0, 0, 0, 0, 932, 944, 0, 949, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 941, 942, 0, 0, 0, - 0, 998, 0, 943, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 2473, 971, 975, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 972, 0, - 996, 1000, 379, 1082, 994, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 1083, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 991, 0, 729, 0, 562, 0, 0, 1066, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 995, 0, - 514, 487, 1079, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 1063, 483, 690, 727, 728, - 615, 0, 1078, 1058, 1060, 1061, 1065, 1069, 1070, 1071, - 1072, 1073, 1075, 1077, 1081, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 1080, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 999, 666, 667, 471, 472, 473, - 474, 1067, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 1089, 1062, 1088, 1090, 1091, - 1087, 1092, 1093, 1074, 953, 0, 1006, 1007, 1085, 1084, - 1086, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 960, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 1052, 1015, 1016, 1017, 950, 1018, 1012, 1013, 951, - 1014, 1053, 1004, 1049, 1050, 979, 1009, 1019, 1048, 1020, - 1051, 980, 1054, 1094, 1095, 1026, 1010, 296, 1096, 1023, - 1055, 1047, 1046, 1021, 1005, 1056, 1057, 987, 982, 1024, - 1025, 1011, 1032, 1033, 1034, 1037, 952, 1038, 1039, 1040, - 1041, 1042, 1036, 1035, 1001, 1002, 1003, 1027, 1028, 1030, - 1031, 1008, 533, 983, 984, 985, 986, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 1043, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 1029, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 997, 945, 0, 0, - 0, 0, 0, 0, 0, 485, 0, 0, 626, 660, - 649, 736, 614, 0, 0, 0, 0, 0, 0, 948, - 0, 0, 0, 386, 5033, 0, 449, 664, 645, 656, - 646, 491, 631, 632, 633, 640, 398, 634, 768, 636, - 606, 637, 607, 638, 639, 988, 663, 613, 526, 467, - 0, 680, 0, 0, 1068, 1076, 0, 0, 0, 0, - 0, 0, 0, 0, 1064, 0, 0, 0, 0, 940, - 0, 0, 977, 1045, 1044, 964, 974, 0, 0, 357, - 263, 608, 732, 610, 609, 965, 0, 966, 970, 973, - 969, 967, 968, 0, 1059, 0, 0, 0, 0, 0, - 0, 932, 944, 0, 949, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 941, 942, 0, 0, 0, 0, 998, 0, 943, 0, - 0, 0, 0, 0, 527, 555, 0, 568, 0, 423, - 424, 993, 971, 975, 0, 0, 0, 0, 342, 534, - 552, 358, 521, 566, 363, 529, 545, 353, 484, 518, - 0, 0, 344, 550, 528, 463, 343, 0, 511, 383, - 400, 380, 482, 972, 0, 996, 1000, 379, 1082, 994, - 560, 346, 0, 559, 481, 455, 546, 551, 464, 457, - 0, 345, 548, 462, 456, 435, 390, 1083, 436, 437, - 438, 439, 440, 441, 404, 497, 453, 498, 405, 469, - 468, 470, 406, 407, 408, 409, 410, 411, 412, 413, - 414, 415, 0, 0, 0, 0, 0, 590, 591, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 724, 991, 0, 729, 0, 562, - 0, 0, 1066, 0, 0, 0, 532, 0, 0, 442, - 0, 0, 0, 995, 0, 514, 487, 1079, 0, 0, - 512, 451, 547, 499, 553, 535, 561, 506, 500, 336, - 536, 382, 465, 354, 356, 757, 384, 387, 391, 392, - 475, 476, 493, 520, 539, 540, 541, 381, 368, 513, - 369, 401, 370, 337, 373, 372, 374, 522, 375, 339, - 494, 544, 0, 397, 509, 460, 340, 459, 495, 543, - 542, 355, 570, 577, 578, 668, 0, 583, 771, 772, - 773, 592, 0, 501, 351, 348, 0, 0, 0, 377, - 496, 364, 366, 367, 365, 490, 492, 597, 598, 599, - 601, 0, 602, 603, 0, 0, 0, 0, 604, 669, - 685, 653, 622, 585, 677, 619, 623, 624, 418, 419, - 420, 688, 0, 0, 0, 576, 443, 444, 0, 389, - 388, 461, 341, 0, 0, 432, 417, 502, 347, 385, - 434, 422, 445, 446, 447, 448, 395, 331, 332, 765, - 1063, 483, 690, 727, 728, 615, 0, 1078, 1058, 1060, - 1061, 1065, 1069, 1070, 1071, 1072, 1073, 1075, 1077, 1081, - 764, 0, 670, 684, 769, 683, 761, 489, 0, 519, - 681, 628, 0, 674, 647, 648, 0, 675, 643, 679, - 0, 617, 0, 586, 589, 618, 703, 704, 705, 338, - 588, 707, 708, 709, 710, 711, 712, 713, 706, 1080, - 651, 627, 654, 567, 630, 629, 0, 0, 665, 999, - 666, 667, 471, 472, 473, 474, 1067, 691, 362, 587, - 504, 505, 0, 652, 0, 0, 0, 0, 0, 0, - 0, 0, 657, 658, 655, 774, 0, 714, 715, 0, - 0, 581, 582, 394, 0, 600, 402, 361, 488, 396, - 565, 425, 0, 593, 659, 594, 507, 508, 718, 723, - 719, 720, 722, 743, 478, 416, 421, 523, 433, 452, - 510, 564, 486, 515, 359, 554, 525, 458, 644, 672, - 1089, 1062, 1088, 1090, 1091, 1087, 1092, 1093, 1074, 953, - 0, 1006, 1007, 1085, 1084, 1086, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 699, 698, 697, - 696, 695, 694, 693, 692, 0, 0, 641, 376, 762, - 756, 763, 744, 747, 746, 721, 960, 333, 621, 450, - 503, 393, 686, 687, 0, 742, 1052, 1015, 1016, 1017, - 950, 1018, 1012, 1013, 951, 1014, 1053, 1004, 1049, 1050, - 979, 1009, 1019, 1048, 1020, 1051, 980, 1054, 1094, 1095, - 1026, 1010, 296, 1096, 1023, 1055, 1047, 1046, 1021, 1005, - 1056, 1057, 987, 982, 1024, 1025, 1011, 1032, 1033, 1034, - 1037, 952, 1038, 1039, 1040, 1041, 1042, 1036, 1035, 1001, - 1002, 1003, 1027, 1028, 1030, 1031, 1008, 533, 983, 984, - 985, 986, 0, 0, 571, 572, 573, 596, 0, 574, - 556, 620, 403, 334, 537, 563, 759, 760, 0, 0, - 0, 0, 0, 0, 0, 671, 682, 716, 717, 0, - 730, 731, 733, 735, 1043, 737, 530, 531, 745, 0, - 0, 426, 349, 350, 479, 480, 517, 0, 427, 428, - 429, 431, 430, 1029, 740, 741, 738, 454, 516, 538, - 524, 758, 466, 0, 766, 611, 612, 767, 726, 725, - 335, 997, 945, 0, 0, 0, 0, 0, 0, 0, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 948, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 988, 663, 613, 526, 467, 0, 680, 0, 0, 1068, - 1076, 0, 0, 0, 0, 0, 0, 0, 0, 1064, - 0, 0, 0, 0, 940, 0, 0, 977, 1045, 1044, - 964, 974, 0, 0, 357, 263, 608, 732, 610, 609, - 965, 0, 966, 970, 973, 969, 967, 968, 0, 1059, - 0, 0, 0, 0, 0, 0, 932, 944, 0, 949, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 941, 942, 0, 0, 0, - 0, 998, 0, 943, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 993, 971, 975, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 972, 0, - 996, 1000, 379, 1082, 994, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 1083, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 991, 0, 729, 0, 562, 0, 0, 1066, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 995, 0, - 514, 487, 1079, 4894, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 1063, 483, 690, 727, 728, - 615, 0, 1078, 1058, 1060, 1061, 1065, 1069, 1070, 1071, - 1072, 1073, 1075, 1077, 1081, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 1080, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 999, 666, 667, 471, 472, 473, - 474, 1067, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 1089, 1062, 1088, 1090, 1091, - 1087, 1092, 1093, 1074, 953, 0, 1006, 1007, 1085, 1084, - 1086, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 960, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 1052, 1015, 1016, 1017, 950, 1018, 1012, 1013, 951, - 1014, 1053, 1004, 1049, 1050, 979, 1009, 1019, 1048, 1020, - 1051, 980, 1054, 1094, 1095, 1026, 1010, 296, 1096, 1023, - 1055, 1047, 1046, 1021, 1005, 1056, 1057, 987, 982, 1024, - 1025, 1011, 1032, 1033, 1034, 1037, 952, 1038, 1039, 1040, - 1041, 1042, 1036, 1035, 1001, 1002, 1003, 1027, 1028, 1030, - 1031, 1008, 533, 983, 984, 985, 986, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 1043, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 1029, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 997, 945, 0, 0, - 0, 0, 0, 0, 0, 485, 0, 0, 626, 660, - 649, 736, 614, 0, 0, 0, 0, 0, 0, 948, - 0, 0, 0, 386, 0, 0, 449, 664, 645, 656, - 646, 491, 631, 632, 633, 640, 398, 634, 768, 636, - 606, 637, 607, 638, 639, 988, 663, 613, 526, 467, - 0, 680, 0, 0, 1068, 1076, 0, 0, 0, 0, - 0, 0, 0, 0, 1064, 0, 0, 0, 0, 940, - 0, 0, 977, 1045, 1044, 964, 974, 0, 0, 357, - 263, 608, 732, 610, 609, 965, 0, 966, 970, 973, - 969, 967, 968, 0, 1059, 0, 0, 0, 0, 0, - 0, 932, 944, 0, 949, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 941, 942, 1860, 0, 0, 0, 998, 0, 943, 0, - 0, 0, 0, 0, 527, 555, 0, 568, 0, 423, - 424, 993, 971, 975, 0, 0, 0, 0, 342, 534, - 552, 358, 521, 566, 363, 529, 545, 353, 484, 518, - 0, 0, 344, 550, 528, 463, 343, 0, 511, 383, - 400, 380, 482, 972, 0, 996, 1000, 379, 1082, 994, - 560, 346, 0, 559, 481, 455, 546, 551, 464, 457, - 0, 345, 548, 462, 456, 435, 390, 1083, 436, 437, - 438, 439, 440, 441, 404, 497, 453, 498, 405, 469, - 468, 470, 406, 407, 408, 409, 410, 411, 412, 413, - 414, 415, 0, 0, 0, 0, 0, 590, 591, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 724, 991, 0, 729, 0, 562, - 0, 0, 1066, 0, 0, 0, 532, 0, 0, 442, - 0, 0, 0, 995, 0, 514, 487, 1079, 0, 0, - 512, 451, 547, 499, 553, 535, 561, 506, 500, 336, - 536, 382, 465, 354, 356, 757, 384, 387, 391, 392, - 475, 476, 493, 520, 539, 540, 541, 381, 368, 513, - 369, 401, 370, 337, 373, 372, 374, 522, 375, 339, - 494, 544, 0, 397, 509, 460, 340, 459, 495, 543, - 542, 355, 570, 577, 578, 668, 0, 583, 771, 772, - 773, 592, 0, 501, 351, 348, 0, 0, 0, 377, - 496, 364, 366, 367, 365, 490, 492, 597, 598, 599, - 601, 0, 602, 603, 0, 0, 0, 0, 604, 669, - 685, 653, 622, 585, 677, 619, 623, 624, 418, 419, - 420, 688, 0, 0, 0, 576, 443, 444, 0, 389, - 388, 461, 341, 0, 0, 432, 417, 502, 347, 385, - 434, 422, 445, 446, 447, 448, 395, 331, 332, 765, - 1063, 483, 690, 727, 728, 615, 0, 1078, 1058, 1060, - 1061, 1065, 1069, 1070, 1071, 1072, 1073, 1075, 1077, 1081, - 764, 0, 670, 684, 769, 683, 761, 489, 0, 519, - 681, 628, 0, 674, 647, 648, 0, 675, 643, 679, - 0, 617, 0, 586, 589, 618, 703, 704, 705, 338, - 588, 707, 708, 709, 710, 711, 712, 713, 706, 1080, - 651, 627, 654, 567, 630, 629, 0, 0, 665, 999, - 666, 667, 471, 472, 473, 474, 1067, 691, 362, 587, - 504, 505, 0, 652, 0, 0, 0, 0, 0, 0, - 0, 0, 657, 658, 655, 774, 0, 714, 715, 0, - 0, 581, 582, 394, 0, 600, 402, 361, 488, 396, - 565, 425, 0, 593, 659, 594, 507, 508, 718, 723, - 719, 720, 722, 743, 478, 416, 421, 523, 433, 452, - 510, 564, 486, 515, 359, 554, 525, 458, 644, 672, - 1089, 1062, 1088, 1090, 1091, 1087, 1092, 1093, 1074, 953, - 0, 1006, 1007, 1085, 1084, 1086, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 699, 698, 697, - 696, 695, 694, 693, 692, 0, 0, 641, 376, 762, - 756, 763, 744, 747, 746, 721, 960, 333, 621, 450, - 503, 393, 686, 687, 0, 742, 1052, 1015, 1016, 1017, - 950, 1018, 1012, 1013, 951, 1014, 1053, 1004, 1049, 1050, - 979, 1009, 1019, 1048, 1020, 1051, 980, 1054, 1094, 1095, - 1026, 1010, 296, 1096, 1023, 1055, 1047, 1046, 1021, 1005, - 1056, 1057, 987, 982, 1024, 1025, 1011, 1032, 1033, 1034, - 1037, 952, 1038, 1039, 1040, 1041, 1042, 1036, 1035, 1001, - 1002, 1003, 1027, 1028, 1030, 1031, 1008, 533, 983, 984, - 985, 986, 0, 0, 571, 572, 573, 596, 0, 574, - 556, 620, 403, 334, 537, 563, 759, 760, 0, 0, - 0, 0, 0, 0, 0, 671, 682, 716, 717, 0, - 730, 731, 733, 735, 1043, 737, 530, 531, 745, 0, - 0, 426, 349, 350, 479, 480, 517, 0, 427, 428, - 429, 431, 430, 1029, 740, 741, 738, 454, 516, 538, - 524, 758, 466, 0, 766, 611, 612, 767, 726, 725, - 335, 997, 945, 0, 0, 0, 0, 0, 0, 0, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 948, 0, 0, 0, 386, 2240, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 988, 663, 613, 526, 467, 0, 680, 0, 0, 1068, - 1076, 0, 0, 0, 0, 0, 0, 0, 0, 1064, - 0, 0, 0, 0, 940, 0, 0, 977, 1045, 1044, - 964, 974, 0, 0, 357, 263, 608, 732, 610, 609, - 965, 0, 966, 970, 973, 969, 967, 968, 0, 1059, - 0, 0, 0, 0, 0, 0, 932, 944, 0, 949, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 941, 942, 0, 0, 0, - 0, 998, 0, 943, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 993, 971, 975, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 972, 0, - 996, 1000, 379, 1082, 994, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 1083, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, + 0, 0, 0, 593, 594, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 727, 994, 0, 732, 0, 565, 0, 0, 1069, 0, + 0, 0, 535, 0, 0, 443, 0, 0, 0, 998, + 0, 517, 490, 1082, 0, 0, 515, 454, 550, 502, + 556, 538, 564, 509, 503, 336, 539, 382, 468, 354, + 356, 760, 384, 387, 391, 392, 478, 479, 496, 523, + 542, 543, 544, 381, 368, 516, 369, 401, 370, 337, + 373, 372, 374, 525, 375, 339, 497, 547, 0, 397, + 512, 463, 340, 462, 498, 546, 545, 355, 573, 580, + 581, 671, 0, 586, 774, 775, 776, 595, 0, 504, + 351, 348, 0, 0, 0, 377, 499, 364, 366, 367, + 365, 493, 495, 600, 601, 602, 604, 0, 605, 606, + 0, 0, 0, 0, 607, 672, 688, 656, 625, 588, + 680, 622, 626, 627, 418, 419, 420, 421, 691, 0, + 0, 0, 579, 444, 445, 0, 389, 388, 464, 341, + 0, 0, 433, 417, 505, 347, 385, 435, 423, 446, + 447, 448, 449, 450, 0, 451, 395, 331, 332, 768, + 1066, 486, 693, 730, 731, 618, 0, 1081, 1061, 1063, + 1064, 1068, 1072, 1073, 1074, 1075, 1076, 1078, 1080, 1084, + 767, 0, 673, 687, 772, 686, 764, 492, 0, 522, + 684, 631, 0, 677, 650, 651, 0, 678, 646, 682, + 0, 620, 0, 589, 592, 621, 706, 707, 708, 338, + 591, 710, 711, 712, 713, 714, 715, 716, 709, 1083, + 654, 630, 657, 570, 633, 632, 0, 0, 668, 1002, + 669, 670, 474, 475, 476, 477, 1070, 694, 362, 590, + 507, 508, 0, 655, 0, 0, 0, 0, 0, 0, + 0, 0, 660, 661, 658, 777, 0, 717, 718, 0, + 0, 584, 585, 394, 0, 603, 402, 361, 491, 396, + 568, 426, 0, 596, 662, 597, 510, 511, 721, 726, + 722, 723, 725, 746, 481, 416, 422, 526, 434, 455, + 513, 567, 489, 518, 359, 557, 528, 461, 647, 675, + 1092, 1065, 1091, 1093, 1094, 1090, 1095, 1096, 1077, 956, + 0, 1009, 1010, 1088, 1087, 1089, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 702, 701, 700, + 699, 698, 697, 696, 695, 0, 0, 644, 376, 765, + 759, 766, 747, 750, 749, 724, 963, 333, 624, 453, + 506, 393, 689, 690, 0, 745, 1055, 1018, 1019, 1020, + 953, 1021, 1015, 1016, 954, 1017, 1056, 1007, 1052, 1053, + 982, 1012, 1022, 1051, 1023, 1054, 983, 1057, 1097, 1098, + 1029, 1013, 296, 1099, 1026, 1058, 1050, 1049, 1024, 1008, + 1059, 1060, 990, 985, 1027, 1028, 1014, 1035, 1036, 1037, + 1040, 955, 1041, 1042, 1043, 1044, 1045, 1039, 1038, 1004, + 1005, 1006, 1030, 1031, 1033, 1034, 1011, 536, 986, 987, + 988, 989, 0, 0, 574, 575, 576, 599, 0, 577, + 559, 623, 403, 334, 540, 566, 762, 763, 0, 0, + 0, 0, 0, 0, 0, 674, 685, 719, 720, 0, + 733, 734, 736, 738, 1046, 740, 533, 534, 748, 0, + 0, 427, 349, 350, 482, 483, 520, 0, 428, 429, + 430, 432, 431, 1032, 743, 744, 741, 457, 519, 541, + 527, 761, 469, 0, 769, 614, 615, 770, 729, 728, + 335, 1000, 948, 0, 0, 0, 0, 0, 0, 0, + 488, 0, 0, 629, 663, 652, 739, 617, 0, 0, + 0, 0, 0, 0, 951, 0, 0, 0, 386, 2244, + 0, 452, 667, 648, 659, 649, 494, 634, 635, 636, + 643, 398, 637, 771, 639, 609, 640, 610, 641, 642, + 991, 666, 616, 529, 470, 0, 683, 0, 0, 1071, + 1079, 0, 0, 0, 0, 0, 0, 0, 0, 1067, + 0, 2476, 0, 0, 943, 0, 0, 980, 1048, 1047, + 967, 977, 0, 0, 357, 263, 611, 735, 613, 612, + 968, 0, 969, 973, 976, 972, 970, 971, 0, 1062, + 0, 0, 0, 0, 0, 0, 935, 947, 0, 952, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 944, 945, 0, 0, 0, + 0, 1001, 0, 946, 0, 0, 0, 0, 0, 530, + 558, 0, 571, 0, 424, 425, 2477, 974, 978, 0, + 0, 0, 0, 342, 537, 555, 358, 524, 569, 363, + 532, 548, 353, 487, 521, 0, 0, 344, 553, 531, + 466, 343, 0, 514, 383, 400, 380, 485, 975, 0, + 999, 1003, 379, 1085, 997, 563, 346, 0, 562, 484, + 458, 549, 554, 467, 460, 0, 345, 551, 465, 459, + 436, 390, 1086, 437, 438, 439, 440, 441, 442, 404, + 500, 456, 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 991, 0, 729, 0, 562, 0, 0, 1066, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 995, 0, - 514, 487, 1079, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 1063, 483, 690, 727, 728, - 615, 0, 1078, 1058, 1060, 1061, 1065, 1069, 1070, 1071, - 1072, 1073, 1075, 1077, 1081, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 1080, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 999, 666, 667, 471, 472, 473, - 474, 1067, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 1089, 1062, 1088, 1090, 1091, - 1087, 1092, 1093, 1074, 953, 0, 1006, 1007, 1085, 1084, - 1086, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 960, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 1052, 1015, 1016, 1017, 950, 1018, 1012, 1013, 951, - 1014, 1053, 1004, 1049, 1050, 979, 1009, 1019, 1048, 1020, - 1051, 980, 1054, 1094, 1095, 1026, 1010, 296, 1096, 1023, - 1055, 1047, 1046, 1021, 1005, 1056, 1057, 987, 982, 1024, - 1025, 1011, 1032, 1033, 1034, 1037, 952, 1038, 1039, 1040, - 1041, 1042, 1036, 1035, 1001, 1002, 1003, 1027, 1028, 1030, - 1031, 1008, 533, 983, 984, 985, 986, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 1043, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 1029, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 997, 945, 0, 2705, - 0, 0, 0, 0, 0, 485, 0, 0, 626, 660, - 649, 736, 614, 0, 0, 0, 0, 0, 0, 948, - 0, 0, 0, 386, 0, 0, 449, 664, 645, 656, - 646, 491, 631, 632, 633, 640, 398, 634, 768, 636, - 606, 637, 607, 638, 639, 988, 663, 613, 526, 467, - 0, 680, 0, 0, 1068, 1076, 0, 0, 0, 0, - 0, 0, 0, 0, 1064, 0, 0, 0, 0, 940, - 0, 0, 977, 1045, 1044, 964, 974, 0, 0, 357, - 263, 608, 732, 610, 609, 965, 0, 966, 970, 973, - 969, 967, 968, 0, 1059, 0, 0, 0, 0, 0, - 0, 932, 944, 0, 949, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 941, 942, 0, 0, 0, 0, 998, 0, 943, 0, - 0, 0, 0, 0, 527, 555, 0, 568, 0, 423, - 424, 993, 971, 975, 0, 0, 0, 0, 342, 534, - 552, 358, 521, 566, 363, 529, 545, 353, 484, 518, - 0, 0, 344, 550, 528, 463, 343, 0, 511, 383, - 400, 380, 482, 972, 0, 996, 1000, 379, 1082, 994, - 560, 346, 0, 559, 481, 455, 546, 551, 464, 457, - 0, 345, 548, 462, 456, 435, 390, 1083, 436, 437, - 438, 439, 440, 441, 404, 497, 453, 498, 405, 469, - 468, 470, 406, 407, 408, 409, 410, 411, 412, 413, - 414, 415, 0, 0, 0, 0, 0, 590, 591, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 724, 991, 0, 729, 0, 562, - 0, 0, 1066, 0, 0, 0, 532, 0, 0, 442, - 0, 0, 0, 995, 0, 514, 487, 1079, 0, 0, - 512, 451, 547, 499, 553, 535, 561, 506, 500, 336, - 536, 382, 465, 354, 356, 757, 384, 387, 391, 392, - 475, 476, 493, 520, 539, 540, 541, 381, 368, 513, - 369, 401, 370, 337, 373, 372, 374, 522, 375, 339, - 494, 544, 0, 397, 509, 460, 340, 459, 495, 543, - 542, 355, 570, 577, 578, 668, 0, 583, 771, 772, - 773, 592, 0, 501, 351, 348, 0, 0, 0, 377, - 496, 364, 366, 367, 365, 490, 492, 597, 598, 599, - 601, 0, 602, 603, 0, 0, 0, 0, 604, 669, - 685, 653, 622, 585, 677, 619, 623, 624, 418, 419, - 420, 688, 0, 0, 0, 576, 443, 444, 0, 389, - 388, 461, 341, 0, 0, 432, 417, 502, 347, 385, - 434, 422, 445, 446, 447, 448, 395, 331, 332, 765, - 1063, 483, 690, 727, 728, 615, 0, 1078, 1058, 1060, - 1061, 1065, 1069, 1070, 1071, 1072, 1073, 1075, 1077, 1081, - 764, 0, 670, 684, 769, 683, 761, 489, 0, 519, - 681, 628, 0, 674, 647, 648, 0, 675, 643, 679, - 0, 617, 0, 586, 589, 618, 703, 704, 705, 338, - 588, 707, 708, 709, 710, 711, 712, 713, 706, 1080, - 651, 627, 654, 567, 630, 629, 0, 0, 665, 999, - 666, 667, 471, 472, 473, 474, 1067, 691, 362, 587, - 504, 505, 0, 652, 0, 0, 0, 0, 0, 0, - 0, 0, 657, 658, 655, 774, 0, 714, 715, 0, - 0, 581, 582, 394, 0, 600, 402, 361, 488, 396, - 565, 425, 0, 593, 659, 594, 507, 508, 718, 723, - 719, 720, 722, 743, 478, 416, 421, 523, 433, 452, - 510, 564, 486, 515, 359, 554, 525, 458, 644, 672, - 1089, 1062, 1088, 1090, 1091, 1087, 1092, 1093, 1074, 953, - 0, 1006, 1007, 1085, 1084, 1086, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 699, 698, 697, - 696, 695, 694, 693, 692, 0, 0, 641, 376, 762, - 756, 763, 744, 747, 746, 721, 960, 333, 621, 450, - 503, 393, 686, 687, 0, 742, 1052, 1015, 1016, 1017, - 950, 1018, 1012, 1013, 951, 1014, 1053, 1004, 1049, 1050, - 979, 1009, 1019, 1048, 1020, 1051, 980, 1054, 1094, 1095, - 1026, 1010, 296, 1096, 1023, 1055, 1047, 1046, 1021, 1005, - 1056, 1057, 987, 982, 1024, 1025, 1011, 1032, 1033, 1034, - 1037, 952, 1038, 1039, 1040, 1041, 1042, 1036, 1035, 1001, - 1002, 1003, 1027, 1028, 1030, 1031, 1008, 533, 983, 984, - 985, 986, 0, 0, 571, 572, 573, 596, 0, 574, - 556, 620, 403, 334, 537, 563, 759, 760, 0, 0, - 0, 0, 0, 0, 0, 671, 682, 716, 717, 0, - 730, 731, 733, 735, 1043, 737, 530, 531, 745, 0, - 0, 426, 349, 350, 479, 480, 517, 0, 427, 428, - 429, 431, 430, 1029, 740, 741, 738, 454, 516, 538, - 524, 758, 466, 0, 766, 611, 612, 767, 726, 725, - 335, 997, 945, 0, 0, 0, 0, 0, 0, 0, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 948, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 988, 663, 613, 526, 467, 0, 680, 0, 0, 1068, - 1076, 0, 0, 0, 0, 0, 0, 0, 0, 1064, - 0, 0, 0, 0, 940, 0, 0, 977, 1045, 1044, - 964, 974, 0, 0, 357, 263, 608, 732, 610, 609, - 965, 0, 966, 970, 973, 969, 967, 968, 0, 1059, - 0, 0, 0, 0, 0, 0, 932, 944, 0, 949, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 941, 942, 2231, 0, 0, - 0, 998, 0, 943, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 993, 971, 975, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 972, 0, - 996, 1000, 379, 1082, 994, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 1083, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 991, 0, 729, 0, 562, 0, 0, 1066, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 995, 0, - 514, 487, 1079, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 1063, 483, 690, 727, 728, - 615, 0, 1078, 1058, 1060, 1061, 1065, 1069, 1070, 1071, - 1072, 1073, 1075, 1077, 1081, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 1080, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 999, 666, 667, 471, 472, 473, - 474, 1067, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 1089, 1062, 1088, 1090, 1091, - 1087, 1092, 1093, 1074, 953, 0, 1006, 1007, 1085, 1084, - 1086, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 960, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 1052, 1015, 1016, 1017, 950, 1018, 1012, 1013, 951, - 1014, 1053, 1004, 1049, 1050, 979, 1009, 1019, 1048, 1020, - 1051, 980, 1054, 1094, 1095, 1026, 1010, 296, 1096, 1023, - 1055, 1047, 1046, 1021, 1005, 1056, 1057, 987, 982, 1024, - 1025, 1011, 1032, 1033, 1034, 1037, 952, 1038, 1039, 1040, - 1041, 1042, 1036, 1035, 1001, 1002, 1003, 1027, 1028, 1030, - 1031, 1008, 533, 983, 984, 985, 986, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 1043, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 1029, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 997, 945, 0, 0, - 0, 0, 0, 0, 0, 485, 0, 0, 626, 660, - 649, 736, 614, 0, 0, 0, 0, 0, 0, 948, - 0, 0, 0, 386, 0, 0, 449, 664, 645, 656, - 646, 491, 631, 632, 633, 640, 398, 634, 768, 636, - 606, 637, 607, 638, 639, 988, 663, 613, 526, 467, - 0, 680, 0, 0, 1068, 1076, 0, 0, 0, 0, - 0, 0, 0, 0, 1064, 0, 0, 0, 0, 940, - 0, 0, 977, 1045, 1044, 964, 974, 0, 0, 357, - 263, 608, 732, 610, 609, 965, 0, 966, 970, 973, - 969, 967, 968, 0, 1059, 0, 0, 0, 0, 0, - 0, 932, 944, 0, 949, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 941, 942, 0, 0, 0, 0, 998, 0, 943, 0, - 0, 0, 0, 0, 527, 555, 0, 568, 0, 423, - 424, 993, 971, 975, 0, 0, 0, 0, 342, 534, - 552, 358, 521, 566, 363, 529, 545, 353, 484, 518, - 0, 0, 344, 550, 528, 463, 343, 0, 511, 383, - 400, 380, 482, 972, 0, 996, 1000, 379, 1082, 994, - 560, 346, 0, 559, 481, 455, 546, 551, 464, 457, - 0, 345, 548, 462, 456, 435, 390, 1083, 436, 437, - 438, 439, 440, 441, 404, 497, 453, 498, 405, 469, - 468, 470, 406, 407, 408, 409, 410, 411, 412, 413, - 414, 415, 0, 0, 0, 0, 0, 590, 591, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 724, 991, 0, 729, 0, 562, - 0, 0, 1066, 0, 0, 0, 532, 0, 0, 442, - 0, 0, 0, 995, 0, 514, 487, 1079, 0, 0, - 512, 451, 547, 499, 553, 535, 561, 506, 500, 336, - 536, 382, 465, 354, 356, 757, 384, 387, 391, 392, - 475, 476, 493, 520, 539, 540, 541, 381, 368, 513, - 369, 401, 370, 337, 373, 372, 374, 522, 375, 339, - 494, 544, 0, 397, 509, 460, 340, 459, 495, 543, - 542, 355, 570, 577, 578, 668, 0, 583, 771, 772, - 773, 592, 0, 501, 351, 348, 0, 0, 0, 377, - 496, 364, 366, 367, 365, 490, 492, 597, 598, 599, - 601, 0, 602, 603, 0, 0, 0, 0, 604, 669, - 685, 653, 622, 585, 677, 619, 623, 624, 418, 419, - 420, 688, 0, 0, 0, 576, 443, 444, 0, 389, - 388, 461, 341, 0, 0, 432, 417, 502, 347, 385, - 434, 422, 445, 446, 447, 448, 395, 331, 332, 765, - 1063, 483, 690, 727, 728, 615, 0, 1078, 1058, 1060, - 1061, 1065, 1069, 1070, 1071, 1072, 1073, 1075, 1077, 1081, - 764, 0, 670, 684, 769, 683, 761, 489, 0, 519, - 681, 628, 0, 674, 647, 648, 0, 675, 643, 679, - 0, 617, 0, 586, 589, 618, 703, 704, 705, 338, - 588, 707, 708, 709, 710, 711, 712, 713, 706, 1080, - 651, 627, 654, 567, 630, 629, 0, 0, 665, 999, - 666, 667, 471, 472, 473, 474, 1067, 691, 362, 587, - 504, 505, 0, 652, 0, 0, 0, 0, 0, 0, - 0, 0, 657, 658, 655, 774, 0, 714, 715, 0, - 0, 581, 582, 394, 0, 600, 402, 361, 488, 396, - 565, 425, 0, 593, 659, 594, 507, 508, 718, 723, - 719, 720, 722, 743, 478, 416, 421, 523, 433, 452, - 510, 564, 486, 515, 359, 554, 525, 458, 644, 672, - 1089, 1062, 1088, 1090, 1091, 1087, 1092, 1093, 1074, 953, - 0, 1006, 1007, 1085, 1084, 1086, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 699, 698, 697, - 696, 695, 694, 693, 692, 0, 0, 641, 376, 762, - 756, 763, 744, 747, 746, 721, 960, 333, 621, 450, - 503, 393, 686, 687, 0, 742, 1052, 1015, 1016, 1017, - 950, 1018, 1012, 1013, 951, 1014, 1053, 1004, 1049, 1050, - 979, 1009, 1019, 1048, 1020, 1051, 980, 1054, 1094, 1095, - 1026, 1010, 296, 1096, 1023, 1055, 1047, 1046, 1021, 1005, - 1056, 1057, 987, 982, 1024, 1025, 1011, 1032, 1033, 1034, - 1037, 952, 1038, 1039, 1040, 1041, 1042, 1036, 1035, 1001, - 1002, 1003, 1027, 1028, 1030, 1031, 1008, 533, 983, 984, - 985, 986, 0, 0, 571, 572, 573, 596, 0, 574, - 556, 620, 403, 334, 537, 563, 759, 760, 0, 0, - 0, 0, 0, 0, 0, 671, 682, 716, 717, 0, - 730, 731, 733, 735, 1043, 737, 530, 531, 745, 0, - 0, 426, 349, 350, 479, 480, 517, 0, 427, 428, - 429, 431, 430, 1029, 740, 741, 738, 454, 516, 538, - 524, 758, 466, 0, 766, 611, 612, 767, 726, 725, - 335, 997, 945, 0, 0, 0, 0, 0, 0, 0, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 948, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 988, 663, 613, 526, 467, 0, 680, 0, 0, 1068, - 1076, 0, 0, 0, 0, 0, 0, 0, 0, 1064, - 0, 0, 0, 0, 940, 0, 0, 977, 1045, 1044, - 964, 974, 0, 0, 357, 263, 608, 732, 610, 609, - 965, 0, 966, 970, 973, 969, 967, 968, 0, 1059, - 0, 0, 0, 0, 0, 0, 932, 944, 0, 949, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 941, 942, 0, 0, 0, - 0, 998, 0, 943, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 993, 971, 975, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 972, 0, - 996, 1000, 379, 1082, 994, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 1083, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 991, 0, 729, 0, 562, 0, 0, 1066, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 995, 0, - 514, 487, 1079, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 1063, 483, 690, 727, 728, - 615, 0, 1078, 1058, 1060, 1061, 1065, 1069, 1070, 1071, - 1072, 1073, 1075, 1077, 1081, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 1080, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 999, 666, 667, 471, 472, 473, - 474, 1067, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 1089, 1062, 1088, 1090, 1091, - 1087, 1092, 1093, 1074, 953, 0, 1006, 1007, 1085, 1084, - 1086, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 960, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 1052, 1015, 1016, 1017, 950, 1018, 1012, 1013, 951, - 1014, 1053, 1004, 1049, 1050, 979, 1009, 1019, 1048, 1020, - 1051, 980, 1054, 1094, 1095, 1026, 1010, 296, 1096, 1023, - 1055, 1047, 1046, 1021, 1005, 1056, 1057, 987, 982, 1024, - 1025, 1011, 1032, 1033, 1034, 1037, 952, 1038, 1039, 1040, - 1041, 1042, 1036, 1035, 1001, 1002, 1003, 1027, 1028, 1030, - 1031, 1008, 533, 983, 984, 985, 986, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 1043, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 4296, 740, - 4297, 4298, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 997, 945, 0, 0, - 0, 0, 0, 0, 0, 485, 0, 0, 626, 660, - 649, 736, 614, 0, 0, 0, 0, 0, 0, 948, - 0, 0, 0, 386, 0, 0, 449, 664, 645, 656, - 646, 491, 631, 632, 633, 640, 398, 634, 768, 636, - 606, 637, 607, 638, 639, 988, 663, 613, 526, 467, - 0, 680, 0, 0, 1068, 1076, 0, 0, 0, 0, - 0, 0, 0, 0, 1064, 0, 0, 0, 0, 940, - 0, 0, 977, 1045, 1044, 964, 974, 0, 0, 357, - 263, 608, 732, 610, 609, 3236, 0, 3237, 970, 973, - 969, 967, 968, 0, 1059, 0, 0, 0, 0, 0, - 0, 932, 944, 0, 949, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 941, 942, 0, 0, 0, 0, 998, 0, 943, 0, - 0, 0, 0, 0, 527, 555, 0, 568, 0, 423, - 424, 993, 971, 975, 0, 0, 0, 0, 342, 534, - 552, 358, 521, 566, 363, 529, 545, 353, 484, 518, - 0, 0, 344, 550, 528, 463, 343, 0, 511, 383, - 400, 380, 482, 972, 0, 996, 1000, 379, 1082, 994, - 560, 346, 0, 559, 481, 455, 546, 551, 464, 457, - 0, 345, 548, 462, 456, 435, 390, 1083, 436, 437, - 438, 439, 440, 441, 404, 497, 453, 498, 405, 469, - 468, 470, 406, 407, 408, 409, 410, 411, 412, 413, - 414, 415, 0, 0, 0, 0, 0, 590, 591, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 724, 991, 0, 729, 0, 562, - 0, 0, 1066, 0, 0, 0, 532, 0, 0, 442, - 0, 0, 0, 995, 0, 514, 487, 1079, 0, 0, - 512, 451, 547, 499, 553, 535, 561, 506, 500, 336, - 536, 382, 465, 354, 356, 757, 384, 387, 391, 392, - 475, 476, 493, 520, 539, 540, 541, 381, 368, 513, - 369, 401, 370, 337, 373, 372, 374, 522, 375, 339, - 494, 544, 0, 397, 509, 460, 340, 459, 495, 543, - 542, 355, 570, 577, 578, 668, 0, 583, 771, 772, - 773, 592, 0, 501, 351, 348, 0, 0, 0, 377, - 496, 364, 366, 367, 365, 490, 492, 597, 598, 599, - 601, 0, 602, 603, 0, 0, 0, 0, 604, 669, - 685, 653, 622, 585, 677, 619, 623, 624, 418, 419, - 420, 688, 0, 0, 0, 576, 443, 444, 0, 389, - 388, 461, 341, 0, 0, 432, 417, 502, 347, 385, - 434, 422, 445, 446, 447, 448, 395, 331, 332, 765, - 1063, 483, 690, 727, 728, 615, 0, 1078, 1058, 1060, - 1061, 1065, 1069, 1070, 1071, 1072, 1073, 1075, 1077, 1081, - 764, 0, 670, 684, 769, 683, 761, 489, 0, 519, - 681, 628, 0, 674, 647, 648, 0, 675, 643, 679, - 0, 617, 0, 586, 589, 618, 703, 704, 705, 338, - 588, 707, 708, 709, 710, 711, 712, 713, 706, 1080, - 651, 627, 654, 567, 630, 629, 0, 0, 665, 999, - 666, 667, 471, 472, 473, 474, 1067, 691, 362, 587, - 504, 505, 0, 652, 0, 0, 0, 0, 0, 0, - 0, 0, 657, 658, 655, 774, 0, 714, 715, 0, - 0, 581, 582, 394, 0, 600, 402, 361, 488, 396, - 565, 425, 0, 593, 659, 594, 507, 508, 718, 723, - 719, 720, 722, 743, 478, 416, 421, 523, 433, 452, - 510, 564, 486, 515, 359, 554, 525, 458, 644, 672, - 1089, 1062, 1088, 1090, 1091, 1087, 1092, 1093, 1074, 953, - 0, 1006, 1007, 1085, 1084, 1086, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 699, 698, 697, - 696, 695, 694, 693, 692, 0, 0, 641, 376, 762, - 756, 763, 744, 747, 746, 721, 960, 333, 621, 450, - 503, 393, 686, 687, 0, 742, 1052, 1015, 1016, 1017, - 950, 1018, 1012, 1013, 951, 1014, 1053, 1004, 1049, 1050, - 979, 1009, 1019, 1048, 1020, 1051, 980, 1054, 1094, 1095, - 1026, 1010, 296, 1096, 1023, 1055, 1047, 1046, 1021, 1005, - 1056, 1057, 987, 982, 1024, 1025, 1011, 1032, 1033, 1034, - 1037, 952, 1038, 1039, 1040, 1041, 1042, 1036, 1035, 1001, - 1002, 1003, 1027, 1028, 1030, 1031, 1008, 533, 983, 984, - 985, 986, 0, 0, 571, 572, 573, 596, 0, 574, - 556, 620, 403, 334, 537, 563, 759, 760, 0, 0, - 0, 0, 0, 0, 0, 671, 682, 716, 717, 0, - 730, 731, 733, 735, 1043, 737, 530, 531, 745, 0, - 0, 426, 349, 350, 479, 480, 517, 0, 427, 428, - 429, 431, 430, 1029, 740, 741, 738, 454, 516, 538, - 524, 758, 466, 0, 766, 611, 612, 767, 726, 725, - 335, 997, 945, 0, 0, 0, 0, 0, 0, 0, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 2071, 0, 0, 0, 948, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 988, 663, 613, 526, 467, 0, 680, 0, 0, 1068, - 1076, 0, 0, 0, 0, 0, 0, 0, 0, 1064, - 0, 0, 0, 0, 940, 0, 0, 977, 1045, 1044, - 964, 974, 0, 0, 357, 263, 608, 732, 610, 609, - 965, 0, 966, 970, 973, 969, 967, 968, 0, 1059, - 0, 0, 0, 0, 0, 0, 0, 944, 0, 949, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 941, 942, 0, 0, 0, - 0, 998, 0, 943, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 993, 971, 975, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 972, 0, - 996, 1000, 379, 1082, 994, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 1083, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 991, 0, 729, 0, 562, 0, 0, 1066, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 995, 0, - 514, 487, 1079, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 2072, 2073, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 1063, 483, 690, 727, 728, - 615, 0, 1078, 1058, 1060, 1061, 1065, 1069, 1070, 1071, - 1072, 1073, 1075, 1077, 1081, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 1080, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 999, 666, 667, 471, 472, 473, - 474, 1067, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 1089, 1062, 1088, 1090, 1091, - 1087, 1092, 1093, 1074, 953, 0, 1006, 1007, 1085, 1084, - 1086, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 960, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 1052, 1015, 1016, 1017, 950, 1018, 1012, 1013, 951, - 1014, 1053, 1004, 1049, 1050, 979, 1009, 1019, 1048, 1020, - 1051, 980, 1054, 1094, 1095, 1026, 1010, 296, 1096, 1023, - 1055, 1047, 1046, 1021, 1005, 1056, 1057, 987, 982, 1024, - 1025, 1011, 1032, 1033, 1034, 1037, 952, 1038, 1039, 1040, - 1041, 1042, 1036, 1035, 1001, 1002, 1003, 1027, 1028, 1030, - 1031, 1008, 533, 983, 984, 985, 986, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 1043, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 1029, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 997, 945, 0, 0, - 0, 0, 0, 0, 0, 485, 0, 0, 626, 660, - 649, 736, 614, 0, 0, 0, 0, 0, 0, 948, - 0, 0, 0, 386, 0, 0, 449, 664, 645, 656, - 646, 491, 631, 632, 633, 640, 398, 634, 768, 636, - 606, 637, 607, 638, 639, 988, 663, 613, 526, 467, - 0, 680, 0, 0, 1068, 1076, 0, 0, 0, 0, - 0, 0, 0, 0, 1064, 0, 0, 0, 0, 1585, - 0, 0, 977, 1045, 1044, 964, 974, 0, 0, 357, - 263, 608, 732, 610, 609, 965, 0, 966, 970, 973, - 969, 967, 968, 0, 1059, 0, 0, 0, 0, 0, - 0, 932, 944, 0, 949, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 941, 942, 0, 0, 0, 0, 998, 0, 943, 0, - 0, 0, 0, 0, 527, 555, 0, 568, 0, 423, - 424, 993, 971, 975, 0, 0, 0, 0, 342, 534, - 552, 358, 521, 566, 363, 529, 545, 353, 484, 518, - 0, 0, 344, 550, 528, 463, 343, 0, 511, 383, - 400, 380, 482, 972, 0, 996, 1000, 379, 1082, 994, - 560, 346, 0, 559, 481, 455, 546, 551, 464, 457, - 0, 345, 548, 462, 456, 435, 390, 1083, 436, 437, - 438, 439, 440, 441, 404, 497, 453, 498, 405, 469, - 468, 470, 406, 407, 408, 409, 410, 411, 412, 413, - 414, 415, 0, 0, 0, 0, 0, 590, 591, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 724, 991, 0, 729, 0, 562, - 0, 0, 1066, 0, 0, 0, 532, 0, 0, 442, - 0, 0, 0, 995, 0, 514, 487, 1079, 0, 0, - 512, 451, 547, 499, 553, 535, 561, 506, 500, 336, - 536, 382, 465, 354, 356, 757, 384, 387, 391, 392, - 475, 476, 493, 520, 539, 540, 541, 381, 368, 513, - 369, 401, 370, 337, 373, 372, 374, 522, 375, 339, - 494, 544, 0, 397, 509, 460, 340, 459, 495, 543, - 542, 355, 570, 577, 578, 668, 0, 583, 771, 772, - 773, 592, 0, 501, 351, 348, 0, 0, 0, 377, - 496, 364, 366, 367, 365, 490, 492, 597, 598, 599, - 601, 0, 602, 603, 0, 0, 0, 0, 604, 669, - 685, 653, 622, 585, 677, 619, 623, 624, 418, 419, - 420, 688, 0, 0, 0, 576, 443, 444, 0, 389, - 388, 461, 341, 0, 0, 432, 417, 502, 347, 385, - 434, 422, 445, 446, 447, 448, 395, 331, 332, 765, - 1063, 483, 690, 727, 728, 615, 0, 1078, 1058, 1060, - 1061, 1065, 1069, 1070, 1071, 1072, 1073, 1075, 1077, 1081, - 764, 0, 670, 684, 769, 683, 761, 489, 0, 519, - 681, 628, 0, 674, 647, 648, 0, 675, 643, 679, - 0, 617, 0, 586, 589, 618, 703, 704, 705, 338, - 588, 707, 708, 709, 710, 711, 712, 713, 706, 1080, - 651, 627, 654, 567, 630, 629, 0, 0, 665, 999, - 666, 667, 471, 472, 473, 474, 1067, 691, 362, 587, - 504, 505, 0, 652, 0, 0, 0, 0, 0, 0, - 0, 0, 657, 658, 655, 774, 0, 714, 715, 0, - 0, 581, 582, 394, 0, 600, 402, 361, 488, 396, - 565, 425, 0, 593, 659, 594, 507, 508, 718, 723, - 719, 720, 722, 743, 478, 416, 421, 523, 433, 452, - 510, 564, 486, 515, 359, 554, 525, 458, 644, 672, - 1089, 1062, 1088, 1090, 1091, 1087, 1092, 1093, 1074, 953, - 0, 1006, 1007, 1085, 1084, 1086, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 699, 698, 697, - 696, 695, 694, 693, 692, 0, 0, 641, 376, 762, - 756, 763, 744, 747, 746, 721, 960, 333, 621, 450, - 503, 393, 686, 687, 0, 742, 1052, 1015, 1016, 1017, - 950, 1018, 1012, 1013, 951, 1014, 1053, 1004, 1049, 1050, - 979, 1009, 1019, 1048, 1020, 1051, 980, 1054, 1094, 1095, - 1026, 1010, 296, 1096, 1023, 1055, 1047, 1046, 1021, 1005, - 1056, 1057, 987, 982, 1024, 1025, 1011, 1032, 1033, 1034, - 1037, 952, 1038, 1039, 1040, 1041, 1042, 1036, 1035, 1001, - 1002, 1003, 1027, 1028, 1030, 1031, 1008, 533, 983, 984, - 985, 986, 0, 0, 571, 572, 573, 596, 0, 574, - 556, 620, 403, 334, 537, 563, 759, 760, 0, 0, - 0, 0, 0, 0, 0, 671, 682, 716, 717, 0, - 730, 731, 733, 735, 1043, 737, 530, 531, 745, 0, - 0, 426, 349, 350, 479, 480, 517, 0, 427, 428, - 429, 431, 430, 1029, 740, 741, 738, 454, 516, 538, - 524, 758, 466, 0, 766, 611, 612, 767, 726, 725, - 335, 997, 945, 0, 0, 0, 0, 0, 0, 0, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 948, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 988, 663, 613, 526, 467, 0, 680, 0, 0, 1068, - 1076, 0, 0, 0, 0, 0, 0, 0, 0, 1064, - 0, 0, 0, 0, 940, 0, 0, 977, 1045, 1044, - 964, 974, 0, 0, 357, 263, 608, 732, 610, 609, - 965, 0, 966, 970, 973, 969, 967, 968, 0, 1059, - 0, 0, 0, 0, 0, 0, 0, 944, 0, 949, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 941, 942, 0, 0, 0, - 0, 998, 0, 943, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 993, 971, 975, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 972, 0, - 996, 1000, 379, 1082, 994, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 1083, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, + 0, 0, 593, 594, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 727, + 994, 0, 732, 0, 565, 0, 0, 1069, 0, 0, + 0, 535, 0, 0, 443, 0, 0, 0, 998, 0, + 517, 490, 1082, 0, 0, 515, 454, 550, 502, 556, + 538, 564, 509, 503, 336, 539, 382, 468, 354, 356, + 760, 384, 387, 391, 392, 478, 479, 496, 523, 542, + 543, 544, 381, 368, 516, 369, 401, 370, 337, 373, + 372, 374, 525, 375, 339, 497, 547, 0, 397, 512, + 463, 340, 462, 498, 546, 545, 355, 573, 580, 581, + 671, 0, 586, 774, 775, 776, 595, 0, 504, 351, + 348, 0, 0, 0, 377, 499, 364, 366, 367, 365, + 493, 495, 600, 601, 602, 604, 0, 605, 606, 0, + 0, 0, 0, 607, 672, 688, 656, 625, 588, 680, + 622, 626, 627, 418, 419, 420, 421, 691, 0, 0, + 0, 579, 444, 445, 0, 389, 388, 464, 341, 0, + 0, 433, 417, 505, 347, 385, 435, 423, 446, 447, + 448, 449, 450, 0, 451, 395, 331, 332, 768, 1066, + 486, 693, 730, 731, 618, 0, 1081, 1061, 1063, 1064, + 1068, 1072, 1073, 1074, 1075, 1076, 1078, 1080, 1084, 767, + 0, 673, 687, 772, 686, 764, 492, 0, 522, 684, + 631, 0, 677, 650, 651, 0, 678, 646, 682, 0, + 620, 0, 589, 592, 621, 706, 707, 708, 338, 591, + 710, 711, 712, 713, 714, 715, 716, 709, 1083, 654, + 630, 657, 570, 633, 632, 0, 0, 668, 1002, 669, + 670, 474, 475, 476, 477, 1070, 694, 362, 590, 507, + 508, 0, 655, 0, 0, 0, 0, 0, 0, 0, + 0, 660, 661, 658, 777, 0, 717, 718, 0, 0, + 584, 585, 394, 0, 603, 402, 361, 491, 396, 568, + 426, 0, 596, 662, 597, 510, 511, 721, 726, 722, + 723, 725, 746, 481, 416, 422, 526, 434, 455, 513, + 567, 489, 518, 359, 557, 528, 461, 647, 675, 1092, + 1065, 1091, 1093, 1094, 1090, 1095, 1096, 1077, 956, 0, + 1009, 1010, 1088, 1087, 1089, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 702, 701, 700, 699, + 698, 697, 696, 695, 0, 0, 644, 376, 765, 759, + 766, 747, 750, 749, 724, 963, 333, 624, 453, 506, + 393, 689, 690, 0, 745, 1055, 1018, 1019, 1020, 953, + 1021, 1015, 1016, 954, 1017, 1056, 1007, 1052, 1053, 982, + 1012, 1022, 1051, 1023, 1054, 983, 1057, 1097, 1098, 1029, + 1013, 296, 1099, 1026, 1058, 1050, 1049, 1024, 1008, 1059, + 1060, 990, 985, 1027, 1028, 1014, 1035, 1036, 1037, 1040, + 955, 1041, 1042, 1043, 1044, 1045, 1039, 1038, 1004, 1005, + 1006, 1030, 1031, 1033, 1034, 1011, 536, 986, 987, 988, + 989, 0, 0, 574, 575, 576, 599, 0, 577, 559, + 623, 403, 334, 540, 566, 762, 763, 0, 0, 0, + 0, 0, 0, 0, 674, 685, 719, 720, 0, 733, + 734, 736, 738, 1046, 740, 533, 534, 748, 0, 0, + 427, 349, 350, 482, 483, 520, 0, 428, 429, 430, + 432, 431, 1032, 743, 744, 741, 457, 519, 541, 527, + 761, 469, 0, 769, 614, 615, 770, 729, 728, 335, + 1000, 948, 0, 0, 0, 0, 0, 0, 0, 488, + 0, 0, 629, 663, 652, 739, 617, 0, 0, 0, + 0, 0, 0, 951, 0, 0, 0, 386, 5060, 0, + 452, 667, 648, 659, 649, 494, 634, 635, 636, 643, + 398, 637, 771, 639, 609, 640, 610, 641, 642, 991, + 666, 616, 529, 470, 0, 683, 0, 0, 1071, 1079, + 0, 0, 0, 0, 0, 0, 0, 0, 1067, 0, + 0, 0, 0, 943, 0, 0, 980, 1048, 1047, 967, + 977, 0, 0, 357, 263, 611, 735, 613, 612, 968, + 0, 969, 973, 976, 972, 970, 971, 0, 1062, 0, + 0, 0, 0, 0, 0, 935, 947, 0, 952, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 944, 945, 0, 0, 0, 0, + 1001, 0, 946, 0, 0, 0, 0, 0, 530, 558, + 0, 571, 0, 424, 425, 996, 974, 978, 0, 0, + 0, 0, 342, 537, 555, 358, 524, 569, 363, 532, + 548, 353, 487, 521, 0, 0, 344, 553, 531, 466, + 343, 0, 514, 383, 400, 380, 485, 975, 0, 999, + 1003, 379, 1085, 997, 563, 346, 0, 562, 484, 458, + 549, 554, 467, 460, 0, 345, 551, 465, 459, 436, + 390, 1086, 437, 438, 439, 440, 441, 442, 404, 500, + 456, 501, 405, 472, 471, 473, 406, 407, 408, 409, + 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, + 0, 593, 594, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 727, 994, + 0, 732, 0, 565, 0, 0, 1069, 0, 0, 0, + 535, 0, 0, 443, 0, 0, 0, 998, 0, 517, + 490, 1082, 0, 0, 515, 454, 550, 502, 556, 538, + 564, 509, 503, 336, 539, 382, 468, 354, 356, 760, + 384, 387, 391, 392, 478, 479, 496, 523, 542, 543, + 544, 381, 368, 516, 369, 401, 370, 337, 373, 372, + 374, 525, 375, 339, 497, 547, 0, 397, 512, 463, + 340, 462, 498, 546, 545, 355, 573, 580, 581, 671, + 0, 586, 774, 775, 776, 595, 0, 504, 351, 348, + 0, 0, 0, 377, 499, 364, 366, 367, 365, 493, + 495, 600, 601, 602, 604, 0, 605, 606, 0, 0, + 0, 0, 607, 672, 688, 656, 625, 588, 680, 622, + 626, 627, 418, 419, 420, 421, 691, 0, 0, 0, + 579, 444, 445, 0, 389, 388, 464, 341, 0, 0, + 433, 417, 505, 347, 385, 435, 423, 446, 447, 448, + 449, 450, 0, 451, 395, 331, 332, 768, 1066, 486, + 693, 730, 731, 618, 0, 1081, 1061, 1063, 1064, 1068, + 1072, 1073, 1074, 1075, 1076, 1078, 1080, 1084, 767, 0, + 673, 687, 772, 686, 764, 492, 0, 522, 684, 631, + 0, 677, 650, 651, 0, 678, 646, 682, 0, 620, + 0, 589, 592, 621, 706, 707, 708, 338, 591, 710, + 711, 712, 713, 714, 715, 716, 709, 1083, 654, 630, + 657, 570, 633, 632, 0, 0, 668, 1002, 669, 670, + 474, 475, 476, 477, 1070, 694, 362, 590, 507, 508, + 0, 655, 0, 0, 0, 0, 0, 0, 0, 0, + 660, 661, 658, 777, 0, 717, 718, 0, 0, 584, + 585, 394, 0, 603, 402, 361, 491, 396, 568, 426, + 0, 596, 662, 597, 510, 511, 721, 726, 722, 723, + 725, 746, 481, 416, 422, 526, 434, 455, 513, 567, + 489, 518, 359, 557, 528, 461, 647, 675, 1092, 1065, + 1091, 1093, 1094, 1090, 1095, 1096, 1077, 956, 0, 1009, + 1010, 1088, 1087, 1089, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 702, 701, 700, 699, 698, + 697, 696, 695, 0, 0, 644, 376, 765, 759, 766, + 747, 750, 749, 724, 963, 333, 624, 453, 506, 393, + 689, 690, 0, 745, 1055, 1018, 1019, 1020, 953, 1021, + 1015, 1016, 954, 1017, 1056, 1007, 1052, 1053, 982, 1012, + 1022, 1051, 1023, 1054, 983, 1057, 1097, 1098, 1029, 1013, + 296, 1099, 1026, 1058, 1050, 1049, 1024, 1008, 1059, 1060, + 990, 985, 1027, 1028, 1014, 1035, 1036, 1037, 1040, 955, + 1041, 1042, 1043, 1044, 1045, 1039, 1038, 1004, 1005, 1006, + 1030, 1031, 1033, 1034, 1011, 536, 986, 987, 988, 989, + 0, 0, 574, 575, 576, 599, 0, 577, 559, 623, + 403, 334, 540, 566, 762, 763, 0, 0, 0, 0, + 0, 0, 0, 674, 685, 719, 720, 0, 733, 734, + 736, 738, 1046, 740, 533, 534, 748, 0, 0, 427, + 349, 350, 482, 483, 520, 0, 428, 429, 430, 432, + 431, 1032, 743, 744, 741, 457, 519, 541, 527, 761, + 469, 0, 769, 614, 615, 770, 729, 728, 335, 1000, + 948, 0, 0, 0, 0, 0, 0, 0, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 951, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 991, 666, + 616, 529, 470, 0, 683, 0, 0, 1071, 1079, 0, + 0, 0, 0, 0, 0, 0, 0, 1067, 0, 0, + 0, 0, 943, 0, 0, 980, 1048, 1047, 967, 977, + 0, 0, 357, 263, 611, 735, 613, 612, 968, 0, + 969, 973, 976, 972, 970, 971, 0, 1062, 0, 0, + 0, 0, 0, 0, 935, 947, 0, 952, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 944, 945, 0, 0, 0, 0, 1001, + 0, 946, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 996, 974, 978, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 975, 0, 999, 1003, + 379, 1085, 997, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 1086, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 994, 0, + 732, 0, 565, 0, 0, 1069, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 998, 0, 517, 490, + 1082, 4921, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 1066, 486, 693, + 730, 731, 618, 0, 1081, 1061, 1063, 1064, 1068, 1072, + 1073, 1074, 1075, 1076, 1078, 1080, 1084, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 1083, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 1002, 669, 670, 474, + 475, 476, 477, 1070, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 1092, 1065, 1091, + 1093, 1094, 1090, 1095, 1096, 1077, 956, 0, 1009, 1010, + 1088, 1087, 1089, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 963, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 1055, 1018, 1019, 1020, 953, 1021, 1015, + 1016, 954, 1017, 1056, 1007, 1052, 1053, 982, 1012, 1022, + 1051, 1023, 1054, 983, 1057, 1097, 1098, 1029, 1013, 296, + 1099, 1026, 1058, 1050, 1049, 1024, 1008, 1059, 1060, 990, + 985, 1027, 1028, 1014, 1035, 1036, 1037, 1040, 955, 1041, + 1042, 1043, 1044, 1045, 1039, 1038, 1004, 1005, 1006, 1030, + 1031, 1033, 1034, 1011, 536, 986, 987, 988, 989, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 1046, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 1032, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 1000, 948, + 0, 0, 0, 0, 0, 0, 0, 488, 0, 0, + 629, 663, 652, 739, 617, 0, 0, 0, 0, 0, + 0, 951, 0, 0, 0, 386, 0, 0, 452, 667, + 648, 659, 649, 494, 634, 635, 636, 643, 398, 637, + 771, 639, 609, 640, 610, 641, 642, 991, 666, 616, + 529, 470, 0, 683, 0, 0, 1071, 1079, 0, 0, + 0, 0, 0, 0, 0, 0, 1067, 0, 0, 0, + 0, 943, 0, 0, 980, 1048, 1047, 967, 977, 0, + 0, 357, 263, 611, 735, 613, 612, 968, 0, 969, + 973, 976, 972, 970, 971, 0, 1062, 0, 0, 0, + 0, 0, 0, 935, 947, 0, 952, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 944, 945, 1864, 0, 0, 0, 1001, 0, + 946, 0, 0, 0, 0, 0, 530, 558, 0, 571, + 0, 424, 425, 996, 974, 978, 0, 0, 0, 0, + 342, 537, 555, 358, 524, 569, 363, 532, 548, 353, + 487, 521, 0, 0, 344, 553, 531, 466, 343, 0, + 514, 383, 400, 380, 485, 975, 0, 999, 1003, 379, + 1085, 997, 563, 346, 0, 562, 484, 458, 549, 554, + 467, 460, 0, 345, 551, 465, 459, 436, 390, 1086, + 437, 438, 439, 440, 441, 442, 404, 500, 456, 501, + 405, 472, 471, 473, 406, 407, 408, 409, 410, 411, + 412, 413, 414, 415, 0, 0, 0, 0, 0, 593, + 594, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 727, 994, 0, 732, + 0, 565, 0, 0, 1069, 0, 0, 0, 535, 0, + 0, 443, 0, 0, 0, 998, 0, 517, 490, 1082, + 0, 0, 515, 454, 550, 502, 556, 538, 564, 509, + 503, 336, 539, 382, 468, 354, 356, 760, 384, 387, + 391, 392, 478, 479, 496, 523, 542, 543, 544, 381, + 368, 516, 369, 401, 370, 337, 373, 372, 374, 525, + 375, 339, 497, 547, 0, 397, 512, 463, 340, 462, + 498, 546, 545, 355, 573, 580, 581, 671, 0, 586, + 774, 775, 776, 595, 0, 504, 351, 348, 0, 0, + 0, 377, 499, 364, 366, 367, 365, 493, 495, 600, + 601, 602, 604, 0, 605, 606, 0, 0, 0, 0, + 607, 672, 688, 656, 625, 588, 680, 622, 626, 627, + 418, 419, 420, 421, 691, 0, 0, 0, 579, 444, + 445, 0, 389, 388, 464, 341, 0, 0, 433, 417, + 505, 347, 385, 435, 423, 446, 447, 448, 449, 450, + 0, 451, 395, 331, 332, 768, 1066, 486, 693, 730, + 731, 618, 0, 1081, 1061, 1063, 1064, 1068, 1072, 1073, + 1074, 1075, 1076, 1078, 1080, 1084, 767, 0, 673, 687, + 772, 686, 764, 492, 0, 522, 684, 631, 0, 677, + 650, 651, 0, 678, 646, 682, 0, 620, 0, 589, + 592, 621, 706, 707, 708, 338, 591, 710, 711, 712, + 713, 714, 715, 716, 709, 1083, 654, 630, 657, 570, + 633, 632, 0, 0, 668, 1002, 669, 670, 474, 475, + 476, 477, 1070, 694, 362, 590, 507, 508, 0, 655, + 0, 0, 0, 0, 0, 0, 0, 0, 660, 661, + 658, 777, 0, 717, 718, 0, 0, 584, 585, 394, + 0, 603, 402, 361, 491, 396, 568, 426, 0, 596, + 662, 597, 510, 511, 721, 726, 722, 723, 725, 746, + 481, 416, 422, 526, 434, 455, 513, 567, 489, 518, + 359, 557, 528, 461, 647, 675, 1092, 1065, 1091, 1093, + 1094, 1090, 1095, 1096, 1077, 956, 0, 1009, 1010, 1088, + 1087, 1089, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 702, 701, 700, 699, 698, 697, 696, + 695, 0, 0, 644, 376, 765, 759, 766, 747, 750, + 749, 724, 963, 333, 624, 453, 506, 393, 689, 690, + 0, 745, 1055, 1018, 1019, 1020, 953, 1021, 1015, 1016, + 954, 1017, 1056, 1007, 1052, 1053, 982, 1012, 1022, 1051, + 1023, 1054, 983, 1057, 1097, 1098, 1029, 1013, 296, 1099, + 1026, 1058, 1050, 1049, 1024, 1008, 1059, 1060, 990, 985, + 1027, 1028, 1014, 1035, 1036, 1037, 1040, 955, 1041, 1042, + 1043, 1044, 1045, 1039, 1038, 1004, 1005, 1006, 1030, 1031, + 1033, 1034, 1011, 536, 986, 987, 988, 989, 0, 0, + 574, 575, 576, 599, 0, 577, 559, 623, 403, 334, + 540, 566, 762, 763, 0, 0, 0, 0, 0, 0, + 0, 674, 685, 719, 720, 0, 733, 734, 736, 738, + 1046, 740, 533, 534, 748, 0, 0, 427, 349, 350, + 482, 483, 520, 0, 428, 429, 430, 432, 431, 1032, + 743, 744, 741, 457, 519, 541, 527, 761, 469, 0, + 769, 614, 615, 770, 729, 728, 335, 1000, 948, 0, + 0, 0, 0, 0, 0, 0, 488, 0, 0, 629, + 663, 652, 739, 617, 0, 0, 0, 0, 0, 0, + 951, 0, 0, 0, 386, 2244, 0, 452, 667, 648, + 659, 649, 494, 634, 635, 636, 643, 398, 637, 771, + 639, 609, 640, 610, 641, 642, 991, 666, 616, 529, + 470, 0, 683, 0, 0, 1071, 1079, 0, 0, 0, + 0, 0, 0, 0, 0, 1067, 0, 0, 0, 0, + 943, 0, 0, 980, 1048, 1047, 967, 977, 0, 0, + 357, 263, 611, 735, 613, 612, 968, 0, 969, 973, + 976, 972, 970, 971, 0, 1062, 0, 0, 0, 0, + 0, 0, 935, 947, 0, 952, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 944, 945, 0, 0, 0, 0, 1001, 0, 946, + 0, 0, 0, 0, 0, 530, 558, 0, 571, 0, + 424, 425, 996, 974, 978, 0, 0, 0, 0, 342, + 537, 555, 358, 524, 569, 363, 532, 548, 353, 487, + 521, 0, 0, 344, 553, 531, 466, 343, 0, 514, + 383, 400, 380, 485, 975, 0, 999, 1003, 379, 1085, + 997, 563, 346, 0, 562, 484, 458, 549, 554, 467, + 460, 0, 345, 551, 465, 459, 436, 390, 1086, 437, + 438, 439, 440, 441, 442, 404, 500, 456, 501, 405, + 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, + 413, 414, 415, 0, 0, 0, 0, 0, 593, 594, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 727, 994, 0, 732, 0, + 565, 0, 0, 1069, 0, 0, 0, 535, 0, 0, + 443, 0, 0, 0, 998, 0, 517, 490, 1082, 0, + 0, 515, 454, 550, 502, 556, 538, 564, 509, 503, + 336, 539, 382, 468, 354, 356, 760, 384, 387, 391, + 392, 478, 479, 496, 523, 542, 543, 544, 381, 368, + 516, 369, 401, 370, 337, 373, 372, 374, 525, 375, + 339, 497, 547, 0, 397, 512, 463, 340, 462, 498, + 546, 545, 355, 573, 580, 581, 671, 0, 586, 774, + 775, 776, 595, 0, 504, 351, 348, 0, 0, 0, + 377, 499, 364, 366, 367, 365, 493, 495, 600, 601, + 602, 604, 0, 605, 606, 0, 0, 0, 0, 607, + 672, 688, 656, 625, 588, 680, 622, 626, 627, 418, + 419, 420, 421, 691, 0, 0, 0, 579, 444, 445, + 0, 389, 388, 464, 341, 0, 0, 433, 417, 505, + 347, 385, 435, 423, 446, 447, 448, 449, 450, 0, + 451, 395, 331, 332, 768, 1066, 486, 693, 730, 731, + 618, 0, 1081, 1061, 1063, 1064, 1068, 1072, 1073, 1074, + 1075, 1076, 1078, 1080, 1084, 767, 0, 673, 687, 772, + 686, 764, 492, 0, 522, 684, 631, 0, 677, 650, + 651, 0, 678, 646, 682, 0, 620, 0, 589, 592, + 621, 706, 707, 708, 338, 591, 710, 711, 712, 713, + 714, 715, 716, 709, 1083, 654, 630, 657, 570, 633, + 632, 0, 0, 668, 1002, 669, 670, 474, 475, 476, + 477, 1070, 694, 362, 590, 507, 508, 0, 655, 0, + 0, 0, 0, 0, 0, 0, 0, 660, 661, 658, + 777, 0, 717, 718, 0, 0, 584, 585, 394, 0, + 603, 402, 361, 491, 396, 568, 426, 0, 596, 662, + 597, 510, 511, 721, 726, 722, 723, 725, 746, 481, + 416, 422, 526, 434, 455, 513, 567, 489, 518, 359, + 557, 528, 461, 647, 675, 1092, 1065, 1091, 1093, 1094, + 1090, 1095, 1096, 1077, 956, 0, 1009, 1010, 1088, 1087, + 1089, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 702, 701, 700, 699, 698, 697, 696, 695, + 0, 0, 644, 376, 765, 759, 766, 747, 750, 749, + 724, 963, 333, 624, 453, 506, 393, 689, 690, 0, + 745, 1055, 1018, 1019, 1020, 953, 1021, 1015, 1016, 954, + 1017, 1056, 1007, 1052, 1053, 982, 1012, 1022, 1051, 1023, + 1054, 983, 1057, 1097, 1098, 1029, 1013, 296, 1099, 1026, + 1058, 1050, 1049, 1024, 1008, 1059, 1060, 990, 985, 1027, + 1028, 1014, 1035, 1036, 1037, 1040, 955, 1041, 1042, 1043, + 1044, 1045, 1039, 1038, 1004, 1005, 1006, 1030, 1031, 1033, + 1034, 1011, 536, 986, 987, 988, 989, 0, 0, 574, + 575, 576, 599, 0, 577, 559, 623, 403, 334, 540, + 566, 762, 763, 0, 0, 0, 0, 0, 0, 0, + 674, 685, 719, 720, 0, 733, 734, 736, 738, 1046, + 740, 533, 534, 748, 0, 0, 427, 349, 350, 482, + 483, 520, 0, 428, 429, 430, 432, 431, 1032, 743, + 744, 741, 457, 519, 541, 527, 761, 469, 0, 769, + 614, 615, 770, 729, 728, 335, 1000, 948, 0, 2709, + 0, 0, 0, 0, 0, 488, 0, 0, 629, 663, + 652, 739, 617, 0, 0, 0, 0, 0, 0, 951, + 0, 0, 0, 386, 0, 0, 452, 667, 648, 659, + 649, 494, 634, 635, 636, 643, 398, 637, 771, 639, + 609, 640, 610, 641, 642, 991, 666, 616, 529, 470, + 0, 683, 0, 0, 1071, 1079, 0, 0, 0, 0, + 0, 0, 0, 0, 1067, 0, 0, 0, 0, 943, + 0, 0, 980, 1048, 1047, 967, 977, 0, 0, 357, + 263, 611, 735, 613, 612, 968, 0, 969, 973, 976, + 972, 970, 971, 0, 1062, 0, 0, 0, 0, 0, + 0, 935, 947, 0, 952, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 944, 945, 0, 0, 0, 0, 1001, 0, 946, 0, + 0, 0, 0, 0, 530, 558, 0, 571, 0, 424, + 425, 996, 974, 978, 0, 0, 0, 0, 342, 537, + 555, 358, 524, 569, 363, 532, 548, 353, 487, 521, + 0, 0, 344, 553, 531, 466, 343, 0, 514, 383, + 400, 380, 485, 975, 0, 999, 1003, 379, 1085, 997, + 563, 346, 0, 562, 484, 458, 549, 554, 467, 460, + 0, 345, 551, 465, 459, 436, 390, 1086, 437, 438, + 439, 440, 441, 442, 404, 500, 456, 501, 405, 472, + 471, 473, 406, 407, 408, 409, 410, 411, 412, 413, + 414, 415, 0, 0, 0, 0, 0, 593, 594, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 727, 994, 0, 732, 0, 565, + 0, 0, 1069, 0, 0, 0, 535, 0, 0, 443, + 0, 0, 0, 998, 0, 517, 490, 1082, 0, 0, + 515, 454, 550, 502, 556, 538, 564, 509, 503, 336, + 539, 382, 468, 354, 356, 760, 384, 387, 391, 392, + 478, 479, 496, 523, 542, 543, 544, 381, 368, 516, + 369, 401, 370, 337, 373, 372, 374, 525, 375, 339, + 497, 547, 0, 397, 512, 463, 340, 462, 498, 546, + 545, 355, 573, 580, 581, 671, 0, 586, 774, 775, + 776, 595, 0, 504, 351, 348, 0, 0, 0, 377, + 499, 364, 366, 367, 365, 493, 495, 600, 601, 602, + 604, 0, 605, 606, 0, 0, 0, 0, 607, 672, + 688, 656, 625, 588, 680, 622, 626, 627, 418, 419, + 420, 421, 691, 0, 0, 0, 579, 444, 445, 0, + 389, 388, 464, 341, 0, 0, 433, 417, 505, 347, + 385, 435, 423, 446, 447, 448, 449, 450, 0, 451, + 395, 331, 332, 768, 1066, 486, 693, 730, 731, 618, + 0, 1081, 1061, 1063, 1064, 1068, 1072, 1073, 1074, 1075, + 1076, 1078, 1080, 1084, 767, 0, 673, 687, 772, 686, + 764, 492, 0, 522, 684, 631, 0, 677, 650, 651, + 0, 678, 646, 682, 0, 620, 0, 589, 592, 621, + 706, 707, 708, 338, 591, 710, 711, 712, 713, 714, + 715, 716, 709, 1083, 654, 630, 657, 570, 633, 632, + 0, 0, 668, 1002, 669, 670, 474, 475, 476, 477, + 1070, 694, 362, 590, 507, 508, 0, 655, 0, 0, + 0, 0, 0, 0, 0, 0, 660, 661, 658, 777, + 0, 717, 718, 0, 0, 584, 585, 394, 0, 603, + 402, 361, 491, 396, 568, 426, 0, 596, 662, 597, + 510, 511, 721, 726, 722, 723, 725, 746, 481, 416, + 422, 526, 434, 455, 513, 567, 489, 518, 359, 557, + 528, 461, 647, 675, 1092, 1065, 1091, 1093, 1094, 1090, + 1095, 1096, 1077, 956, 0, 1009, 1010, 1088, 1087, 1089, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 702, 701, 700, 699, 698, 697, 696, 695, 0, + 0, 644, 376, 765, 759, 766, 747, 750, 749, 724, + 963, 333, 624, 453, 506, 393, 689, 690, 0, 745, + 1055, 1018, 1019, 1020, 953, 1021, 1015, 1016, 954, 1017, + 1056, 1007, 1052, 1053, 982, 1012, 1022, 1051, 1023, 1054, + 983, 1057, 1097, 1098, 1029, 1013, 296, 1099, 1026, 1058, + 1050, 1049, 1024, 1008, 1059, 1060, 990, 985, 1027, 1028, + 1014, 1035, 1036, 1037, 1040, 955, 1041, 1042, 1043, 1044, + 1045, 1039, 1038, 1004, 1005, 1006, 1030, 1031, 1033, 1034, + 1011, 536, 986, 987, 988, 989, 0, 0, 574, 575, + 576, 599, 0, 577, 559, 623, 403, 334, 540, 566, + 762, 763, 0, 0, 0, 0, 0, 0, 0, 674, + 685, 719, 720, 0, 733, 734, 736, 738, 1046, 740, + 533, 534, 748, 0, 0, 427, 349, 350, 482, 483, + 520, 0, 428, 429, 430, 432, 431, 1032, 743, 744, + 741, 457, 519, 541, 527, 761, 469, 0, 769, 614, + 615, 770, 729, 728, 335, 1000, 948, 0, 0, 0, + 0, 0, 0, 0, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 951, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 991, 666, 616, 529, 470, 0, + 683, 0, 0, 1071, 1079, 0, 0, 0, 0, 0, + 0, 0, 0, 1067, 0, 0, 0, 0, 943, 0, + 0, 980, 1048, 1047, 967, 977, 0, 0, 357, 263, + 611, 735, 613, 612, 968, 0, 969, 973, 976, 972, + 970, 971, 0, 1062, 0, 0, 0, 0, 0, 0, + 935, 947, 0, 952, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 944, + 945, 2235, 0, 0, 0, 1001, 0, 946, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 996, 974, 978, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 975, 0, 999, 1003, 379, 1085, 997, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 1086, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 994, 0, 732, 0, 565, 0, + 0, 1069, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 998, 0, 517, 490, 1082, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 1066, 486, 693, 730, 731, 618, 0, + 1081, 1061, 1063, 1064, 1068, 1072, 1073, 1074, 1075, 1076, + 1078, 1080, 1084, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 1083, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 1002, 669, 670, 474, 475, 476, 477, 1070, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 1092, 1065, 1091, 1093, 1094, 1090, 1095, + 1096, 1077, 956, 0, 1009, 1010, 1088, 1087, 1089, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 963, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 1055, + 1018, 1019, 1020, 953, 1021, 1015, 1016, 954, 1017, 1056, + 1007, 1052, 1053, 982, 1012, 1022, 1051, 1023, 1054, 983, + 1057, 1097, 1098, 1029, 1013, 296, 1099, 1026, 1058, 1050, + 1049, 1024, 1008, 1059, 1060, 990, 985, 1027, 1028, 1014, + 1035, 1036, 1037, 1040, 955, 1041, 1042, 1043, 1044, 1045, + 1039, 1038, 1004, 1005, 1006, 1030, 1031, 1033, 1034, 1011, + 536, 986, 987, 988, 989, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 1046, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 1032, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 1000, 948, 0, 0, 0, 0, + 0, 0, 0, 488, 0, 0, 629, 663, 652, 739, + 617, 0, 0, 0, 0, 0, 0, 951, 0, 0, + 0, 386, 0, 0, 452, 667, 648, 659, 649, 494, + 634, 635, 636, 643, 398, 637, 771, 639, 609, 640, + 610, 641, 642, 991, 666, 616, 529, 470, 0, 683, + 0, 0, 1071, 1079, 0, 0, 0, 0, 0, 0, + 0, 0, 1067, 0, 0, 0, 0, 943, 0, 0, + 980, 1048, 1047, 967, 977, 0, 0, 357, 263, 611, + 735, 613, 612, 968, 0, 969, 973, 976, 972, 970, + 971, 0, 1062, 0, 0, 0, 0, 0, 0, 935, + 947, 0, 952, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 944, 945, + 0, 0, 0, 0, 1001, 0, 946, 0, 0, 0, + 0, 0, 530, 558, 0, 571, 0, 424, 425, 996, + 974, 978, 0, 0, 0, 0, 342, 537, 555, 358, + 524, 569, 363, 532, 548, 353, 487, 521, 0, 0, + 344, 553, 531, 466, 343, 0, 514, 383, 400, 380, + 485, 975, 0, 999, 1003, 379, 1085, 997, 563, 346, + 0, 562, 484, 458, 549, 554, 467, 460, 0, 345, + 551, 465, 459, 436, 390, 1086, 437, 438, 439, 440, + 441, 442, 404, 500, 456, 501, 405, 472, 471, 473, + 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, + 0, 0, 0, 0, 0, 593, 594, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 727, 994, 0, 732, 0, 565, 0, 0, + 1069, 0, 0, 0, 535, 0, 0, 443, 0, 0, + 0, 998, 0, 517, 490, 1082, 0, 0, 515, 454, + 550, 502, 556, 538, 564, 509, 503, 336, 539, 382, + 468, 354, 356, 760, 384, 387, 391, 392, 478, 479, + 496, 523, 542, 543, 544, 381, 368, 516, 369, 401, + 370, 337, 373, 372, 374, 525, 375, 339, 497, 547, + 0, 397, 512, 463, 340, 462, 498, 546, 545, 355, + 573, 580, 581, 671, 0, 586, 774, 775, 776, 595, + 0, 504, 351, 348, 0, 0, 0, 377, 499, 364, + 366, 367, 365, 493, 495, 600, 601, 602, 604, 0, + 605, 606, 0, 0, 0, 0, 607, 672, 688, 656, + 625, 588, 680, 622, 626, 627, 418, 419, 420, 421, + 691, 0, 0, 0, 579, 444, 445, 0, 389, 388, + 464, 341, 0, 0, 433, 417, 505, 347, 385, 435, + 423, 446, 447, 448, 449, 450, 0, 451, 395, 331, + 332, 768, 1066, 486, 693, 730, 731, 618, 0, 1081, + 1061, 1063, 1064, 1068, 1072, 1073, 1074, 1075, 1076, 1078, + 1080, 1084, 767, 0, 673, 687, 772, 686, 764, 492, + 0, 522, 684, 631, 0, 677, 650, 651, 0, 678, + 646, 682, 0, 620, 0, 589, 592, 621, 706, 707, + 708, 338, 591, 710, 711, 712, 713, 714, 715, 716, + 709, 1083, 654, 630, 657, 570, 633, 632, 0, 0, + 668, 1002, 669, 670, 474, 475, 476, 477, 1070, 694, + 362, 590, 507, 508, 0, 655, 0, 0, 0, 0, + 0, 0, 0, 0, 660, 661, 658, 777, 0, 717, + 718, 0, 0, 584, 585, 394, 0, 603, 402, 361, + 491, 396, 568, 426, 0, 596, 662, 597, 510, 511, + 721, 726, 722, 723, 725, 746, 481, 416, 422, 526, + 434, 455, 513, 567, 489, 518, 359, 557, 528, 461, + 647, 675, 1092, 1065, 1091, 1093, 1094, 1090, 1095, 1096, + 1077, 956, 0, 1009, 1010, 1088, 1087, 1089, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 702, + 701, 700, 699, 698, 697, 696, 695, 0, 0, 644, + 376, 765, 759, 766, 747, 750, 749, 724, 963, 333, + 624, 453, 506, 393, 689, 690, 0, 745, 1055, 1018, + 1019, 1020, 953, 1021, 1015, 1016, 954, 1017, 1056, 1007, + 1052, 1053, 982, 1012, 1022, 1051, 1023, 1054, 983, 1057, + 1097, 1098, 1029, 1013, 296, 1099, 1026, 1058, 1050, 1049, + 1024, 1008, 1059, 1060, 990, 985, 1027, 1028, 1014, 1035, + 1036, 1037, 1040, 955, 1041, 1042, 1043, 1044, 1045, 1039, + 1038, 1004, 1005, 1006, 1030, 1031, 1033, 1034, 1011, 536, + 986, 987, 988, 989, 0, 0, 574, 575, 576, 599, + 0, 577, 559, 623, 403, 334, 540, 566, 762, 763, + 0, 0, 0, 0, 0, 0, 0, 674, 685, 719, + 720, 0, 733, 734, 736, 738, 1046, 740, 533, 534, + 748, 0, 0, 427, 349, 350, 482, 483, 520, 0, + 428, 429, 430, 432, 431, 1032, 743, 744, 741, 457, + 519, 541, 527, 761, 469, 0, 769, 614, 615, 770, + 729, 728, 335, 1000, 948, 0, 0, 0, 0, 0, + 0, 0, 488, 0, 0, 629, 663, 652, 739, 617, + 0, 0, 0, 0, 0, 0, 951, 0, 0, 0, + 386, 0, 0, 452, 667, 648, 659, 649, 494, 634, + 635, 636, 643, 398, 637, 771, 639, 609, 640, 610, + 641, 642, 991, 666, 616, 529, 470, 0, 683, 0, + 0, 1071, 1079, 0, 0, 0, 0, 0, 0, 0, + 0, 1067, 0, 0, 0, 0, 943, 0, 0, 980, + 1048, 1047, 967, 977, 0, 0, 357, 263, 611, 735, + 613, 612, 968, 0, 969, 973, 976, 972, 970, 971, + 0, 1062, 0, 0, 0, 0, 0, 0, 935, 947, + 0, 952, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 944, 945, 0, + 0, 0, 0, 1001, 0, 946, 0, 0, 0, 0, + 0, 530, 558, 0, 571, 0, 424, 425, 996, 974, + 978, 0, 0, 0, 0, 342, 537, 555, 358, 524, + 569, 363, 532, 548, 353, 487, 521, 0, 0, 344, + 553, 531, 466, 343, 0, 514, 383, 400, 380, 485, + 975, 0, 999, 1003, 379, 1085, 997, 563, 346, 0, + 562, 484, 458, 549, 554, 467, 460, 0, 345, 551, + 465, 459, 436, 390, 1086, 437, 438, 439, 440, 441, + 442, 404, 500, 456, 501, 405, 472, 471, 473, 406, + 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, + 0, 0, 0, 0, 593, 594, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 727, 994, 0, 732, 0, 565, 0, 0, 1069, + 0, 0, 0, 535, 0, 0, 443, 0, 0, 0, + 998, 0, 517, 490, 1082, 0, 0, 515, 454, 550, + 502, 556, 538, 564, 509, 503, 336, 539, 382, 468, + 354, 356, 760, 384, 387, 391, 392, 478, 479, 496, + 523, 542, 543, 544, 381, 368, 516, 369, 401, 370, + 337, 373, 372, 374, 525, 375, 339, 497, 547, 0, + 397, 512, 463, 340, 462, 498, 546, 545, 355, 573, + 580, 581, 671, 0, 586, 774, 775, 776, 595, 0, + 504, 351, 348, 0, 0, 0, 377, 499, 364, 366, + 367, 365, 493, 495, 600, 601, 602, 604, 0, 605, + 606, 0, 0, 0, 0, 607, 672, 688, 656, 625, + 588, 680, 622, 626, 627, 418, 419, 420, 421, 691, + 0, 0, 0, 579, 444, 445, 0, 389, 388, 464, + 341, 0, 0, 433, 417, 505, 347, 385, 435, 423, + 446, 447, 448, 449, 450, 0, 451, 395, 331, 332, + 768, 1066, 486, 693, 730, 731, 618, 0, 1081, 1061, + 1063, 1064, 1068, 1072, 1073, 1074, 1075, 1076, 1078, 1080, + 1084, 767, 0, 673, 687, 772, 686, 764, 492, 0, + 522, 684, 631, 0, 677, 650, 651, 0, 678, 646, + 682, 0, 620, 0, 589, 592, 621, 706, 707, 708, + 338, 591, 710, 711, 712, 713, 714, 715, 716, 709, + 1083, 654, 630, 657, 570, 633, 632, 0, 0, 668, + 1002, 669, 670, 474, 475, 476, 477, 1070, 694, 362, + 590, 507, 508, 0, 655, 0, 0, 0, 0, 0, + 0, 0, 0, 660, 661, 658, 777, 0, 717, 718, + 0, 0, 584, 585, 394, 0, 603, 402, 361, 491, + 396, 568, 426, 0, 596, 662, 597, 510, 511, 721, + 726, 722, 723, 725, 746, 481, 416, 422, 526, 434, + 455, 513, 567, 489, 518, 359, 557, 528, 461, 647, + 675, 1092, 1065, 1091, 1093, 1094, 1090, 1095, 1096, 1077, + 956, 0, 1009, 1010, 1088, 1087, 1089, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 702, 701, + 700, 699, 698, 697, 696, 695, 0, 0, 644, 376, + 765, 759, 766, 747, 750, 749, 724, 963, 333, 624, + 453, 506, 393, 689, 690, 0, 745, 1055, 1018, 1019, + 1020, 953, 1021, 1015, 1016, 954, 1017, 1056, 1007, 1052, + 1053, 982, 1012, 1022, 1051, 1023, 1054, 983, 1057, 1097, + 1098, 1029, 1013, 296, 1099, 1026, 1058, 1050, 1049, 1024, + 1008, 1059, 1060, 990, 985, 1027, 1028, 1014, 1035, 1036, + 1037, 1040, 955, 1041, 1042, 1043, 1044, 1045, 1039, 1038, + 1004, 1005, 1006, 1030, 1031, 1033, 1034, 1011, 536, 986, + 987, 988, 989, 0, 0, 574, 575, 576, 599, 0, + 577, 559, 623, 403, 334, 540, 566, 762, 763, 0, + 0, 0, 0, 0, 0, 0, 674, 685, 719, 720, + 0, 733, 734, 736, 738, 1046, 740, 533, 534, 748, + 0, 0, 427, 349, 350, 482, 483, 520, 0, 428, + 429, 430, 432, 431, 4306, 743, 4307, 4308, 457, 519, + 541, 527, 761, 469, 0, 769, 614, 615, 770, 729, + 728, 335, 1000, 948, 0, 0, 0, 0, 0, 0, + 0, 488, 0, 0, 629, 663, 652, 739, 617, 0, + 0, 0, 0, 0, 0, 951, 0, 0, 0, 386, + 0, 0, 452, 667, 648, 659, 649, 494, 634, 635, + 636, 643, 398, 637, 771, 639, 609, 640, 610, 641, + 642, 991, 666, 616, 529, 470, 0, 683, 0, 0, + 1071, 1079, 0, 0, 0, 0, 0, 0, 0, 0, + 1067, 0, 0, 0, 0, 943, 0, 0, 980, 1048, + 1047, 967, 977, 0, 0, 357, 263, 611, 735, 613, + 612, 3241, 0, 3242, 973, 976, 972, 970, 971, 0, + 1062, 0, 0, 0, 0, 0, 0, 935, 947, 0, + 952, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 944, 945, 0, 0, + 0, 0, 1001, 0, 946, 0, 0, 0, 0, 0, + 530, 558, 0, 571, 0, 424, 425, 996, 974, 978, + 0, 0, 0, 0, 342, 537, 555, 358, 524, 569, + 363, 532, 548, 353, 487, 521, 0, 0, 344, 553, + 531, 466, 343, 0, 514, 383, 400, 380, 485, 975, + 0, 999, 1003, 379, 1085, 997, 563, 346, 0, 562, + 484, 458, 549, 554, 467, 460, 0, 345, 551, 465, + 459, 436, 390, 1086, 437, 438, 439, 440, 441, 442, + 404, 500, 456, 501, 405, 472, 471, 473, 406, 407, + 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, + 0, 0, 0, 593, 594, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 727, 994, 0, 732, 0, 565, 0, 0, 1069, 0, + 0, 0, 535, 0, 0, 443, 0, 0, 0, 998, + 0, 517, 490, 1082, 0, 0, 515, 454, 550, 502, + 556, 538, 564, 509, 503, 336, 539, 382, 468, 354, + 356, 760, 384, 387, 391, 392, 478, 479, 496, 523, + 542, 543, 544, 381, 368, 516, 369, 401, 370, 337, + 373, 372, 374, 525, 375, 339, 497, 547, 0, 397, + 512, 463, 340, 462, 498, 546, 545, 355, 573, 580, + 581, 671, 0, 586, 774, 775, 776, 595, 0, 504, + 351, 348, 0, 0, 0, 377, 499, 364, 366, 367, + 365, 493, 495, 600, 601, 602, 604, 0, 605, 606, + 0, 0, 0, 0, 607, 672, 688, 656, 625, 588, + 680, 622, 626, 627, 418, 419, 420, 421, 691, 0, + 0, 0, 579, 444, 445, 0, 389, 388, 464, 341, + 0, 0, 433, 417, 505, 347, 385, 435, 423, 446, + 447, 448, 449, 450, 0, 451, 395, 331, 332, 768, + 1066, 486, 693, 730, 731, 618, 0, 1081, 1061, 1063, + 1064, 1068, 1072, 1073, 1074, 1075, 1076, 1078, 1080, 1084, + 767, 0, 673, 687, 772, 686, 764, 492, 0, 522, + 684, 631, 0, 677, 650, 651, 0, 678, 646, 682, + 0, 620, 0, 589, 592, 621, 706, 707, 708, 338, + 591, 710, 711, 712, 713, 714, 715, 716, 709, 1083, + 654, 630, 657, 570, 633, 632, 0, 0, 668, 1002, + 669, 670, 474, 475, 476, 477, 1070, 694, 362, 590, + 507, 508, 0, 655, 0, 0, 0, 0, 0, 0, + 0, 0, 660, 661, 658, 777, 0, 717, 718, 0, + 0, 584, 585, 394, 0, 603, 402, 361, 491, 396, + 568, 426, 0, 596, 662, 597, 510, 511, 721, 726, + 722, 723, 725, 746, 481, 416, 422, 526, 434, 455, + 513, 567, 489, 518, 359, 557, 528, 461, 647, 675, + 1092, 1065, 1091, 1093, 1094, 1090, 1095, 1096, 1077, 956, + 0, 1009, 1010, 1088, 1087, 1089, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 702, 701, 700, + 699, 698, 697, 696, 695, 0, 0, 644, 376, 765, + 759, 766, 747, 750, 749, 724, 963, 333, 624, 453, + 506, 393, 689, 690, 0, 745, 1055, 1018, 1019, 1020, + 953, 1021, 1015, 1016, 954, 1017, 1056, 1007, 1052, 1053, + 982, 1012, 1022, 1051, 1023, 1054, 983, 1057, 1097, 1098, + 1029, 1013, 296, 1099, 1026, 1058, 1050, 1049, 1024, 1008, + 1059, 1060, 990, 985, 1027, 1028, 1014, 1035, 1036, 1037, + 1040, 955, 1041, 1042, 1043, 1044, 1045, 1039, 1038, 1004, + 1005, 1006, 1030, 1031, 1033, 1034, 1011, 536, 986, 987, + 988, 989, 0, 0, 574, 575, 576, 599, 0, 577, + 559, 623, 403, 334, 540, 566, 762, 763, 0, 0, + 0, 0, 0, 0, 0, 674, 685, 719, 720, 0, + 733, 734, 736, 738, 1046, 740, 533, 534, 748, 0, + 0, 427, 349, 350, 482, 483, 520, 0, 428, 429, + 430, 432, 431, 1032, 743, 744, 741, 457, 519, 541, + 527, 761, 469, 0, 769, 614, 615, 770, 729, 728, + 335, 1000, 948, 0, 0, 0, 0, 0, 0, 0, + 488, 0, 0, 629, 663, 652, 739, 617, 0, 0, + 2075, 0, 0, 0, 951, 0, 0, 0, 386, 0, + 0, 452, 667, 648, 659, 649, 494, 634, 635, 636, + 643, 398, 637, 771, 639, 609, 640, 610, 641, 642, + 991, 666, 616, 529, 470, 0, 683, 0, 0, 1071, + 1079, 0, 0, 0, 0, 0, 0, 0, 0, 1067, + 0, 0, 0, 0, 943, 0, 0, 980, 1048, 1047, + 967, 977, 0, 0, 357, 263, 611, 735, 613, 612, + 968, 0, 969, 973, 976, 972, 970, 971, 0, 1062, + 0, 0, 0, 0, 0, 0, 0, 947, 0, 952, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 944, 945, 0, 0, 0, + 0, 1001, 0, 946, 0, 0, 0, 0, 0, 530, + 558, 0, 571, 0, 424, 425, 996, 974, 978, 0, + 0, 0, 0, 342, 537, 555, 358, 524, 569, 363, + 532, 548, 353, 487, 521, 0, 0, 344, 553, 531, + 466, 343, 0, 514, 383, 400, 380, 485, 975, 0, + 999, 1003, 379, 1085, 997, 563, 346, 0, 562, 484, + 458, 549, 554, 467, 460, 0, 345, 551, 465, 459, + 436, 390, 1086, 437, 438, 439, 440, 441, 442, 404, + 500, 456, 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 991, 0, 729, 0, 562, 0, 0, 1066, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 995, 0, - 514, 487, 1079, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 1063, 483, 690, 727, 728, - 615, 0, 1078, 1058, 1060, 1061, 1065, 1069, 1070, 1071, - 1072, 1073, 1075, 1077, 1081, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 1080, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 999, 666, 667, 471, 472, 473, - 474, 1067, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 1089, 1062, 1088, 1090, 1091, - 1087, 1092, 1093, 1074, 953, 0, 1006, 1007, 1085, 1084, - 1086, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 960, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 1052, 1015, 1016, 1017, 950, 1018, 1012, 1013, 951, - 1014, 1053, 1004, 1049, 1050, 979, 1009, 1019, 1048, 1020, - 1051, 980, 1054, 1094, 1095, 1026, 1010, 296, 1096, 1023, - 1055, 1047, 1046, 1021, 1005, 1056, 1057, 987, 982, 1024, - 1025, 1011, 1032, 1033, 1034, 1037, 952, 1038, 1039, 1040, - 1041, 1042, 1036, 1035, 1001, 1002, 1003, 1027, 1028, 1030, - 1031, 1008, 533, 983, 984, 985, 986, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 1043, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 1029, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 0, 945, 197, 240, - 196, 229, 198, 0, 0, 0, 0, 0, 0, 485, - 0, 0, 626, 660, 649, 736, 614, 0, 230, 0, - 0, 0, 0, 0, 0, 222, 0, 386, 0, 231, - 449, 664, 645, 656, 646, 491, 631, 632, 633, 640, - 398, 634, 635, 636, 606, 637, 607, 638, 639, 167, - 663, 613, 526, 467, 0, 680, 0, 0, 0, 0, - 0, 0, 0, 0, 152, 0, 0, 0, 0, 0, - 0, 0, 0, 234, 0, 0, 262, 0, 0, 0, - 0, 0, 0, 357, 263, 608, 732, 610, 609, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 254, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 527, 555, - 0, 568, 0, 423, 424, 0, 0, 0, 0, 0, - 0, 0, 342, 534, 552, 358, 521, 566, 363, 529, - 545, 353, 484, 518, 0, 0, 344, 550, 528, 463, - 343, 0, 511, 383, 400, 380, 482, 0, 0, 549, - 579, 379, 569, 0, 560, 346, 0, 559, 481, 455, - 546, 551, 464, 457, 0, 345, 548, 462, 456, 435, - 390, 595, 436, 437, 438, 439, 440, 441, 404, 497, - 453, 498, 405, 469, 468, 470, 406, 407, 408, 409, + 0, 0, 593, 594, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 727, + 994, 0, 732, 0, 565, 0, 0, 1069, 0, 0, + 0, 535, 0, 0, 443, 0, 0, 0, 998, 0, + 517, 490, 1082, 0, 0, 515, 454, 550, 502, 556, + 538, 564, 509, 503, 336, 539, 382, 468, 354, 356, + 760, 384, 387, 391, 392, 478, 479, 496, 523, 542, + 543, 544, 381, 368, 516, 369, 401, 370, 337, 373, + 372, 374, 525, 375, 339, 497, 547, 0, 397, 512, + 463, 340, 462, 498, 546, 545, 355, 573, 2076, 2077, + 671, 0, 586, 774, 775, 776, 595, 0, 504, 351, + 348, 0, 0, 0, 377, 499, 364, 366, 367, 365, + 493, 495, 600, 601, 602, 604, 0, 605, 606, 0, + 0, 0, 0, 607, 672, 688, 656, 625, 588, 680, + 622, 626, 627, 418, 419, 420, 421, 691, 0, 0, + 0, 579, 444, 445, 0, 389, 388, 464, 341, 0, + 0, 433, 417, 505, 347, 385, 435, 423, 446, 447, + 448, 449, 450, 0, 451, 395, 331, 332, 768, 1066, + 486, 693, 730, 731, 618, 0, 1081, 1061, 1063, 1064, + 1068, 1072, 1073, 1074, 1075, 1076, 1078, 1080, 1084, 767, + 0, 673, 687, 772, 686, 764, 492, 0, 522, 684, + 631, 0, 677, 650, 651, 0, 678, 646, 682, 0, + 620, 0, 589, 592, 621, 706, 707, 708, 338, 591, + 710, 711, 712, 713, 714, 715, 716, 709, 1083, 654, + 630, 657, 570, 633, 632, 0, 0, 668, 1002, 669, + 670, 474, 475, 476, 477, 1070, 694, 362, 590, 507, + 508, 0, 655, 0, 0, 0, 0, 0, 0, 0, + 0, 660, 661, 658, 777, 0, 717, 718, 0, 0, + 584, 585, 394, 0, 603, 402, 361, 491, 396, 568, + 426, 0, 596, 662, 597, 510, 511, 721, 726, 722, + 723, 725, 746, 481, 416, 422, 526, 434, 455, 513, + 567, 489, 518, 359, 557, 528, 461, 647, 675, 1092, + 1065, 1091, 1093, 1094, 1090, 1095, 1096, 1077, 956, 0, + 1009, 1010, 1088, 1087, 1089, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 702, 701, 700, 699, + 698, 697, 696, 695, 0, 0, 644, 376, 765, 759, + 766, 747, 750, 749, 724, 963, 333, 624, 453, 506, + 393, 689, 690, 0, 745, 1055, 1018, 1019, 1020, 953, + 1021, 1015, 1016, 954, 1017, 1056, 1007, 1052, 1053, 982, + 1012, 1022, 1051, 1023, 1054, 983, 1057, 1097, 1098, 1029, + 1013, 296, 1099, 1026, 1058, 1050, 1049, 1024, 1008, 1059, + 1060, 990, 985, 1027, 1028, 1014, 1035, 1036, 1037, 1040, + 955, 1041, 1042, 1043, 1044, 1045, 1039, 1038, 1004, 1005, + 1006, 1030, 1031, 1033, 1034, 1011, 536, 986, 987, 988, + 989, 0, 0, 574, 575, 576, 599, 0, 577, 559, + 623, 403, 334, 540, 566, 762, 763, 0, 0, 0, + 0, 0, 0, 0, 674, 685, 719, 720, 0, 733, + 734, 736, 738, 1046, 740, 533, 534, 748, 0, 0, + 427, 349, 350, 482, 483, 520, 0, 428, 429, 430, + 432, 431, 1032, 743, 744, 741, 457, 519, 541, 527, + 761, 469, 0, 769, 614, 615, 770, 729, 728, 335, + 1000, 948, 0, 0, 0, 0, 0, 0, 0, 488, + 0, 0, 629, 663, 652, 739, 617, 0, 0, 0, + 0, 0, 0, 951, 0, 0, 0, 386, 0, 0, + 452, 667, 648, 659, 649, 494, 634, 635, 636, 643, + 398, 637, 771, 639, 609, 640, 610, 641, 642, 991, + 666, 616, 529, 470, 0, 683, 0, 0, 1071, 1079, + 0, 0, 0, 0, 0, 0, 0, 0, 1067, 0, + 0, 0, 0, 1589, 0, 0, 980, 1048, 1047, 967, + 977, 0, 0, 357, 263, 611, 735, 613, 612, 968, + 0, 969, 973, 976, 972, 970, 971, 0, 1062, 0, + 0, 0, 0, 0, 0, 935, 947, 0, 952, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 944, 945, 0, 0, 0, 0, + 1001, 0, 946, 0, 0, 0, 0, 0, 530, 558, + 0, 571, 0, 424, 425, 996, 974, 978, 0, 0, + 0, 0, 342, 537, 555, 358, 524, 569, 363, 532, + 548, 353, 487, 521, 0, 0, 344, 553, 531, 466, + 343, 0, 514, 383, 400, 380, 485, 975, 0, 999, + 1003, 379, 1085, 997, 563, 346, 0, 562, 484, 458, + 549, 554, 467, 460, 0, 345, 551, 465, 459, 436, + 390, 1086, 437, 438, 439, 440, 441, 442, 404, 500, + 456, 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, - 0, 590, 591, 0, 0, 0, 0, 0, 0, 0, - 195, 227, 238, 228, 83, 148, 0, 0, 724, 0, - 0, 729, 0, 562, 0, 0, 255, 0, 0, 0, - 532, 0, 0, 442, 150, 221, 220, 580, 0, 514, - 487, 268, 0, 0, 512, 451, 547, 499, 553, 535, - 561, 506, 500, 336, 536, 382, 465, 354, 356, 277, - 384, 387, 391, 392, 475, 476, 493, 520, 539, 540, - 541, 381, 368, 513, 369, 401, 370, 337, 373, 372, - 374, 522, 375, 339, 494, 544, 149, 397, 509, 460, - 340, 459, 495, 543, 542, 355, 570, 577, 578, 668, - 0, 583, 700, 701, 702, 592, 0, 501, 351, 348, - 0, 0, 0, 377, 496, 364, 366, 367, 365, 490, - 492, 597, 598, 599, 601, 0, 602, 603, 0, 0, - 0, 0, 604, 669, 685, 653, 622, 585, 677, 619, - 623, 624, 418, 419, 420, 688, 0, 0, 0, 576, - 443, 444, 0, 389, 388, 461, 341, 0, 0, 432, - 417, 502, 347, 385, 434, 422, 445, 446, 447, 448, - 395, 331, 332, 557, 378, 483, 690, 727, 728, 615, - 0, 678, 616, 625, 371, 650, 662, 661, 477, 575, - 258, 673, 676, 605, 269, 0, 670, 684, 642, 683, - 270, 489, 0, 519, 681, 628, 0, 674, 647, 648, - 0, 675, 643, 679, 0, 617, 0, 586, 589, 618, - 703, 704, 705, 338, 588, 707, 708, 709, 710, 711, - 712, 713, 706, 558, 651, 627, 654, 567, 630, 629, - 0, 0, 665, 584, 666, 667, 471, 472, 473, 474, - 399, 691, 362, 587, 504, 505, 164, 652, 0, 0, - 0, 0, 0, 0, 0, 0, 657, 658, 655, 267, - 0, 714, 715, 0, 0, 581, 582, 394, 0, 600, - 402, 361, 488, 396, 565, 425, 0, 593, 659, 594, - 507, 508, 718, 723, 719, 720, 722, 743, 478, 416, - 421, 523, 433, 452, 510, 564, 486, 515, 359, 554, - 525, 458, 644, 672, 0, 0, 0, 0, 0, 0, - 0, 0, 78, 0, 0, 319, 320, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 699, 698, 697, 696, 695, 694, 693, 692, 0, - 0, 641, 376, 275, 352, 276, 744, 747, 746, 721, - 0, 333, 621, 450, 503, 393, 686, 687, 72, 742, - 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, - 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, - 301, 302, 303, 304, 689, 295, 296, 305, 306, 307, - 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, - 318, 0, 0, 0, 0, 327, 748, 749, 750, 751, - 752, 0, 0, 328, 329, 330, 0, 0, 293, 294, - 321, 533, 322, 323, 324, 325, 0, 0, 571, 572, - 573, 596, 0, 574, 556, 620, 403, 334, 537, 563, - 271, 272, 55, 256, 259, 261, 260, 0, 73, 671, - 682, 716, 717, 9, 730, 731, 733, 735, 734, 737, - 530, 531, 745, 0, 0, 426, 349, 350, 479, 480, - 517, 0, 427, 428, 429, 431, 430, 739, 740, 741, - 738, 454, 516, 538, 524, 266, 466, 170, 273, 611, - 612, 274, 726, 725, 335, 197, 240, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 1233, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 234, 0, 0, 262, 0, 0, 0, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 2919, 2922, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 1232, 729, 2923, - 562, 0, 0, 0, 2918, 0, 2917, 532, 2915, 2920, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 2921, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 593, 594, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 727, 994, + 0, 732, 0, 565, 0, 0, 1069, 0, 0, 0, + 535, 0, 0, 443, 0, 0, 0, 998, 0, 517, + 490, 1082, 0, 0, 515, 454, 550, 502, 556, 538, + 564, 509, 503, 336, 539, 382, 468, 354, 356, 760, + 384, 387, 391, 392, 478, 479, 496, 523, 542, 543, + 544, 381, 368, 516, 369, 401, 370, 337, 373, 372, + 374, 525, 375, 339, 497, 547, 0, 397, 512, 463, + 340, 462, 498, 546, 545, 355, 573, 580, 581, 671, + 0, 586, 774, 775, 776, 595, 0, 504, 351, 348, + 0, 0, 0, 377, 499, 364, 366, 367, 365, 493, + 495, 600, 601, 602, 604, 0, 605, 606, 0, 0, + 0, 0, 607, 672, 688, 656, 625, 588, 680, 622, + 626, 627, 418, 419, 420, 421, 691, 0, 0, 0, + 579, 444, 445, 0, 389, 388, 464, 341, 0, 0, + 433, 417, 505, 347, 385, 435, 423, 446, 447, 448, + 449, 450, 0, 451, 395, 331, 332, 768, 1066, 486, + 693, 730, 731, 618, 0, 1081, 1061, 1063, 1064, 1068, + 1072, 1073, 1074, 1075, 1076, 1078, 1080, 1084, 767, 0, + 673, 687, 772, 686, 764, 492, 0, 522, 684, 631, + 0, 677, 650, 651, 0, 678, 646, 682, 0, 620, + 0, 589, 592, 621, 706, 707, 708, 338, 591, 710, + 711, 712, 713, 714, 715, 716, 709, 1083, 654, 630, + 657, 570, 633, 632, 0, 0, 668, 1002, 669, 670, + 474, 475, 476, 477, 1070, 694, 362, 590, 507, 508, + 0, 655, 0, 0, 0, 0, 0, 0, 0, 0, + 660, 661, 658, 777, 0, 717, 718, 0, 0, 584, + 585, 394, 0, 603, 402, 361, 491, 396, 568, 426, + 0, 596, 662, 597, 510, 511, 721, 726, 722, 723, + 725, 746, 481, 416, 422, 526, 434, 455, 513, 567, + 489, 518, 359, 557, 528, 461, 647, 675, 1092, 1065, + 1091, 1093, 1094, 1090, 1095, 1096, 1077, 956, 0, 1009, + 1010, 1088, 1087, 1089, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 702, 701, 700, 699, 698, + 697, 696, 695, 0, 0, 644, 376, 765, 759, 766, + 747, 750, 749, 724, 963, 333, 624, 453, 506, 393, + 689, 690, 0, 745, 1055, 1018, 1019, 1020, 953, 1021, + 1015, 1016, 954, 1017, 1056, 1007, 1052, 1053, 982, 1012, + 1022, 1051, 1023, 1054, 983, 1057, 1097, 1098, 1029, 1013, + 296, 1099, 1026, 1058, 1050, 1049, 1024, 1008, 1059, 1060, + 990, 985, 1027, 1028, 1014, 1035, 1036, 1037, 1040, 955, + 1041, 1042, 1043, 1044, 1045, 1039, 1038, 1004, 1005, 1006, + 1030, 1031, 1033, 1034, 1011, 536, 986, 987, 988, 989, + 0, 0, 574, 575, 576, 599, 0, 577, 559, 623, + 403, 334, 540, 566, 762, 763, 0, 0, 0, 0, + 0, 0, 0, 674, 685, 719, 720, 0, 733, 734, + 736, 738, 1046, 740, 533, 534, 748, 0, 0, 427, + 349, 350, 482, 483, 520, 0, 428, 429, 430, 432, + 431, 1032, 743, 744, 741, 457, 519, 541, 527, 761, + 469, 0, 769, 614, 615, 770, 729, 728, 335, 1000, + 948, 0, 0, 0, 0, 0, 0, 0, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 951, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 991, 666, + 616, 529, 470, 0, 683, 0, 0, 1071, 1079, 0, + 0, 0, 0, 0, 0, 0, 0, 1067, 0, 0, + 0, 0, 943, 0, 0, 980, 1048, 1047, 967, 977, + 0, 0, 357, 263, 611, 735, 613, 612, 968, 0, + 969, 973, 976, 972, 970, 971, 0, 1062, 0, 0, + 0, 0, 0, 0, 0, 947, 0, 952, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 944, 945, 0, 0, 0, 0, 1001, + 0, 946, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 996, 974, 978, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 975, 0, 999, 1003, + 379, 1085, 997, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 1086, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 994, 0, + 732, 0, 565, 0, 0, 1069, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 998, 0, 517, 490, + 1082, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 1066, 486, 693, + 730, 731, 618, 0, 1081, 1061, 1063, 1064, 1068, 1072, + 1073, 1074, 1075, 1076, 1078, 1080, 1084, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 1083, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 1002, 669, 670, 474, + 475, 476, 477, 1070, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 1092, 1065, 1091, + 1093, 1094, 1090, 1095, 1096, 1077, 956, 0, 1009, 1010, + 1088, 1087, 1089, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 963, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 1055, 1018, 1019, 1020, 953, 1021, 1015, + 1016, 954, 1017, 1056, 1007, 1052, 1053, 982, 1012, 1022, + 1051, 1023, 1054, 983, 1057, 1097, 1098, 1029, 1013, 296, + 1099, 1026, 1058, 1050, 1049, 1024, 1008, 1059, 1060, 990, + 985, 1027, 1028, 1014, 1035, 1036, 1037, 1040, 955, 1041, + 1042, 1043, 1044, 1045, 1039, 1038, 1004, 1005, 1006, 1030, + 1031, 1033, 1034, 1011, 536, 986, 987, 988, 989, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 1046, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 1032, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 0, 948, + 197, 240, 196, 229, 198, 0, 0, 0, 0, 0, + 0, 488, 0, 0, 629, 663, 652, 739, 617, 0, + 230, 0, 0, 0, 0, 0, 0, 222, 0, 386, + 0, 231, 452, 667, 648, 659, 649, 494, 634, 635, + 636, 643, 398, 637, 638, 639, 609, 640, 610, 641, + 642, 167, 666, 616, 529, 470, 0, 683, 0, 0, + 0, 0, 0, 0, 0, 0, 152, 0, 0, 0, + 0, 0, 0, 0, 0, 234, 0, 0, 262, 0, + 0, 0, 0, 0, 0, 357, 263, 611, 735, 613, + 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 530, 558, 0, 571, 0, 424, 425, 0, 0, 0, + 0, 0, 0, 0, 342, 537, 555, 358, 524, 569, + 363, 532, 548, 353, 487, 521, 0, 0, 344, 553, + 531, 466, 343, 0, 514, 383, 400, 380, 485, 0, + 0, 552, 582, 379, 572, 0, 563, 346, 0, 562, + 484, 458, 549, 554, 467, 460, 0, 345, 551, 465, + 459, 436, 390, 598, 437, 438, 439, 440, 441, 442, + 404, 500, 456, 501, 405, 472, 471, 473, 406, 407, + 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, + 0, 0, 0, 593, 594, 0, 0, 0, 0, 0, + 0, 0, 195, 227, 238, 228, 83, 148, 0, 0, + 727, 0, 0, 732, 0, 565, 0, 0, 255, 0, + 0, 0, 535, 0, 0, 443, 150, 221, 220, 583, + 0, 517, 490, 268, 0, 0, 515, 454, 550, 502, + 556, 538, 564, 509, 503, 336, 539, 382, 468, 354, + 356, 277, 384, 387, 391, 392, 478, 479, 496, 523, + 542, 543, 544, 381, 368, 516, 369, 401, 370, 337, + 373, 372, 374, 525, 375, 339, 497, 547, 149, 397, + 512, 463, 340, 462, 498, 546, 545, 355, 573, 580, + 581, 671, 0, 586, 703, 704, 705, 595, 0, 504, + 351, 348, 0, 0, 0, 377, 499, 364, 366, 367, + 365, 493, 495, 600, 601, 602, 604, 0, 605, 606, + 0, 0, 0, 0, 607, 672, 688, 656, 625, 588, + 680, 622, 626, 627, 418, 419, 420, 421, 691, 0, + 0, 0, 579, 444, 445, 0, 389, 388, 464, 341, + 0, 0, 433, 417, 505, 347, 385, 435, 423, 446, + 447, 448, 449, 450, 0, 451, 395, 331, 332, 560, + 378, 486, 693, 730, 731, 618, 0, 681, 619, 628, + 371, 653, 665, 664, 480, 578, 258, 676, 679, 608, + 269, 0, 673, 687, 645, 686, 270, 492, 0, 522, + 684, 631, 0, 677, 650, 651, 0, 678, 646, 682, + 0, 620, 0, 589, 592, 621, 706, 707, 708, 338, + 591, 710, 711, 712, 713, 714, 715, 716, 709, 561, + 654, 630, 657, 570, 633, 632, 0, 0, 668, 587, + 669, 670, 474, 475, 476, 477, 399, 694, 362, 590, + 507, 508, 164, 655, 0, 0, 0, 0, 0, 0, + 0, 0, 660, 661, 658, 267, 0, 717, 718, 0, + 0, 584, 585, 394, 0, 603, 402, 361, 491, 396, + 568, 426, 0, 596, 662, 597, 510, 511, 721, 726, + 722, 723, 725, 746, 481, 416, 422, 526, 434, 455, + 513, 567, 489, 518, 359, 557, 528, 461, 647, 675, + 0, 0, 0, 0, 0, 0, 0, 0, 78, 0, + 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 702, 701, 700, + 699, 698, 697, 696, 695, 0, 0, 644, 376, 275, + 352, 276, 747, 750, 749, 724, 0, 333, 624, 453, + 506, 393, 689, 690, 72, 745, 278, 279, 280, 281, + 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, + 291, 292, 297, 298, 299, 300, 301, 302, 303, 304, + 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 317, 318, 0, 0, 0, + 0, 327, 751, 752, 753, 754, 755, 0, 0, 328, + 329, 330, 0, 0, 293, 294, 321, 536, 322, 323, + 324, 325, 0, 0, 574, 575, 576, 599, 0, 577, + 559, 623, 403, 334, 540, 566, 271, 272, 55, 256, + 259, 261, 260, 0, 73, 674, 685, 719, 720, 9, + 733, 734, 736, 738, 737, 740, 533, 534, 748, 0, + 0, 427, 349, 350, 482, 483, 520, 0, 428, 429, + 430, 432, 431, 742, 743, 744, 741, 457, 519, 541, + 527, 266, 469, 170, 273, 614, 615, 274, 729, 728, + 335, 197, 240, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 488, 0, 0, 629, 663, 652, 739, 617, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 386, 0, 0, 452, 667, 648, 659, 649, 494, 634, + 635, 636, 643, 398, 637, 771, 639, 609, 640, 610, + 641, 642, 1237, 666, 616, 529, 470, 0, 683, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 234, 0, 0, 262, + 0, 0, 0, 0, 0, 0, 357, 263, 611, 735, + 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 360, 2924, 2927, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 530, 558, 0, 571, 0, 424, 425, 0, 0, + 0, 0, 0, 0, 0, 342, 537, 555, 358, 524, + 569, 363, 532, 548, 353, 487, 521, 0, 0, 344, + 553, 531, 466, 343, 0, 514, 383, 400, 380, 485, + 0, 0, 552, 582, 379, 572, 0, 563, 346, 0, + 562, 484, 458, 549, 554, 467, 460, 0, 345, 551, + 465, 459, 436, 390, 598, 437, 438, 439, 440, 441, + 442, 404, 500, 456, 501, 405, 472, 471, 473, 406, + 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, + 0, 0, 0, 0, 593, 594, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 727, 0, 1236, 732, 2928, 565, 0, 0, 0, + 2923, 0, 2922, 535, 2919, 2925, 443, 0, 0, 0, + 583, 0, 517, 490, 773, 0, 0, 515, 454, 550, + 502, 556, 538, 564, 509, 503, 336, 539, 382, 468, + 354, 356, 760, 384, 387, 391, 392, 478, 479, 496, + 523, 542, 543, 544, 381, 368, 516, 369, 401, 370, + 337, 373, 372, 374, 525, 375, 339, 497, 547, 2926, + 397, 512, 463, 340, 462, 498, 546, 545, 355, 573, + 580, 581, 671, 0, 586, 774, 775, 776, 595, 0, + 504, 351, 348, 0, 0, 0, 377, 499, 364, 366, + 367, 365, 493, 495, 600, 601, 602, 604, 0, 605, + 606, 0, 0, 0, 0, 607, 672, 688, 656, 625, + 588, 680, 622, 626, 627, 418, 419, 420, 421, 691, + 0, 0, 0, 579, 444, 445, 0, 389, 388, 464, + 341, 0, 0, 433, 417, 505, 347, 385, 435, 423, + 446, 447, 448, 449, 450, 2920, 451, 395, 331, 332, + 768, 378, 486, 693, 730, 731, 618, 0, 681, 619, + 628, 371, 653, 665, 664, 480, 578, 0, 676, 679, + 608, 767, 0, 673, 687, 772, 686, 764, 492, 0, + 522, 684, 631, 0, 677, 650, 651, 0, 678, 646, + 682, 0, 620, 0, 589, 592, 621, 706, 707, 708, + 338, 591, 710, 711, 712, 713, 714, 715, 716, 709, + 561, 654, 630, 657, 570, 633, 632, 0, 0, 668, + 587, 669, 670, 474, 475, 476, 477, 399, 694, 362, + 590, 507, 508, 0, 655, 0, 0, 0, 0, 0, + 0, 0, 0, 660, 661, 658, 777, 0, 717, 718, + 0, 0, 584, 585, 394, 0, 603, 402, 361, 491, + 396, 568, 426, 0, 596, 662, 597, 510, 511, 721, + 726, 722, 723, 725, 746, 481, 416, 422, 526, 434, + 455, 513, 567, 489, 518, 359, 557, 528, 461, 647, + 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, + 0, 0, 0, 0, 0, 0, 0, 0, 702, 701, + 700, 699, 698, 697, 696, 695, 0, 0, 644, 376, + 765, 759, 766, 747, 750, 749, 724, 0, 333, 624, + 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, + 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1606, 0, 0, 262, - 0, 0, 964, 974, 0, 0, 357, 263, 608, 732, - 610, 609, 965, 0, 966, 970, 973, 969, 967, 968, + 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, + 328, 329, 330, 0, 0, 293, 294, 321, 536, 322, + 323, 324, 325, 0, 0, 574, 575, 576, 599, 0, + 577, 559, 623, 403, 334, 540, 566, 762, 763, 0, + 0, 0, 0, 0, 0, 0, 674, 685, 719, 720, + 0, 733, 734, 736, 738, 737, 740, 533, 534, 748, + 0, 0, 427, 349, 350, 482, 483, 520, 0, 428, + 429, 430, 432, 431, 742, 743, 744, 741, 457, 519, + 541, 527, 761, 469, 0, 769, 614, 615, 770, 729, + 728, 335, 488, 0, 0, 629, 663, 652, 739, 617, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 386, 0, 0, 452, 667, 648, 659, 649, 494, 634, + 635, 636, 643, 398, 637, 771, 639, 609, 640, 610, + 641, 642, 0, 666, 616, 529, 470, 0, 683, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1610, 0, 0, 262, + 0, 0, 967, 977, 0, 0, 357, 263, 611, 735, + 613, 612, 968, 0, 969, 973, 976, 972, 970, 971, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 971, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 972, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, + 0, 530, 558, 0, 571, 0, 424, 425, 0, 974, + 0, 0, 0, 0, 0, 342, 537, 555, 358, 524, + 569, 363, 532, 548, 353, 487, 521, 0, 0, 344, + 553, 531, 466, 343, 0, 514, 383, 400, 380, 485, + 975, 0, 552, 582, 379, 572, 0, 563, 346, 0, + 562, 484, 458, 549, 554, 467, 460, 0, 345, 551, + 465, 459, 436, 390, 598, 437, 438, 439, 440, 441, + 442, 404, 500, 456, 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, + 0, 0, 0, 0, 593, 594, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 727, 0, 0, 732, 0, 565, 0, 0, 0, + 0, 0, 0, 535, 0, 0, 443, 0, 0, 0, + 583, 0, 517, 490, 773, 0, 0, 515, 454, 550, + 502, 556, 538, 564, 509, 503, 336, 539, 382, 468, + 354, 356, 760, 384, 387, 391, 392, 478, 479, 496, + 523, 542, 543, 544, 381, 368, 516, 369, 401, 370, + 337, 373, 372, 374, 525, 375, 339, 497, 547, 0, + 397, 512, 463, 340, 462, 498, 546, 545, 355, 573, + 580, 581, 671, 0, 586, 774, 775, 776, 595, 0, + 504, 351, 348, 0, 0, 0, 377, 499, 364, 366, + 367, 365, 493, 495, 600, 601, 602, 604, 0, 605, + 606, 0, 0, 0, 0, 607, 672, 688, 656, 625, + 588, 680, 622, 626, 627, 418, 419, 420, 421, 691, + 0, 0, 0, 579, 444, 445, 0, 389, 388, 464, + 341, 0, 0, 433, 417, 505, 347, 385, 435, 423, + 446, 447, 448, 449, 450, 0, 451, 395, 331, 332, + 768, 378, 486, 693, 730, 731, 618, 0, 681, 619, + 628, 371, 653, 665, 664, 480, 578, 0, 676, 679, + 608, 767, 0, 673, 687, 772, 686, 764, 492, 0, + 522, 684, 631, 0, 677, 650, 651, 0, 678, 646, + 682, 0, 620, 0, 589, 592, 621, 706, 707, 708, + 338, 591, 710, 711, 712, 713, 714, 715, 716, 709, + 561, 654, 630, 657, 570, 633, 632, 0, 0, 668, + 587, 669, 670, 474, 475, 476, 477, 399, 694, 362, + 590, 507, 508, 0, 655, 0, 0, 0, 0, 0, + 0, 0, 0, 660, 661, 658, 777, 0, 717, 718, + 0, 0, 584, 585, 394, 0, 603, 402, 361, 491, + 396, 568, 426, 0, 596, 662, 597, 510, 511, 721, + 726, 722, 723, 725, 746, 481, 416, 422, 526, 434, + 455, 513, 567, 489, 518, 359, 557, 528, 461, 647, + 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 702, 701, + 700, 699, 698, 697, 696, 695, 0, 0, 644, 376, + 765, 759, 766, 747, 750, 749, 724, 0, 333, 624, + 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, + 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, + 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, + 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, + 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, + 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, + 328, 329, 330, 0, 0, 293, 294, 321, 536, 322, + 323, 324, 325, 0, 0, 574, 575, 576, 599, 0, + 577, 559, 623, 403, 334, 540, 566, 762, 763, 0, + 0, 0, 0, 0, 0, 0, 674, 685, 719, 720, + 0, 733, 734, 736, 738, 737, 740, 533, 534, 748, + 0, 0, 427, 349, 350, 482, 483, 520, 0, 428, + 429, 430, 432, 431, 742, 743, 744, 741, 457, 519, + 541, 527, 761, 469, 0, 769, 614, 615, 770, 729, + 728, 335, 197, 240, 196, 229, 198, 0, 0, 0, + 0, 0, 0, 488, 798, 0, 629, 663, 652, 739, + 617, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 386, 0, 0, 452, 667, 648, 659, 649, 494, + 634, 635, 636, 643, 398, 637, 771, 639, 609, 640, + 610, 641, 642, 0, 666, 616, 529, 470, 0, 683, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 805, + 0, 0, 0, 0, 0, 0, 0, 804, 0, 0, + 262, 0, 0, 0, 0, 0, 0, 357, 263, 611, + 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 530, 558, 0, 571, 0, 424, 425, 0, + 0, 0, 0, 0, 0, 0, 342, 537, 555, 358, + 524, 569, 363, 532, 548, 353, 487, 521, 0, 0, + 344, 553, 531, 466, 343, 0, 514, 383, 400, 380, + 485, 0, 0, 552, 582, 379, 572, 0, 563, 346, + 0, 562, 484, 458, 549, 554, 467, 460, 0, 345, + 551, 465, 459, 436, 390, 598, 437, 438, 439, 440, + 441, 442, 404, 500, 456, 501, 405, 472, 471, 473, + 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, + 0, 0, 0, 0, 0, 593, 594, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 802, + 803, 0, 727, 0, 0, 732, 0, 565, 0, 0, + 0, 0, 0, 0, 535, 0, 0, 443, 0, 0, + 0, 583, 0, 517, 490, 773, 0, 0, 515, 454, + 550, 502, 556, 538, 564, 509, 503, 336, 539, 382, + 468, 354, 356, 760, 384, 387, 391, 392, 478, 479, + 496, 523, 542, 543, 544, 381, 368, 516, 369, 401, + 370, 337, 373, 372, 374, 525, 375, 339, 497, 547, + 0, 397, 512, 463, 340, 462, 498, 546, 545, 355, + 573, 580, 581, 671, 0, 586, 774, 775, 776, 595, + 0, 504, 351, 348, 0, 0, 0, 377, 499, 364, + 366, 367, 365, 493, 495, 600, 601, 602, 604, 0, + 605, 606, 0, 0, 0, 0, 607, 672, 688, 656, + 625, 588, 680, 622, 626, 627, 418, 419, 420, 421, + 691, 0, 0, 0, 579, 444, 445, 0, 389, 388, + 464, 341, 0, 0, 433, 417, 505, 347, 385, 435, + 423, 446, 447, 448, 449, 450, 0, 451, 395, 331, + 332, 768, 378, 486, 693, 730, 731, 618, 0, 681, + 619, 628, 371, 653, 665, 664, 480, 578, 0, 676, + 679, 608, 767, 0, 673, 687, 772, 686, 764, 492, + 0, 522, 684, 631, 0, 677, 650, 651, 0, 678, + 646, 682, 0, 620, 0, 589, 592, 621, 706, 707, + 708, 338, 591, 710, 711, 712, 713, 714, 715, 716, + 709, 561, 654, 630, 657, 570, 633, 632, 0, 0, + 668, 587, 669, 670, 474, 475, 476, 477, 799, 801, + 362, 590, 507, 508, 814, 655, 0, 0, 0, 0, + 0, 0, 0, 0, 660, 661, 658, 777, 0, 717, + 718, 0, 0, 584, 585, 394, 0, 603, 402, 361, + 491, 396, 568, 426, 0, 596, 662, 597, 510, 511, + 721, 726, 722, 723, 725, 746, 481, 416, 422, 526, + 434, 455, 513, 567, 489, 518, 359, 557, 528, 461, + 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, + 78, 0, 0, 319, 320, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 702, + 701, 700, 699, 698, 697, 696, 695, 0, 0, 644, + 376, 765, 759, 766, 747, 750, 749, 724, 0, 333, + 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, + 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, + 289, 290, 291, 292, 297, 298, 299, 300, 301, 302, + 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, 318, 0, + 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, + 0, 328, 329, 330, 0, 0, 293, 294, 321, 536, + 322, 323, 324, 325, 0, 0, 574, 575, 576, 599, + 0, 577, 559, 623, 403, 334, 540, 566, 762, 763, + 0, 0, 0, 0, 0, 0, 0, 674, 685, 719, + 720, 0, 733, 734, 736, 738, 737, 740, 533, 534, + 748, 0, 0, 427, 349, 350, 482, 483, 520, 0, + 428, 429, 430, 432, 431, 742, 743, 744, 741, 457, + 519, 541, 527, 761, 469, 0, 769, 614, 615, 770, + 729, 728, 335, 488, 0, 0, 629, 663, 652, 739, + 617, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 386, 0, 0, 452, 667, 648, 659, 649, 494, + 634, 635, 636, 643, 398, 637, 771, 639, 609, 640, + 610, 641, 642, 0, 666, 616, 529, 470, 0, 683, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 262, 0, 0, 0, 0, 0, 0, 357, 263, 611, + 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 360, 2924, 2927, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 530, 558, 0, 571, 0, 424, 425, 0, + 0, 0, 0, 0, 0, 0, 342, 537, 555, 358, + 524, 569, 363, 532, 548, 353, 487, 521, 0, 0, + 344, 553, 531, 466, 343, 0, 514, 383, 400, 380, + 485, 0, 0, 552, 582, 379, 572, 0, 563, 346, + 0, 562, 484, 458, 549, 554, 467, 460, 0, 345, + 551, 465, 459, 436, 390, 598, 437, 438, 439, 440, + 441, 442, 404, 500, 456, 501, 405, 472, 471, 473, + 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, + 0, 0, 0, 0, 0, 593, 594, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 727, 0, 0, 732, 2928, 565, 0, 0, + 0, 2923, 0, 2922, 535, 2919, 2925, 443, 0, 0, + 0, 583, 0, 517, 490, 773, 0, 0, 515, 454, + 550, 502, 556, 538, 564, 509, 503, 336, 539, 382, + 468, 354, 356, 760, 384, 387, 391, 392, 478, 479, + 496, 523, 542, 543, 544, 381, 368, 516, 369, 401, + 370, 337, 373, 372, 374, 525, 375, 339, 497, 547, + 2926, 397, 512, 463, 340, 462, 498, 546, 545, 355, + 573, 580, 581, 671, 0, 586, 774, 775, 776, 595, + 0, 504, 351, 348, 0, 0, 0, 377, 499, 364, + 366, 367, 365, 493, 495, 600, 601, 602, 604, 0, + 605, 606, 0, 0, 0, 0, 607, 672, 688, 656, + 625, 588, 680, 622, 626, 627, 418, 419, 420, 421, + 691, 0, 0, 0, 579, 444, 445, 0, 389, 388, + 464, 341, 0, 0, 433, 417, 505, 347, 385, 435, + 423, 446, 447, 448, 449, 450, 2920, 451, 395, 331, + 332, 768, 378, 486, 693, 730, 731, 618, 0, 681, + 619, 628, 371, 653, 665, 664, 480, 578, 0, 676, + 679, 608, 767, 0, 673, 687, 772, 686, 764, 492, + 0, 522, 684, 631, 0, 677, 650, 651, 0, 678, + 646, 682, 0, 620, 0, 589, 592, 621, 706, 707, + 708, 338, 591, 710, 711, 712, 713, 714, 715, 716, + 709, 561, 654, 630, 657, 570, 633, 632, 0, 0, + 668, 587, 669, 670, 474, 475, 476, 477, 399, 694, + 362, 590, 507, 508, 0, 655, 0, 0, 0, 0, + 0, 0, 0, 0, 660, 661, 658, 777, 0, 717, + 718, 0, 0, 584, 585, 394, 0, 603, 402, 361, + 491, 396, 568, 426, 0, 596, 662, 597, 510, 511, + 721, 726, 722, 723, 725, 746, 481, 416, 422, 526, + 434, 455, 513, 567, 489, 518, 359, 557, 528, 461, + 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 702, + 701, 700, 699, 698, 697, 696, 695, 0, 0, 644, + 376, 765, 759, 766, 747, 750, 749, 724, 0, 333, + 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, + 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, + 289, 290, 291, 292, 297, 298, 299, 300, 301, 302, + 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, 318, 0, + 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, + 0, 328, 329, 330, 0, 0, 293, 294, 321, 536, + 322, 323, 324, 325, 0, 0, 574, 575, 576, 599, + 0, 577, 559, 623, 403, 334, 540, 566, 762, 763, + 0, 0, 0, 0, 0, 0, 0, 674, 685, 719, + 720, 0, 733, 734, 736, 738, 737, 740, 533, 534, + 748, 0, 0, 427, 349, 350, 482, 483, 520, 0, + 428, 429, 430, 432, 431, 742, 743, 744, 741, 457, + 519, 541, 527, 761, 469, 0, 769, 614, 615, 770, + 729, 728, 335, 488, 0, 0, 629, 663, 652, 739, + 617, 0, 1315, 0, 0, 0, 0, 0, 0, 0, + 0, 386, 0, 0, 452, 667, 648, 659, 649, 494, + 634, 635, 636, 643, 398, 637, 771, 639, 609, 640, + 610, 641, 642, 0, 666, 616, 529, 470, 0, 683, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 262, 0, 0, 0, 0, 0, 0, 357, 263, 611, + 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 530, 558, 0, 571, 0, 3337, 3338, 1295, + 0, 0, 0, 0, 0, 0, 342, 537, 555, 358, + 524, 569, 363, 532, 548, 353, 487, 521, 0, 0, + 3331, 3334, 3335, 3336, 3339, 0, 3344, 3340, 3341, 3342, + 3343, 0, 0, 3327, 3328, 3329, 3330, 1293, 3306, 3332, + 0, 3307, 484, 3308, 3309, 3310, 3311, 3312, 1298, 3313, + 3314, 3315, 3316, 3317, 3324, 3325, 3318, 3319, 3320, 3321, + 3322, 3323, 3345, 3346, 3347, 3348, 3349, 3350, 3351, 3352, + 3354, 3353, 3355, 3356, 3357, 3358, 3359, 3360, 3361, 3362, + 1326, 1328, 1330, 1332, 1335, 593, 594, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 727, 0, 0, 732, 0, 565, 0, 0, + 0, 0, 0, 0, 535, 0, 0, 443, 0, 0, + 0, 3326, 0, 517, 490, 773, 0, 0, 515, 454, + 550, 502, 556, 538, 564, 509, 503, 336, 539, 382, + 468, 354, 356, 760, 384, 387, 391, 392, 478, 479, + 496, 523, 542, 543, 544, 381, 368, 516, 369, 401, + 370, 337, 373, 372, 374, 525, 375, 339, 497, 547, + 0, 397, 512, 463, 340, 462, 498, 546, 545, 355, + 573, 580, 581, 671, 0, 586, 774, 775, 776, 595, + 0, 504, 351, 348, 0, 0, 0, 377, 499, 364, + 366, 367, 365, 493, 495, 600, 601, 602, 604, 0, + 605, 606, 0, 0, 0, 0, 607, 672, 688, 656, + 625, 588, 680, 622, 626, 627, 418, 419, 420, 421, + 691, 0, 0, 0, 579, 444, 445, 0, 389, 388, + 464, 341, 0, 0, 433, 417, 505, 347, 385, 435, + 423, 446, 447, 448, 449, 450, 0, 451, 395, 331, + 332, 768, 378, 486, 693, 730, 731, 618, 0, 681, + 619, 628, 371, 653, 665, 664, 480, 578, 0, 676, + 679, 608, 767, 0, 673, 687, 772, 686, 764, 492, + 0, 522, 684, 631, 0, 677, 650, 651, 0, 678, + 646, 682, 0, 620, 0, 589, 592, 621, 706, 707, + 708, 338, 591, 710, 711, 712, 713, 714, 715, 716, + 709, 561, 654, 630, 657, 570, 633, 632, 0, 0, + 668, 587, 669, 670, 474, 475, 476, 477, 399, 694, + 362, 590, 507, 508, 0, 655, 0, 0, 0, 0, + 0, 0, 0, 0, 660, 661, 658, 777, 0, 717, + 718, 0, 0, 584, 585, 394, 0, 603, 402, 361, + 491, 396, 568, 426, 0, 596, 662, 597, 510, 511, + 721, 726, 722, 723, 725, 746, 481, 416, 422, 526, + 434, 455, 513, 567, 489, 518, 359, 557, 528, 461, + 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 702, + 701, 700, 699, 698, 697, 696, 695, 0, 0, 644, + 376, 765, 759, 766, 747, 750, 749, 724, 0, 333, + 3333, 453, 506, 393, 689, 690, 0, 745, 278, 279, + 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, + 289, 290, 291, 292, 297, 298, 299, 300, 301, 302, + 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 315, 316, 317, 318, 0, + 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, + 0, 328, 329, 330, 0, 0, 293, 294, 321, 536, + 322, 323, 324, 325, 0, 0, 574, 575, 576, 599, + 0, 577, 559, 623, 403, 334, 540, 566, 762, 763, + 0, 0, 0, 0, 0, 0, 0, 674, 685, 719, + 720, 0, 733, 734, 736, 738, 737, 740, 533, 534, + 748, 0, 0, 427, 349, 350, 482, 483, 520, 0, + 428, 429, 430, 432, 431, 742, 743, 744, 741, 457, + 519, 541, 527, 761, 469, 0, 769, 614, 615, 770, + 729, 728, 3305, 197, 240, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 1237, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1766, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, - 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 197, 240, - 196, 229, 198, 0, 0, 0, 0, 0, 0, 485, - 795, 0, 626, 660, 649, 736, 614, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 386, 0, 0, - 449, 664, 645, 656, 646, 491, 631, 632, 633, 640, - 398, 634, 768, 636, 606, 637, 607, 638, 639, 0, - 663, 613, 526, 467, 0, 680, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 802, 0, 0, 0, 0, - 0, 0, 0, 801, 0, 0, 262, 0, 0, 0, - 0, 0, 0, 357, 263, 608, 732, 610, 609, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 527, 555, - 0, 568, 0, 423, 424, 0, 0, 0, 0, 0, - 0, 0, 342, 534, 552, 358, 521, 566, 363, 529, - 545, 353, 484, 518, 0, 0, 344, 550, 528, 463, - 343, 0, 511, 383, 400, 380, 482, 0, 0, 549, - 579, 379, 569, 0, 560, 346, 0, 559, 481, 455, - 546, 551, 464, 457, 0, 345, 548, 462, 456, 435, - 390, 595, 436, 437, 438, 439, 440, 441, 404, 497, - 453, 498, 405, 469, 468, 470, 406, 407, 408, 409, - 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, - 0, 590, 591, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 799, 800, 0, 724, 0, - 0, 729, 0, 562, 0, 0, 0, 0, 0, 0, - 532, 0, 0, 442, 0, 0, 0, 580, 0, 514, - 487, 770, 0, 0, 512, 451, 547, 499, 553, 535, - 561, 506, 500, 336, 536, 382, 465, 354, 356, 757, - 384, 387, 391, 392, 475, 476, 493, 520, 539, 540, - 541, 381, 368, 513, 369, 401, 370, 337, 373, 372, - 374, 522, 375, 339, 494, 544, 0, 397, 509, 460, - 340, 459, 495, 543, 542, 355, 570, 577, 578, 668, - 0, 583, 771, 772, 773, 592, 0, 501, 351, 348, - 0, 0, 0, 377, 496, 364, 366, 367, 365, 490, - 492, 597, 598, 599, 601, 0, 602, 603, 0, 0, - 0, 0, 604, 669, 685, 653, 622, 585, 677, 619, - 623, 624, 418, 419, 420, 688, 0, 0, 0, 576, - 443, 444, 0, 389, 388, 461, 341, 0, 0, 432, - 417, 502, 347, 385, 434, 422, 445, 446, 447, 448, - 395, 331, 332, 765, 378, 483, 690, 727, 728, 615, - 0, 678, 616, 625, 371, 650, 662, 661, 477, 575, - 0, 673, 676, 605, 764, 0, 670, 684, 769, 683, - 761, 489, 0, 519, 681, 628, 0, 674, 647, 648, - 0, 675, 643, 679, 0, 617, 0, 586, 589, 618, - 703, 704, 705, 338, 588, 707, 708, 709, 710, 711, - 712, 713, 706, 558, 651, 627, 654, 567, 630, 629, - 0, 0, 665, 584, 666, 667, 471, 472, 473, 474, - 796, 798, 362, 587, 504, 505, 811, 652, 0, 0, - 0, 0, 0, 0, 0, 0, 657, 658, 655, 774, - 0, 714, 715, 0, 0, 581, 582, 394, 0, 600, - 402, 361, 488, 396, 565, 425, 0, 593, 659, 594, - 507, 508, 718, 723, 719, 720, 722, 743, 478, 416, - 421, 523, 433, 452, 510, 564, 486, 515, 359, 554, - 525, 458, 644, 672, 0, 0, 0, 0, 0, 0, - 0, 0, 78, 0, 0, 319, 320, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 699, 698, 697, 696, 695, 694, 693, 692, 0, - 0, 641, 376, 762, 756, 763, 744, 747, 746, 721, - 0, 333, 621, 450, 503, 393, 686, 687, 0, 742, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 1236, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 2365, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 2945, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 2944, 565, 0, + 0, 0, 2950, 2947, 2949, 535, 0, 2948, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 2942, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 2945, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 2944, 565, 0, + 0, 0, 2950, 2947, 2949, 535, 0, 2948, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 197, 240, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 488, 0, 0, 629, 663, + 652, 739, 617, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 386, 0, 0, 452, 667, 648, 659, + 649, 494, 634, 635, 636, 643, 398, 637, 771, 639, + 609, 640, 610, 641, 642, 1237, 666, 616, 529, 470, + 0, 683, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, + 3097, 0, 262, 0, 0, 0, 0, 0, 0, 357, + 263, 611, 735, 613, 612, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 530, 558, 0, 571, 0, 424, + 425, 0, 0, 0, 0, 0, 0, 0, 342, 537, + 555, 358, 524, 569, 363, 532, 548, 353, 487, 521, + 0, 0, 344, 553, 531, 466, 343, 0, 514, 383, + 400, 380, 485, 0, 0, 552, 582, 379, 572, 0, + 563, 346, 0, 562, 484, 458, 549, 554, 467, 460, + 0, 345, 551, 465, 459, 436, 390, 598, 437, 438, + 439, 440, 441, 442, 404, 500, 456, 501, 405, 472, + 471, 473, 406, 407, 408, 409, 410, 411, 412, 413, + 414, 415, 0, 0, 0, 0, 0, 593, 594, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 727, 0, 1236, 732, 0, 565, + 0, 0, 0, 0, 0, 0, 535, 0, 0, 443, + 0, 0, 0, 583, 0, 517, 490, 773, 0, 0, + 515, 454, 550, 502, 556, 538, 564, 509, 503, 336, + 539, 382, 468, 354, 356, 760, 384, 387, 391, 392, + 478, 479, 496, 523, 542, 543, 544, 381, 368, 516, + 369, 401, 370, 337, 373, 372, 374, 525, 375, 339, + 497, 547, 0, 397, 512, 463, 340, 462, 498, 546, + 545, 355, 573, 580, 581, 671, 0, 586, 774, 775, + 776, 595, 0, 504, 351, 348, 0, 0, 0, 377, + 499, 364, 366, 367, 365, 493, 495, 600, 601, 602, + 604, 0, 605, 606, 0, 0, 0, 0, 607, 672, + 688, 656, 625, 588, 680, 622, 626, 627, 418, 419, + 420, 421, 691, 0, 0, 0, 579, 444, 445, 0, + 389, 388, 464, 341, 0, 0, 433, 417, 505, 347, + 385, 435, 423, 446, 447, 448, 449, 450, 0, 451, + 395, 331, 332, 768, 378, 486, 693, 730, 731, 618, + 0, 681, 619, 628, 371, 653, 665, 664, 480, 578, + 0, 676, 679, 608, 767, 0, 673, 687, 772, 686, + 764, 492, 0, 522, 684, 631, 0, 677, 650, 651, + 0, 678, 646, 682, 0, 620, 0, 589, 592, 621, + 706, 707, 708, 338, 591, 710, 711, 712, 713, 714, + 715, 716, 709, 561, 654, 630, 657, 570, 633, 632, + 0, 0, 668, 587, 669, 670, 474, 475, 476, 477, + 399, 694, 362, 590, 507, 508, 0, 655, 0, 0, + 0, 0, 0, 0, 0, 0, 660, 661, 658, 777, + 0, 717, 718, 0, 0, 584, 585, 394, 0, 603, + 402, 361, 491, 396, 568, 426, 0, 596, 662, 597, + 510, 511, 721, 726, 722, 723, 725, 746, 481, 416, + 422, 526, 434, 455, 513, 567, 489, 518, 359, 557, + 528, 461, 647, 675, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 702, 701, 700, 699, 698, 697, 696, 695, 0, + 0, 644, 376, 765, 759, 766, 747, 750, 749, 724, + 0, 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, - 301, 302, 303, 304, 689, 295, 296, 305, 306, 307, + 301, 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, - 318, 0, 0, 0, 0, 327, 748, 749, 750, 751, - 752, 0, 0, 328, 329, 330, 0, 0, 293, 294, - 321, 533, 322, 323, 324, 325, 0, 0, 571, 572, - 573, 596, 0, 574, 556, 620, 403, 334, 537, 563, - 759, 760, 0, 0, 0, 0, 0, 0, 0, 671, - 682, 716, 717, 0, 730, 731, 733, 735, 734, 737, - 530, 531, 745, 0, 0, 426, 349, 350, 479, 480, - 517, 0, 427, 428, 429, 431, 430, 739, 740, 741, - 738, 454, 516, 538, 524, 758, 466, 0, 766, 611, - 612, 767, 726, 725, 335, 485, 0, 0, 626, 660, - 649, 736, 614, 0, 1311, 0, 0, 0, 0, 0, - 0, 0, 0, 386, 0, 0, 449, 664, 645, 656, - 646, 491, 631, 632, 633, 640, 398, 634, 768, 636, - 606, 637, 607, 638, 639, 0, 663, 613, 526, 467, - 0, 680, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 262, 0, 0, 0, 0, 0, 0, 357, - 263, 608, 732, 610, 609, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, + 318, 0, 0, 0, 0, 327, 751, 752, 753, 754, + 755, 0, 0, 328, 329, 330, 0, 0, 293, 294, + 321, 536, 322, 323, 324, 325, 0, 0, 574, 575, + 576, 599, 0, 577, 559, 623, 403, 334, 540, 566, + 762, 763, 0, 0, 0, 0, 0, 0, 0, 674, + 685, 719, 720, 0, 733, 734, 736, 738, 737, 740, + 533, 534, 748, 0, 0, 427, 349, 350, 482, 483, + 520, 0, 428, 429, 430, 432, 431, 742, 743, 744, + 741, 457, 519, 541, 527, 761, 469, 0, 769, 614, + 615, 770, 729, 728, 335, 197, 240, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 488, 0, 0, 629, + 663, 652, 739, 617, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 386, 0, 0, 452, 667, 648, + 659, 649, 494, 634, 635, 636, 643, 398, 637, 771, + 639, 609, 640, 610, 641, 642, 1237, 666, 616, 529, + 470, 0, 683, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 234, 2850, 0, 262, 0, 0, 0, 0, 0, 0, + 357, 263, 611, 735, 613, 612, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 527, 555, 0, 568, 0, 3332, - 3333, 1291, 0, 0, 0, 0, 0, 0, 342, 534, - 552, 358, 521, 566, 363, 529, 545, 353, 484, 518, - 0, 0, 3326, 3329, 3330, 3331, 3334, 0, 3339, 3335, - 3336, 3337, 3338, 0, 0, 3322, 3323, 3324, 3325, 1289, - 3301, 3327, 0, 3302, 481, 3303, 3304, 3305, 3306, 3307, - 1294, 3308, 3309, 3310, 3311, 3312, 3319, 3320, 3313, 3314, - 3315, 3316, 3317, 3318, 3340, 3341, 3342, 3343, 3344, 3345, - 3346, 3347, 3349, 3348, 3350, 3351, 3352, 3353, 3354, 3355, - 3356, 3357, 1322, 1324, 1326, 1328, 1331, 590, 591, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 724, 0, 0, 729, 0, 562, - 0, 0, 0, 0, 0, 0, 532, 0, 0, 442, - 0, 0, 0, 3321, 0, 514, 487, 770, 0, 0, - 512, 451, 547, 499, 553, 535, 561, 506, 500, 336, - 536, 382, 465, 354, 356, 757, 384, 387, 391, 392, - 475, 476, 493, 520, 539, 540, 541, 381, 368, 513, - 369, 401, 370, 337, 373, 372, 374, 522, 375, 339, - 494, 544, 0, 397, 509, 460, 340, 459, 495, 543, - 542, 355, 570, 577, 578, 668, 0, 583, 771, 772, - 773, 592, 0, 501, 351, 348, 0, 0, 0, 377, - 496, 364, 366, 367, 365, 490, 492, 597, 598, 599, - 601, 0, 602, 603, 0, 0, 0, 0, 604, 669, - 685, 653, 622, 585, 677, 619, 623, 624, 418, 419, - 420, 688, 0, 0, 0, 576, 443, 444, 0, 389, - 388, 461, 341, 0, 0, 432, 417, 502, 347, 385, - 434, 422, 445, 446, 447, 448, 395, 331, 332, 765, - 378, 483, 690, 727, 728, 615, 0, 678, 616, 625, - 371, 650, 662, 661, 477, 575, 0, 673, 676, 605, - 764, 0, 670, 684, 769, 683, 761, 489, 0, 519, - 681, 628, 0, 674, 647, 648, 0, 675, 643, 679, - 0, 617, 0, 586, 589, 618, 703, 704, 705, 338, - 588, 707, 708, 709, 710, 711, 712, 713, 706, 558, - 651, 627, 654, 567, 630, 629, 0, 0, 665, 584, - 666, 667, 471, 472, 473, 474, 399, 691, 362, 587, - 504, 505, 0, 652, 0, 0, 0, 0, 0, 0, - 0, 0, 657, 658, 655, 774, 0, 714, 715, 0, - 0, 581, 582, 394, 0, 600, 402, 361, 488, 396, - 565, 425, 0, 593, 659, 594, 507, 508, 718, 723, - 719, 720, 722, 743, 478, 416, 421, 523, 433, 452, - 510, 564, 486, 515, 359, 554, 525, 458, 644, 672, + 0, 0, 0, 0, 0, 530, 558, 0, 571, 0, + 424, 425, 0, 0, 0, 0, 0, 0, 0, 342, + 537, 555, 358, 524, 569, 363, 532, 548, 353, 487, + 521, 0, 0, 344, 553, 531, 466, 343, 0, 514, + 383, 400, 380, 485, 0, 0, 552, 582, 379, 572, + 0, 563, 346, 0, 562, 484, 458, 549, 554, 467, + 460, 0, 345, 551, 465, 459, 436, 390, 598, 437, + 438, 439, 440, 441, 442, 404, 500, 456, 501, 405, + 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, + 413, 414, 415, 0, 0, 0, 0, 0, 593, 594, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 727, 0, 1236, 732, 0, + 565, 0, 0, 0, 0, 0, 0, 535, 0, 0, + 443, 0, 0, 0, 583, 0, 517, 490, 773, 0, + 0, 515, 454, 550, 502, 556, 538, 564, 509, 503, + 336, 539, 382, 468, 354, 356, 760, 384, 387, 391, + 392, 478, 479, 496, 523, 542, 543, 544, 381, 368, + 516, 369, 401, 370, 337, 373, 372, 374, 525, 375, + 339, 497, 547, 0, 397, 512, 463, 340, 462, 498, + 546, 545, 355, 573, 580, 581, 671, 0, 586, 774, + 775, 776, 595, 0, 504, 351, 348, 0, 0, 0, + 377, 499, 364, 366, 367, 365, 493, 495, 600, 601, + 602, 604, 0, 605, 606, 0, 0, 0, 0, 607, + 672, 688, 656, 625, 588, 680, 622, 626, 627, 418, + 419, 420, 421, 691, 0, 0, 0, 579, 444, 445, + 0, 389, 388, 464, 341, 0, 0, 433, 417, 505, + 347, 385, 435, 423, 446, 447, 448, 449, 450, 0, + 451, 395, 331, 332, 768, 378, 486, 693, 730, 731, + 618, 0, 681, 619, 628, 371, 653, 665, 664, 480, + 578, 0, 676, 679, 608, 767, 0, 673, 687, 772, + 686, 764, 492, 0, 522, 684, 631, 0, 677, 650, + 651, 0, 678, 646, 682, 0, 620, 0, 589, 592, + 621, 706, 707, 708, 338, 591, 710, 711, 712, 713, + 714, 715, 716, 709, 561, 654, 630, 657, 570, 633, + 632, 0, 0, 668, 587, 669, 670, 474, 475, 476, + 477, 399, 694, 362, 590, 507, 508, 0, 655, 0, + 0, 0, 0, 0, 0, 0, 0, 660, 661, 658, + 777, 0, 717, 718, 0, 0, 584, 585, 394, 0, + 603, 402, 361, 491, 396, 568, 426, 0, 596, 662, + 597, 510, 511, 721, 726, 722, 723, 725, 746, 481, + 416, 422, 526, 434, 455, 513, 567, 489, 518, 359, + 557, 528, 461, 647, 675, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 699, 698, 697, - 696, 695, 694, 693, 692, 0, 0, 641, 376, 762, - 756, 763, 744, 747, 746, 721, 0, 333, 3328, 450, - 503, 393, 686, 687, 0, 742, 278, 279, 280, 281, - 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, - 291, 292, 297, 298, 299, 300, 301, 302, 303, 304, - 689, 295, 296, 305, 306, 307, 308, 309, 310, 311, - 312, 313, 314, 315, 316, 317, 318, 0, 0, 0, - 0, 327, 748, 749, 750, 751, 752, 0, 0, 328, - 329, 330, 0, 0, 293, 294, 321, 533, 322, 323, - 324, 325, 0, 0, 571, 572, 573, 596, 0, 574, - 556, 620, 403, 334, 537, 563, 759, 760, 0, 0, - 0, 0, 0, 0, 0, 671, 682, 716, 717, 0, - 730, 731, 733, 735, 734, 737, 530, 531, 745, 0, - 0, 426, 349, 350, 479, 480, 517, 0, 427, 428, - 429, 431, 430, 739, 740, 741, 738, 454, 516, 538, - 524, 758, 466, 0, 766, 611, 612, 767, 726, 725, - 3300, 485, 0, 0, 626, 660, 649, 736, 614, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 386, - 0, 0, 449, 664, 645, 656, 646, 491, 631, 632, - 633, 640, 398, 634, 768, 636, 606, 637, 607, 638, - 639, 0, 663, 613, 526, 467, 0, 680, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 262, 0, - 0, 0, 0, 0, 0, 357, 263, 608, 732, 610, - 609, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 360, 2919, 2922, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 527, 555, 0, 568, 0, 423, 424, 0, 0, 0, - 0, 0, 0, 0, 342, 534, 552, 358, 521, 566, - 363, 529, 545, 353, 484, 518, 0, 0, 344, 550, - 528, 463, 343, 0, 511, 383, 400, 380, 482, 0, - 0, 549, 579, 379, 569, 0, 560, 346, 0, 559, - 481, 455, 546, 551, 464, 457, 0, 345, 548, 462, - 456, 435, 390, 595, 436, 437, 438, 439, 440, 441, - 404, 497, 453, 498, 405, 469, 468, 470, 406, 407, - 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, - 0, 0, 0, 590, 591, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 724, 0, 0, 729, 2923, 562, 0, 0, 0, 2918, - 0, 2917, 532, 2915, 2920, 442, 0, 0, 0, 580, - 0, 514, 487, 770, 0, 0, 512, 451, 547, 499, - 553, 535, 561, 506, 500, 336, 536, 382, 465, 354, - 356, 757, 384, 387, 391, 392, 475, 476, 493, 520, - 539, 540, 541, 381, 368, 513, 369, 401, 370, 337, - 373, 372, 374, 522, 375, 339, 494, 544, 2921, 397, - 509, 460, 340, 459, 495, 543, 542, 355, 570, 577, - 578, 668, 0, 583, 771, 772, 773, 592, 0, 501, - 351, 348, 0, 0, 0, 377, 496, 364, 366, 367, - 365, 490, 492, 597, 598, 599, 601, 0, 602, 603, - 0, 0, 0, 0, 604, 669, 685, 653, 622, 585, - 677, 619, 623, 624, 418, 419, 420, 688, 0, 0, - 0, 576, 443, 444, 0, 389, 388, 461, 341, 0, - 0, 432, 417, 502, 347, 385, 434, 422, 445, 446, - 447, 448, 395, 331, 332, 765, 378, 483, 690, 727, - 728, 615, 0, 678, 616, 625, 371, 650, 662, 661, - 477, 575, 0, 673, 676, 605, 764, 0, 670, 684, - 769, 683, 761, 489, 0, 519, 681, 628, 0, 674, - 647, 648, 0, 675, 643, 679, 0, 617, 0, 586, - 589, 618, 703, 704, 705, 338, 588, 707, 708, 709, - 710, 711, 712, 713, 706, 558, 651, 627, 654, 567, - 630, 629, 0, 0, 665, 584, 666, 667, 471, 472, - 473, 474, 399, 691, 362, 587, 504, 505, 0, 652, - 0, 0, 0, 0, 0, 0, 0, 0, 657, 658, - 655, 774, 0, 714, 715, 0, 0, 581, 582, 394, - 0, 600, 402, 361, 488, 396, 565, 425, 0, 593, - 659, 594, 507, 508, 718, 723, 719, 720, 722, 743, - 478, 416, 421, 523, 433, 452, 510, 564, 486, 515, - 359, 554, 525, 458, 644, 672, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 699, 698, 697, 696, 695, 694, 693, - 692, 0, 0, 641, 376, 762, 756, 763, 744, 747, - 746, 721, 0, 333, 621, 450, 503, 393, 686, 687, - 0, 742, 278, 279, 280, 281, 282, 283, 284, 285, - 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, - 299, 300, 301, 302, 303, 304, 689, 295, 296, 305, - 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, - 316, 317, 318, 0, 0, 0, 0, 327, 748, 749, - 750, 751, 752, 0, 0, 328, 329, 330, 0, 0, - 293, 294, 321, 533, 322, 323, 324, 325, 0, 0, - 571, 572, 573, 596, 0, 574, 556, 620, 403, 334, - 537, 563, 759, 760, 0, 0, 0, 0, 0, 0, - 0, 671, 682, 716, 717, 0, 730, 731, 733, 735, - 734, 737, 530, 531, 745, 0, 0, 426, 349, 350, - 479, 480, 517, 0, 427, 428, 429, 431, 430, 739, - 740, 741, 738, 454, 516, 538, 524, 758, 466, 0, - 766, 611, 612, 767, 726, 725, 335, 197, 240, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 1233, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1762, 0, 0, 262, 0, 0, 0, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, + 0, 0, 702, 701, 700, 699, 698, 697, 696, 695, + 0, 0, 644, 376, 765, 759, 766, 747, 750, 749, + 724, 0, 333, 624, 453, 506, 393, 689, 690, 0, + 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, + 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, + 300, 301, 302, 303, 304, 692, 295, 296, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 0, 0, 0, 0, 327, 751, 752, 753, + 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, + 294, 321, 536, 322, 323, 324, 325, 0, 0, 574, + 575, 576, 599, 0, 577, 559, 623, 403, 334, 540, + 566, 762, 763, 0, 0, 0, 0, 0, 0, 0, + 674, 685, 719, 720, 0, 733, 734, 736, 738, 737, + 740, 533, 534, 748, 0, 0, 427, 349, 350, 482, + 483, 520, 0, 428, 429, 430, 432, 431, 742, 743, + 744, 741, 457, 519, 541, 527, 761, 469, 0, 769, + 614, 615, 770, 729, 728, 335, 488, 0, 0, 629, + 663, 652, 739, 617, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 386, 1213, 0, 452, 667, 648, + 659, 649, 494, 634, 635, 636, 643, 398, 637, 771, + 639, 609, 640, 610, 641, 642, 0, 666, 616, 529, + 470, 0, 683, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 262, 1220, 1221, 0, 0, 0, 0, + 357, 263, 611, 735, 613, 612, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1224, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 530, 558, 0, 571, 0, + 424, 425, 0, 0, 0, 0, 0, 0, 0, 342, + 537, 1207, 358, 524, 569, 363, 532, 548, 353, 487, + 521, 0, 0, 344, 553, 531, 466, 343, 0, 514, + 383, 400, 380, 485, 0, 0, 552, 582, 379, 572, + 879, 563, 346, 878, 562, 484, 458, 549, 554, 467, + 460, 0, 345, 551, 465, 459, 436, 390, 598, 437, + 438, 439, 440, 441, 442, 404, 500, 456, 501, 405, + 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, + 413, 414, 415, 0, 0, 0, 0, 0, 593, 594, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 727, 0, 0, 732, 0, + 565, 0, 0, 0, 0, 0, 0, 535, 0, 0, + 443, 0, 0, 0, 583, 0, 517, 490, 773, 0, + 0, 515, 454, 550, 502, 556, 538, 564, 1211, 503, + 336, 539, 382, 468, 354, 356, 760, 384, 387, 391, + 392, 478, 479, 496, 523, 542, 543, 544, 381, 368, + 516, 369, 401, 370, 337, 373, 372, 374, 525, 375, + 339, 497, 547, 0, 397, 512, 463, 340, 462, 498, + 546, 545, 355, 573, 580, 581, 671, 0, 586, 774, + 775, 776, 595, 0, 504, 351, 348, 0, 0, 0, + 377, 499, 364, 366, 367, 365, 493, 495, 600, 601, + 602, 604, 0, 605, 606, 0, 0, 0, 0, 607, + 672, 688, 656, 625, 588, 680, 622, 626, 627, 418, + 419, 420, 421, 691, 0, 0, 0, 579, 444, 445, + 0, 389, 388, 464, 341, 0, 0, 433, 417, 505, + 347, 385, 435, 423, 446, 447, 448, 449, 450, 0, + 451, 395, 331, 332, 768, 378, 486, 693, 730, 731, + 618, 0, 681, 619, 628, 371, 653, 665, 664, 480, + 578, 0, 676, 679, 608, 767, 0, 673, 687, 772, + 686, 764, 492, 0, 522, 684, 631, 0, 677, 650, + 651, 0, 678, 646, 682, 0, 620, 0, 589, 592, + 621, 706, 707, 708, 338, 591, 710, 711, 712, 713, + 714, 715, 1212, 709, 561, 654, 630, 657, 570, 633, + 632, 0, 0, 668, 1215, 669, 670, 474, 475, 476, + 477, 399, 694, 1210, 590, 507, 508, 0, 655, 0, + 0, 0, 0, 0, 0, 0, 0, 660, 661, 658, + 777, 0, 717, 718, 0, 0, 584, 585, 394, 0, + 603, 402, 361, 491, 396, 568, 426, 0, 596, 662, + 597, 510, 511, 721, 726, 722, 723, 725, 746, 1222, + 1208, 1218, 1209, 434, 455, 513, 567, 489, 518, 359, + 557, 528, 1219, 647, 675, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 702, 701, 700, 699, 698, 697, 696, 695, + 0, 0, 644, 376, 765, 759, 766, 747, 750, 749, + 724, 0, 333, 624, 453, 506, 393, 689, 690, 0, + 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, + 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, + 300, 301, 302, 303, 304, 692, 295, 296, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 0, 0, 0, 0, 327, 751, 752, 753, + 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, + 294, 321, 536, 322, 323, 324, 325, 0, 0, 574, + 575, 576, 599, 0, 577, 559, 623, 403, 334, 540, + 566, 762, 763, 0, 0, 0, 0, 0, 0, 0, + 674, 685, 719, 720, 0, 733, 734, 736, 738, 737, + 740, 533, 534, 748, 0, 0, 427, 349, 350, 482, + 483, 520, 0, 428, 429, 430, 432, 431, 742, 743, + 744, 741, 1206, 519, 541, 527, 761, 469, 0, 769, + 614, 615, 770, 729, 728, 335, 488, 0, 0, 629, + 663, 652, 739, 617, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 386, 0, 0, 452, 667, 648, + 659, 649, 494, 634, 635, 636, 643, 398, 637, 771, + 639, 609, 640, 610, 641, 642, 0, 666, 616, 529, + 470, 0, 683, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1766, 0, 0, 262, 0, 0, 0, 0, 0, 0, + 357, 263, 611, 735, 613, 612, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, - 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 1232, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 530, 558, 0, 571, 0, + 424, 425, 0, 0, 0, 0, 0, 0, 0, 342, + 537, 555, 358, 524, 569, 363, 532, 548, 353, 487, + 521, 0, 0, 344, 553, 531, 466, 343, 0, 514, + 383, 400, 380, 485, 0, 0, 552, 582, 379, 572, + 0, 563, 346, 0, 562, 484, 458, 549, 554, 467, + 460, 0, 345, 551, 465, 459, 436, 390, 598, 437, + 438, 439, 440, 441, 442, 404, 500, 456, 501, 405, + 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, + 413, 414, 415, 0, 0, 0, 0, 0, 593, 594, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 727, 0, 0, 732, 0, + 565, 0, 0, 0, 0, 0, 0, 535, 0, 0, + 443, 0, 0, 0, 583, 0, 517, 490, 773, 0, + 0, 515, 454, 550, 502, 556, 538, 564, 509, 503, + 336, 539, 382, 468, 354, 356, 760, 384, 387, 391, + 392, 478, 479, 496, 523, 542, 543, 544, 381, 368, + 516, 369, 401, 370, 337, 373, 372, 374, 525, 375, + 339, 497, 547, 0, 397, 512, 463, 340, 462, 498, + 546, 545, 355, 573, 580, 581, 671, 0, 586, 774, + 775, 776, 595, 0, 504, 351, 348, 0, 0, 0, + 377, 499, 364, 366, 367, 365, 493, 495, 600, 601, + 602, 604, 0, 605, 606, 0, 0, 0, 0, 607, + 672, 688, 656, 625, 588, 680, 622, 626, 627, 418, + 419, 420, 421, 691, 0, 0, 0, 579, 444, 445, + 0, 389, 388, 464, 341, 0, 0, 433, 417, 505, + 347, 385, 435, 423, 446, 447, 448, 449, 450, 0, + 451, 395, 331, 332, 768, 378, 486, 693, 730, 731, + 618, 0, 681, 619, 628, 371, 653, 665, 664, 480, + 578, 0, 676, 679, 608, 767, 0, 673, 687, 772, + 686, 764, 492, 0, 522, 684, 631, 0, 677, 650, + 651, 0, 678, 646, 682, 0, 620, 0, 589, 592, + 621, 706, 707, 708, 338, 591, 710, 711, 712, 713, + 714, 715, 716, 709, 561, 654, 630, 657, 570, 633, + 632, 0, 0, 668, 587, 669, 670, 474, 475, 476, + 477, 399, 694, 362, 590, 507, 508, 0, 655, 0, + 0, 0, 0, 0, 0, 0, 0, 660, 661, 658, + 777, 0, 717, 718, 0, 0, 584, 585, 394, 0, + 603, 402, 361, 491, 396, 568, 426, 0, 596, 662, + 597, 510, 511, 721, 726, 722, 723, 725, 746, 481, + 416, 422, 526, 434, 455, 513, 567, 489, 518, 359, + 557, 528, 461, 647, 675, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 702, 701, 700, 699, 698, 697, 696, 695, + 0, 0, 644, 376, 765, 759, 766, 747, 750, 749, + 724, 0, 333, 624, 453, 506, 393, 689, 690, 0, + 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, + 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, + 300, 301, 302, 303, 304, 692, 295, 296, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 0, 0, 0, 0, 327, 751, 752, 753, + 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, + 294, 321, 536, 322, 323, 324, 325, 0, 0, 574, + 575, 576, 599, 0, 577, 559, 623, 403, 334, 540, + 566, 762, 763, 0, 0, 0, 0, 0, 0, 0, + 674, 685, 719, 720, 0, 733, 734, 736, 738, 737, + 740, 533, 534, 748, 0, 0, 427, 349, 350, 482, + 483, 520, 0, 428, 429, 430, 432, 431, 742, 743, + 744, 741, 457, 519, 541, 527, 761, 469, 0, 769, + 614, 615, 770, 729, 728, 335, 488, 2365, 0, 629, + 663, 652, 739, 617, 0, 0, 0, 0, 0, 2532, + 0, 0, 0, 0, 386, 0, 0, 452, 667, 648, + 659, 649, 494, 634, 635, 636, 643, 398, 637, 771, + 639, 609, 640, 610, 641, 642, 0, 666, 616, 529, + 470, 0, 683, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 262, 0, 0, 2533, 0, 0, 0, + 357, 263, 611, 735, 613, 612, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 360, 0, 0, 1534, 0, + 1535, 1533, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 530, 558, 0, 571, 0, + 424, 425, 0, 0, 0, 0, 0, 0, 0, 342, + 537, 555, 358, 524, 569, 363, 532, 548, 353, 487, + 521, 0, 0, 344, 553, 531, 466, 343, 0, 514, + 383, 400, 380, 485, 0, 0, 552, 582, 379, 572, + 0, 563, 346, 0, 562, 484, 458, 549, 554, 467, + 460, 0, 345, 551, 465, 459, 436, 390, 598, 437, + 438, 439, 440, 441, 442, 404, 500, 456, 501, 405, + 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, + 413, 414, 415, 0, 0, 0, 0, 0, 593, 594, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 727, 0, 0, 732, 0, + 565, 0, 0, 0, 0, 0, 0, 535, 0, 0, + 443, 0, 0, 0, 583, 0, 517, 490, 773, 0, + 0, 515, 454, 550, 502, 556, 538, 564, 509, 503, + 336, 539, 382, 468, 354, 356, 760, 384, 387, 391, + 392, 478, 479, 496, 523, 542, 543, 544, 381, 368, + 516, 369, 401, 370, 337, 373, 372, 374, 525, 375, + 339, 497, 547, 0, 397, 512, 463, 340, 462, 498, + 546, 545, 355, 573, 580, 581, 671, 0, 586, 774, + 775, 776, 595, 0, 504, 351, 348, 0, 0, 0, + 377, 499, 364, 366, 367, 365, 493, 495, 600, 601, + 602, 604, 0, 605, 606, 0, 0, 0, 0, 607, + 672, 688, 656, 625, 588, 680, 622, 626, 627, 418, + 419, 420, 421, 691, 0, 0, 0, 579, 444, 445, + 0, 389, 388, 464, 341, 0, 0, 433, 417, 505, + 347, 385, 435, 423, 446, 447, 448, 449, 450, 0, + 451, 395, 331, 332, 768, 378, 486, 693, 730, 731, + 618, 0, 681, 619, 628, 371, 653, 665, 664, 480, + 578, 0, 676, 679, 608, 767, 0, 673, 687, 772, + 686, 764, 492, 0, 522, 684, 631, 0, 677, 650, + 651, 0, 678, 646, 682, 0, 620, 0, 589, 592, + 621, 706, 707, 708, 338, 591, 710, 711, 712, 713, + 714, 715, 716, 709, 561, 654, 630, 657, 570, 633, + 632, 0, 0, 668, 587, 669, 670, 474, 475, 476, + 477, 399, 694, 362, 590, 507, 508, 0, 655, 0, + 0, 0, 0, 0, 0, 0, 0, 660, 661, 658, + 777, 0, 717, 718, 0, 0, 584, 585, 394, 0, + 603, 402, 361, 491, 396, 568, 426, 0, 596, 662, + 597, 510, 511, 721, 726, 722, 723, 725, 746, 481, + 416, 422, 526, 434, 455, 513, 567, 489, 518, 359, + 557, 528, 461, 647, 675, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 702, 701, 700, 699, 698, 697, 696, 695, + 0, 0, 644, 376, 765, 759, 766, 747, 750, 749, + 724, 0, 333, 624, 453, 506, 393, 689, 690, 0, + 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, + 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, + 300, 301, 302, 303, 304, 692, 295, 296, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 0, 0, 0, 0, 327, 751, 752, 753, + 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, + 294, 321, 536, 322, 323, 324, 325, 0, 0, 574, + 575, 576, 599, 0, 577, 559, 623, 403, 334, 540, + 566, 762, 763, 0, 0, 0, 0, 0, 0, 0, + 674, 685, 719, 720, 0, 733, 734, 736, 738, 737, + 740, 533, 534, 748, 0, 0, 427, 349, 350, 482, + 483, 520, 0, 428, 429, 430, 432, 431, 742, 743, + 744, 741, 457, 519, 541, 527, 761, 469, 0, 769, + 614, 615, 770, 729, 488, 335, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 1220, 1221, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1224, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 879, 563, + 346, 878, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 1222, 2469, 1218, + 2470, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 1219, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 2361, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 3606, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 360, 0, 2940, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 2939, 562, 0, - 0, 0, 2945, 2942, 2944, 532, 0, 2943, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 2937, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, - 0, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, - 0, 2940, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 0, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 0, 729, 2939, 562, 0, 0, 0, 2945, 2942, - 2944, 532, 0, 2943, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 584, 666, 667, 471, 472, 473, - 474, 399, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 197, 240, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 485, 0, 0, - 626, 660, 649, 736, 614, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 386, 0, 0, 449, 664, - 645, 656, 646, 491, 631, 632, 633, 640, 398, 634, - 768, 636, 606, 637, 607, 638, 639, 1233, 663, 613, - 526, 467, 0, 680, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 234, 3092, 0, 262, 0, 0, 0, 0, 0, - 0, 357, 263, 608, 732, 610, 609, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 527, 555, 0, 568, - 0, 423, 424, 0, 0, 0, 0, 0, 0, 0, - 342, 534, 552, 358, 521, 566, 363, 529, 545, 353, - 484, 518, 0, 0, 344, 550, 528, 463, 343, 0, - 511, 383, 400, 380, 482, 0, 0, 549, 579, 379, - 569, 0, 560, 346, 0, 559, 481, 455, 546, 551, - 464, 457, 0, 345, 548, 462, 456, 435, 390, 595, - 436, 437, 438, 439, 440, 441, 404, 497, 453, 498, - 405, 469, 468, 470, 406, 407, 408, 409, 410, 411, - 412, 413, 414, 415, 0, 0, 0, 0, 0, 590, - 591, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 724, 0, 1232, 729, - 0, 562, 0, 0, 0, 0, 0, 0, 532, 0, - 0, 442, 0, 0, 0, 580, 0, 514, 487, 770, - 0, 0, 512, 451, 547, 499, 553, 535, 561, 506, - 500, 336, 536, 382, 465, 354, 356, 757, 384, 387, - 391, 392, 475, 476, 493, 520, 539, 540, 541, 381, - 368, 513, 369, 401, 370, 337, 373, 372, 374, 522, - 375, 339, 494, 544, 0, 397, 509, 460, 340, 459, - 495, 543, 542, 355, 570, 577, 578, 668, 0, 583, - 771, 772, 773, 592, 0, 501, 351, 348, 0, 0, - 0, 377, 496, 364, 366, 367, 365, 490, 492, 597, - 598, 599, 601, 0, 602, 603, 0, 0, 0, 0, - 604, 669, 685, 653, 622, 585, 677, 619, 623, 624, - 418, 419, 420, 688, 0, 0, 0, 576, 443, 444, - 0, 389, 388, 461, 341, 0, 0, 432, 417, 502, - 347, 385, 434, 422, 445, 446, 447, 448, 395, 331, - 332, 765, 378, 483, 690, 727, 728, 615, 0, 678, - 616, 625, 371, 650, 662, 661, 477, 575, 0, 673, - 676, 605, 764, 0, 670, 684, 769, 683, 761, 489, - 0, 519, 681, 628, 0, 674, 647, 648, 0, 675, - 643, 679, 0, 617, 0, 586, 589, 618, 703, 704, - 705, 338, 588, 707, 708, 709, 710, 711, 712, 713, - 706, 558, 651, 627, 654, 567, 630, 629, 0, 0, - 665, 584, 666, 667, 471, 472, 473, 474, 399, 691, - 362, 587, 504, 505, 0, 652, 0, 0, 0, 0, - 0, 0, 0, 0, 657, 658, 655, 774, 0, 714, - 715, 0, 0, 581, 582, 394, 0, 600, 402, 361, - 488, 396, 565, 425, 0, 593, 659, 594, 507, 508, - 718, 723, 719, 720, 722, 743, 478, 416, 421, 523, - 433, 452, 510, 564, 486, 515, 359, 554, 525, 458, - 644, 672, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 699, - 698, 697, 696, 695, 694, 693, 692, 0, 0, 641, - 376, 762, 756, 763, 744, 747, 746, 721, 0, 333, - 621, 450, 503, 393, 686, 687, 0, 742, 278, 279, - 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, - 289, 290, 291, 292, 297, 298, 299, 300, 301, 302, - 303, 304, 689, 295, 296, 305, 306, 307, 308, 309, - 310, 311, 312, 313, 314, 315, 316, 317, 318, 0, - 0, 0, 0, 327, 748, 749, 750, 751, 752, 0, - 0, 328, 329, 330, 0, 0, 293, 294, 321, 533, - 322, 323, 324, 325, 0, 0, 571, 572, 573, 596, - 0, 574, 556, 620, 403, 334, 537, 563, 759, 760, - 0, 0, 0, 0, 0, 0, 0, 671, 682, 716, - 717, 0, 730, 731, 733, 735, 734, 737, 530, 531, - 745, 0, 0, 426, 349, 350, 479, 480, 517, 0, - 427, 428, 429, 431, 430, 739, 740, 741, 738, 454, - 516, 538, 524, 758, 466, 0, 766, 611, 612, 767, - 726, 725, 335, 197, 240, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 1233, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 234, 2846, - 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3609, 0, 0, + 0, 0, 3608, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 1822, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 1820, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 1232, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 1209, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 262, 1216, 1217, - 0, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1220, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 1203, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 876, 560, 346, 875, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 0, 729, 0, 562, 0, 0, 0, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 1207, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 1208, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 1211, 666, 667, 471, 472, 473, - 474, 399, 691, 1206, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 1218, - 1204, 1214, 1205, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 1215, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 1818, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 1202, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1762, 0, 0, 262, 0, 0, 0, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 1816, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 1820, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 0, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, - 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, - 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 2361, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 2528, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 1818, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, - 0, 0, 2529, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 1530, 0, 1531, 1529, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4994, + 0, 262, 1048, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, - 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 485, 335, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 262, 1216, 1217, 0, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1220, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 876, 560, 346, 875, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 0, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 1218, 2465, 1214, 2466, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 1215, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, - 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, - 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 3600, 0, 0, 0, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 1820, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, - 0, 0, 0, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 1818, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3603, 0, 0, 0, 0, - 3602, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 1820, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, - 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 386, 1818, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 262, 0, 0, 1816, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 2112, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 1814, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, - 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 2532, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 2533, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 1812, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 262, 0, 0, 1816, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 3830, 3832, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 1814, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 4967, 0, 262, 1045, 0, - 0, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 0, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 0, 729, 0, 562, 0, 0, 0, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 584, 666, 667, 471, 472, 473, - 474, 399, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 262, 0, 0, 1816, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3473, 0, + 0, 262, 0, 0, 3477, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 1814, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 0, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, - 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, - 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3467, 0, + 0, 262, 0, 0, 3471, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 3031, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 1820, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1179, 386, 0, 0, 452, 667, 648, 659, 649, + 1177, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 1178, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 5056, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 5015, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 1048, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 4967, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, - 0, 0, 1816, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2126, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4673, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 2108, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, - 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 2528, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 262, 0, 0, 2529, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 4666, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, - 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 4450, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 262, 0, 0, 3824, 3826, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 4371, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3468, 0, 0, 262, 0, 0, - 3472, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 0, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 0, 729, 0, 562, 0, 0, 0, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 584, 666, 667, 471, 472, 473, - 474, 399, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3462, 0, 0, 262, 0, 0, 3466, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 4206, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 0, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, - 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, - 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 386, 3026, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, - 0, 0, 1816, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 3477, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, - 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1175, 386, 0, 0, 449, - 664, 645, 656, 646, 1173, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 3130, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, - 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 1174, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 262, 0, 0, 5029, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 3471, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, - 4988, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 0, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 0, 729, 0, 562, 0, 0, 0, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 584, 666, 667, 471, 472, 473, - 474, 399, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 262, 1045, 0, 0, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 0, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, - 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, - 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 4940, 0, 0, 262, - 0, 0, 0, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 4196, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, - 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2122, 0, 0, 262, 0, 0, 0, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3901, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 2357, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 2359, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, - 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 4656, - 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 488, 335, 0, 629, 663, 652, 739, 617, + 0, 0, 4150, 0, 0, 0, 0, 0, 0, 0, + 386, 0, 0, 452, 667, 648, 659, 649, 494, 634, + 635, 636, 643, 398, 637, 771, 639, 609, 640, 610, + 641, 642, 0, 666, 616, 529, 470, 0, 683, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, + 0, 0, 0, 0, 0, 0, 357, 263, 611, 735, + 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, - 0, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 0, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 0, 729, 0, 562, 0, 0, 0, 4649, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 584, 666, 667, 471, 472, 473, - 474, 399, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 0, 530, 558, 0, 571, 0, 424, 425, 0, 0, + 0, 0, 0, 0, 0, 342, 537, 555, 358, 524, + 569, 363, 532, 548, 353, 487, 521, 0, 0, 344, + 553, 531, 466, 343, 0, 514, 383, 400, 380, 485, + 0, 0, 552, 582, 379, 572, 0, 563, 346, 0, + 562, 484, 458, 549, 554, 467, 460, 0, 345, 551, + 465, 459, 436, 390, 598, 437, 438, 439, 440, 441, + 442, 404, 500, 456, 501, 405, 472, 471, 473, 406, + 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, + 0, 0, 0, 0, 593, 594, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 727, 0, 0, 732, 0, 565, 0, 0, 0, + 0, 0, 0, 535, 0, 0, 443, 0, 0, 0, + 583, 0, 517, 490, 773, 0, 0, 515, 454, 550, + 502, 556, 538, 564, 509, 503, 336, 539, 382, 468, + 354, 356, 760, 384, 387, 391, 392, 478, 479, 496, + 523, 542, 543, 544, 381, 368, 516, 369, 401, 370, + 337, 373, 372, 374, 525, 375, 339, 497, 547, 0, + 397, 512, 463, 340, 462, 498, 546, 545, 355, 573, + 580, 581, 671, 0, 586, 774, 775, 776, 595, 0, + 504, 351, 348, 0, 0, 0, 377, 499, 364, 366, + 367, 365, 493, 495, 600, 601, 602, 604, 0, 605, + 606, 0, 0, 0, 0, 607, 672, 688, 656, 625, + 588, 680, 622, 626, 627, 418, 419, 420, 421, 691, + 0, 0, 0, 579, 444, 445, 0, 389, 388, 464, + 341, 0, 0, 433, 417, 505, 347, 385, 435, 423, + 446, 447, 448, 449, 450, 0, 451, 395, 331, 332, + 768, 378, 486, 693, 730, 731, 618, 0, 681, 619, + 628, 371, 653, 665, 664, 480, 578, 0, 676, 679, + 608, 767, 0, 673, 687, 772, 686, 764, 492, 0, + 522, 684, 631, 0, 677, 650, 651, 0, 678, 646, + 682, 0, 620, 0, 589, 592, 621, 706, 707, 708, + 338, 591, 710, 711, 712, 713, 714, 715, 716, 709, + 561, 654, 630, 657, 570, 633, 632, 0, 0, 668, + 587, 669, 670, 474, 475, 476, 477, 399, 694, 362, + 590, 507, 508, 0, 655, 0, 0, 0, 0, 0, + 0, 0, 0, 660, 661, 658, 777, 0, 717, 718, + 0, 0, 584, 585, 394, 0, 603, 402, 361, 491, + 396, 568, 426, 0, 596, 662, 597, 510, 511, 721, + 726, 722, 723, 725, 746, 481, 416, 422, 526, 434, + 455, 513, 567, 489, 518, 359, 557, 528, 461, 647, + 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 702, 701, + 700, 699, 698, 697, 696, 695, 0, 0, 644, 376, + 765, 759, 766, 747, 750, 749, 724, 0, 333, 624, + 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, + 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, + 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, + 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, + 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, + 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, + 328, 329, 330, 0, 0, 293, 294, 321, 536, 322, + 323, 324, 325, 0, 0, 574, 575, 576, 599, 0, + 577, 559, 623, 403, 334, 540, 566, 762, 763, 0, + 0, 0, 0, 0, 0, 0, 674, 685, 719, 720, + 0, 733, 734, 736, 738, 737, 740, 533, 534, 748, + 0, 0, 427, 349, 350, 482, 483, 520, 0, 428, + 429, 430, 432, 431, 742, 743, 744, 741, 457, 519, + 541, 527, 761, 469, 0, 769, 614, 615, 770, 729, + 728, 335, 488, 0, 0, 629, 663, 652, 739, 617, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 386, 0, 0, 452, 667, 648, 659, 649, 494, 634, + 635, 636, 643, 398, 637, 771, 639, 609, 640, 610, + 641, 642, 0, 666, 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, - 0, 0, 4440, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 0, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, + 0, 0, 0, 0, 0, 0, 357, 263, 611, 735, + 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4015, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 530, 558, 0, 571, 0, 424, 425, 0, 0, + 0, 0, 0, 0, 0, 342, 537, 555, 358, 524, + 569, 363, 532, 548, 353, 487, 521, 0, 0, 344, + 553, 531, 466, 343, 0, 514, 383, 400, 380, 485, + 0, 0, 552, 582, 379, 572, 0, 563, 346, 0, + 562, 484, 458, 549, 554, 467, 460, 0, 345, 551, + 465, 459, 436, 390, 598, 437, 438, 439, 440, 441, + 442, 404, 500, 456, 501, 405, 472, 471, 473, 406, + 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, + 0, 0, 0, 0, 593, 594, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 727, 0, 0, 732, 0, 565, 0, 0, 0, + 0, 0, 0, 535, 0, 0, 443, 0, 0, 0, + 583, 0, 517, 490, 773, 0, 0, 515, 454, 550, + 502, 556, 538, 564, 509, 503, 336, 539, 382, 468, + 354, 356, 760, 384, 387, 391, 392, 478, 479, 496, + 523, 542, 543, 544, 381, 368, 516, 369, 401, 370, + 337, 373, 372, 374, 525, 375, 339, 497, 547, 0, + 397, 512, 463, 340, 462, 498, 546, 545, 355, 573, + 580, 581, 671, 0, 586, 774, 775, 776, 595, 0, + 504, 351, 348, 0, 0, 0, 377, 499, 364, 366, + 367, 365, 493, 495, 600, 601, 602, 604, 0, 605, + 606, 0, 0, 0, 0, 607, 672, 688, 656, 625, + 588, 680, 622, 626, 627, 418, 419, 420, 421, 691, + 0, 0, 0, 579, 444, 445, 0, 389, 388, 464, + 341, 0, 0, 433, 417, 505, 347, 385, 435, 423, + 446, 447, 448, 449, 450, 0, 451, 395, 331, 332, + 768, 378, 486, 693, 730, 731, 618, 0, 681, 619, + 628, 371, 653, 665, 664, 480, 578, 0, 676, 679, + 608, 767, 0, 673, 687, 772, 686, 764, 492, 0, + 522, 684, 631, 0, 677, 650, 651, 0, 678, 646, + 682, 0, 620, 0, 589, 592, 621, 706, 707, 708, + 338, 591, 710, 711, 712, 713, 714, 715, 716, 709, + 561, 654, 630, 657, 570, 633, 632, 0, 0, 668, + 587, 669, 670, 474, 475, 476, 477, 399, 694, 362, + 590, 507, 508, 0, 655, 0, 0, 0, 0, 0, + 0, 0, 0, 660, 661, 658, 777, 0, 717, 718, + 0, 0, 584, 585, 394, 0, 603, 402, 361, 491, + 396, 568, 426, 0, 596, 662, 597, 510, 511, 721, + 726, 722, 723, 725, 746, 481, 416, 422, 526, 434, + 455, 513, 567, 489, 518, 359, 557, 528, 461, 647, + 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, + 0, 0, 0, 0, 0, 0, 0, 0, 702, 701, + 700, 699, 698, 697, 696, 695, 0, 0, 644, 376, + 765, 759, 766, 747, 750, 749, 724, 0, 333, 624, + 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, + 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, + 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, + 328, 329, 330, 0, 0, 293, 294, 321, 536, 322, + 323, 324, 325, 0, 0, 574, 575, 576, 599, 0, + 577, 559, 623, 403, 334, 540, 566, 762, 763, 0, + 0, 0, 0, 0, 0, 0, 674, 685, 719, 720, + 0, 733, 734, 736, 738, 737, 740, 533, 534, 748, + 0, 0, 427, 349, 350, 482, 483, 520, 0, 428, + 429, 430, 432, 431, 742, 743, 744, 741, 457, 519, + 541, 527, 761, 469, 0, 769, 614, 615, 770, 729, + 728, 335, 488, 0, 0, 629, 663, 652, 739, 617, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 386, 0, 0, 452, 667, 648, 659, 649, 494, 634, + 635, 636, 643, 398, 637, 771, 639, 609, 640, 610, + 641, 642, 0, 666, 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, - 0, 0, 4361, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3872, 0, 0, 0, 357, 263, 611, 735, + 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, + 0, 530, 558, 0, 571, 0, 424, 425, 0, 0, + 0, 0, 0, 0, 0, 342, 537, 555, 358, 524, + 569, 363, 532, 548, 353, 487, 521, 0, 0, 344, + 553, 531, 466, 343, 0, 514, 383, 400, 380, 485, + 0, 0, 552, 582, 379, 572, 0, 563, 346, 0, + 562, 484, 458, 549, 554, 467, 460, 0, 345, 551, + 465, 459, 436, 390, 598, 437, 438, 439, 440, 441, + 442, 404, 500, 456, 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, + 0, 0, 0, 0, 593, 594, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 727, 0, 0, 732, 0, 565, 0, 0, 0, + 0, 0, 0, 535, 0, 0, 443, 0, 0, 0, + 583, 0, 517, 490, 773, 0, 0, 515, 454, 550, + 502, 556, 538, 564, 509, 503, 336, 539, 382, 468, + 354, 356, 760, 384, 387, 391, 392, 478, 479, 496, + 523, 542, 543, 544, 381, 368, 516, 369, 401, 370, + 337, 373, 372, 374, 525, 375, 339, 497, 547, 0, + 397, 512, 463, 340, 462, 498, 546, 545, 355, 573, + 580, 581, 671, 0, 586, 774, 775, 776, 595, 0, + 504, 351, 348, 0, 0, 0, 377, 499, 364, 366, + 367, 365, 493, 495, 600, 601, 602, 604, 0, 605, + 606, 0, 0, 0, 0, 607, 672, 688, 656, 625, + 588, 680, 622, 626, 627, 418, 419, 420, 421, 691, + 0, 0, 0, 579, 444, 445, 0, 389, 388, 464, + 341, 0, 0, 433, 417, 505, 347, 385, 435, 423, + 446, 447, 448, 449, 450, 0, 451, 395, 331, 332, + 768, 378, 486, 693, 730, 731, 618, 0, 681, 619, + 628, 371, 653, 665, 664, 480, 578, 0, 676, 679, + 608, 767, 0, 673, 687, 772, 686, 764, 492, 0, + 522, 684, 631, 0, 677, 650, 651, 0, 678, 646, + 682, 0, 620, 0, 589, 592, 621, 706, 707, 708, + 338, 591, 710, 711, 712, 713, 714, 715, 716, 709, + 561, 654, 630, 657, 570, 633, 632, 0, 0, 668, + 587, 669, 670, 474, 475, 476, 477, 399, 694, 362, + 590, 507, 508, 0, 655, 0, 0, 0, 0, 0, + 0, 0, 0, 660, 661, 658, 777, 0, 717, 718, + 0, 0, 584, 585, 394, 0, 603, 402, 361, 491, + 396, 568, 426, 0, 596, 662, 597, 510, 511, 721, + 726, 722, 723, 725, 746, 481, 416, 422, 526, 434, + 455, 513, 567, 489, 518, 359, 557, 528, 461, 647, + 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 702, 701, + 700, 699, 698, 697, 696, 695, 0, 0, 644, 376, + 765, 759, 766, 747, 750, 749, 724, 0, 333, 624, + 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, + 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, + 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, + 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, + 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, + 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, + 328, 329, 330, 0, 0, 293, 294, 321, 536, 322, + 323, 324, 325, 0, 0, 574, 575, 576, 599, 0, + 577, 559, 623, 403, 334, 540, 566, 762, 763, 0, + 0, 0, 0, 0, 0, 0, 674, 685, 719, 720, + 0, 733, 734, 736, 738, 737, 740, 533, 534, 748, + 0, 0, 427, 349, 350, 482, 483, 520, 0, 428, + 429, 430, 432, 431, 742, 743, 744, 741, 457, 519, + 541, 527, 761, 469, 0, 769, 614, 615, 770, 729, + 728, 335, 3802, 0, 0, 0, 0, 0, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, - 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 262, 0, 0, 4198, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, + 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, - 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, - 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, - 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, - 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 262, 0, 0, 3472, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, + 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, - 3125, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 0, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 0, 729, 0, 562, 0, 0, 0, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 584, 666, 667, 471, 472, 473, - 474, 399, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 0, 0, 0, 0, 0, 3687, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 262, 0, 0, 3466, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, + 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 262, 0, 0, 1820, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 0, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, - 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, - 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, - 0, 0, 0, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, + 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 262, 0, 0, 2359, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 4188, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 3517, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3895, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, - 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, - 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, - 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, - 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 2353, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 262, 0, 0, 2355, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, + 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1766, 0, 0, 262, 0, 0, 0, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 485, 335, - 0, 626, 660, 649, 736, 614, 0, 0, 4142, 0, - 0, 0, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, + 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 262, 0, 0, 3402, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, - 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, - 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, - 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, - 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, + 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 4007, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, - 3866, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 0, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 0, 729, 0, 562, 0, 0, 0, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 584, 666, 667, 471, 472, 473, - 474, 399, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 3796, 0, 0, 0, - 0, 0, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, + 0, 0, 0, 0, 0, 3383, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, - 0, 0, 0, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, + 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 262, 0, 0, 3261, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3681, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, + 0, 0, 0, 0, 0, 2641, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, - 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, - 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, - 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, - 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 262, 0, 0, 1816, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, + 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 262, 0, 0, 3113, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, - 2355, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 0, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 0, 729, 0, 562, 0, 0, 0, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 584, 666, 667, 471, 472, 473, - 474, 399, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 3511, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, + 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 0, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, - 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, - 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1762, 0, 0, 262, - 0, 0, 0, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3057, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, + 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 488, 0, + 0, 629, 663, 652, 739, 617, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 386, 0, 0, 452, + 667, 648, 659, 649, 494, 634, 635, 636, 643, 398, + 637, 771, 639, 609, 640, 610, 641, 642, 0, 666, + 616, 529, 470, 0, 683, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 262, 0, 0, 3055, 0, + 0, 0, 357, 263, 611, 735, 613, 612, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 530, 558, 0, + 571, 0, 424, 425, 0, 0, 0, 0, 0, 0, + 0, 342, 537, 555, 358, 524, 569, 363, 532, 548, + 353, 487, 521, 0, 0, 344, 553, 531, 466, 343, + 0, 514, 383, 400, 380, 485, 0, 0, 552, 582, + 379, 572, 0, 563, 346, 0, 562, 484, 458, 549, + 554, 467, 460, 0, 345, 551, 465, 459, 436, 390, + 598, 437, 438, 439, 440, 441, 442, 404, 500, 456, + 501, 405, 472, 471, 473, 406, 407, 408, 409, 410, + 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, + 593, 594, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 727, 0, 0, + 732, 0, 565, 0, 0, 0, 0, 0, 0, 535, + 0, 0, 443, 0, 0, 0, 583, 0, 517, 490, + 773, 0, 0, 515, 454, 550, 502, 556, 538, 564, + 509, 503, 336, 539, 382, 468, 354, 356, 760, 384, + 387, 391, 392, 478, 479, 496, 523, 542, 543, 544, + 381, 368, 516, 369, 401, 370, 337, 373, 372, 374, + 525, 375, 339, 497, 547, 0, 397, 512, 463, 340, + 462, 498, 546, 545, 355, 573, 580, 581, 671, 0, + 586, 774, 775, 776, 595, 0, 504, 351, 348, 0, + 0, 0, 377, 499, 364, 366, 367, 365, 493, 495, + 600, 601, 602, 604, 0, 605, 606, 0, 0, 0, + 0, 607, 672, 688, 656, 625, 588, 680, 622, 626, + 627, 418, 419, 420, 421, 691, 0, 0, 0, 579, + 444, 445, 0, 389, 388, 464, 341, 0, 0, 433, + 417, 505, 347, 385, 435, 423, 446, 447, 448, 449, + 450, 0, 451, 395, 331, 332, 768, 378, 486, 693, + 730, 731, 618, 0, 681, 619, 628, 371, 653, 665, + 664, 480, 578, 0, 676, 679, 608, 767, 0, 673, + 687, 772, 686, 764, 492, 0, 522, 684, 631, 0, + 677, 650, 651, 0, 678, 646, 682, 0, 620, 0, + 589, 592, 621, 706, 707, 708, 338, 591, 710, 711, + 712, 713, 714, 715, 716, 709, 561, 654, 630, 657, + 570, 633, 632, 0, 0, 668, 587, 669, 670, 474, + 475, 476, 477, 399, 694, 362, 590, 507, 508, 0, + 655, 0, 0, 0, 0, 0, 0, 0, 0, 660, + 661, 658, 777, 0, 717, 718, 0, 0, 584, 585, + 394, 0, 603, 402, 361, 491, 396, 568, 426, 0, + 596, 662, 597, 510, 511, 721, 726, 722, 723, 725, + 746, 481, 416, 422, 526, 434, 455, 513, 567, 489, + 518, 359, 557, 528, 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, + 0, 0, 0, 0, 702, 701, 700, 699, 698, 697, + 696, 695, 0, 0, 644, 376, 765, 759, 766, 747, + 750, 749, 724, 0, 333, 624, 453, 506, 393, 689, + 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, + 298, 299, 300, 301, 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 262, 0, 0, 3397, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, + 315, 316, 317, 318, 0, 0, 0, 0, 327, 751, + 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, + 0, 293, 294, 321, 536, 322, 323, 324, 325, 0, + 0, 574, 575, 576, 599, 0, 577, 559, 623, 403, + 334, 540, 566, 762, 763, 0, 0, 0, 0, 0, + 0, 0, 674, 685, 719, 720, 0, 733, 734, 736, + 738, 737, 740, 533, 534, 748, 0, 0, 427, 349, + 350, 482, 483, 520, 0, 428, 429, 430, 432, 431, + 742, 743, 744, 741, 457, 519, 541, 527, 761, 469, + 0, 769, 614, 615, 770, 729, 728, 335, 2863, 0, + 0, 0, 0, 0, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, - 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 2302, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3378, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, - 3256, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 0, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 0, 729, 0, 562, 0, 0, 0, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 584, 666, 667, 471, 472, 473, - 474, 399, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 1414, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 2637, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 0, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, - 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, - 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, - 0, 0, 3108, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 2506, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 2441, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, - 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 3052, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, - 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 262, 0, 0, 3050, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 1820, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 2859, 0, 0, 0, 0, 0, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 2004, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 0, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, - 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, - 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, - 0, 0, 0, 2298, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 1856, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, - 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 1410, 0, - 0, 0, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1179, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, - 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 2502, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 2437, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, - 1816, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 0, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 0, 729, 0, 562, 0, 0, 0, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 2000, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 584, 666, 667, 471, 472, 473, - 474, 399, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 795, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 1852, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, - 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, - 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1175, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, - 0, 0, 0, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 1182, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, - 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 930, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 929, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, - 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 792, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 1178, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, - 0, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 552, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 0, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 927, 729, 0, 562, 0, 0, 0, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 926, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 584, 666, 667, 471, 472, 473, - 474, 399, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 3805, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 545, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 0, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, - 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, - 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, - 0, 0, 0, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 2285, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 506, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 3799, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 713, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, - 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 1799, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 2281, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, - 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 1797, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 721, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 1795, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 0, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 0, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 0, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 725, 335, - 485, 0, 0, 626, 660, 649, 736, 614, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 386, 0, - 0, 449, 664, 645, 656, 646, 491, 631, 632, 633, - 640, 398, 634, 768, 636, 606, 637, 607, 638, 639, - 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 262, 0, 0, - 0, 0, 0, 0, 357, 263, 608, 732, 610, 609, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 360, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 527, - 555, 0, 568, 0, 423, 424, 0, 0, 0, 0, - 0, 0, 0, 342, 534, 1793, 358, 521, 566, 363, - 529, 545, 353, 484, 518, 0, 0, 344, 550, 528, - 463, 343, 0, 511, 383, 400, 380, 482, 0, 0, - 549, 579, 379, 569, 0, 560, 346, 0, 559, 481, - 455, 546, 551, 464, 457, 0, 345, 548, 462, 456, - 435, 390, 595, 436, 437, 438, 439, 440, 441, 404, - 497, 453, 498, 405, 469, 468, 470, 406, 407, 408, - 409, 410, 411, 412, 413, 414, 415, 0, 0, 0, - 0, 0, 590, 591, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 724, - 0, 0, 729, 0, 562, 0, 0, 0, 0, 0, - 0, 532, 0, 0, 442, 0, 0, 0, 580, 0, - 514, 487, 770, 0, 0, 512, 451, 547, 499, 553, - 535, 561, 506, 500, 336, 536, 382, 465, 354, 356, - 757, 384, 387, 391, 392, 475, 476, 493, 520, 539, - 540, 541, 381, 368, 513, 369, 401, 370, 337, 373, - 372, 374, 522, 375, 339, 494, 544, 0, 397, 509, - 460, 340, 459, 495, 543, 542, 355, 570, 577, 578, - 668, 0, 583, 771, 772, 773, 592, 0, 501, 351, - 348, 0, 0, 0, 377, 496, 364, 366, 367, 365, - 490, 492, 597, 598, 599, 601, 0, 602, 603, 0, - 0, 0, 0, 604, 669, 685, 653, 622, 585, 677, - 619, 623, 624, 418, 419, 420, 688, 0, 0, 0, - 576, 443, 444, 0, 389, 388, 461, 341, 0, 0, - 432, 417, 502, 347, 385, 434, 422, 445, 446, 447, - 448, 395, 331, 332, 765, 378, 483, 690, 727, 728, - 615, 0, 678, 616, 625, 371, 650, 662, 661, 477, - 575, 0, 673, 676, 605, 764, 0, 670, 684, 769, - 683, 761, 489, 0, 519, 681, 628, 0, 674, 647, - 648, 0, 675, 643, 679, 0, 617, 0, 586, 589, - 618, 703, 704, 705, 338, 588, 707, 708, 709, 710, - 711, 712, 713, 706, 558, 651, 627, 654, 567, 630, - 629, 0, 0, 665, 584, 666, 667, 471, 472, 473, - 474, 399, 691, 362, 587, 504, 505, 0, 652, 0, - 0, 0, 0, 0, 0, 0, 0, 657, 658, 655, - 774, 0, 714, 715, 0, 0, 581, 582, 394, 0, - 600, 402, 361, 488, 396, 565, 425, 0, 593, 659, - 594, 507, 508, 718, 723, 719, 720, 722, 743, 478, - 416, 421, 523, 433, 452, 510, 564, 486, 515, 359, - 554, 525, 458, 644, 672, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 1715, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 699, 698, 697, 696, 695, 694, 693, 692, - 0, 0, 641, 376, 762, 756, 763, 744, 747, 746, - 721, 0, 333, 621, 450, 503, 393, 686, 687, 0, - 742, 278, 279, 280, 281, 282, 283, 284, 285, 326, - 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, - 300, 301, 302, 303, 304, 689, 295, 296, 305, 306, - 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, - 317, 318, 0, 0, 0, 0, 327, 748, 749, 750, - 751, 752, 0, 0, 328, 329, 330, 0, 0, 293, - 294, 321, 533, 322, 323, 324, 325, 0, 0, 571, - 572, 573, 596, 0, 574, 556, 620, 403, 334, 537, - 563, 759, 760, 0, 0, 0, 0, 0, 0, 0, - 671, 682, 716, 717, 0, 730, 731, 733, 735, 734, - 737, 530, 531, 745, 0, 0, 426, 349, 350, 479, - 480, 517, 0, 427, 428, 429, 431, 430, 739, 740, - 741, 738, 454, 516, 538, 524, 758, 466, 0, 766, - 611, 612, 767, 726, 725, 335, 485, 0, 0, 626, - 660, 649, 736, 614, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 386, 0, 0, 449, 664, 645, - 656, 646, 491, 631, 632, 633, 640, 398, 634, 768, - 636, 606, 637, 607, 638, 639, 0, 663, 613, 526, - 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 262, 0, 0, 0, 0, 0, 0, - 357, 263, 608, 732, 610, 609, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 360, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 527, 555, 0, 568, 0, - 423, 424, 0, 0, 0, 0, 0, 0, 0, 342, - 534, 552, 358, 521, 566, 363, 529, 1711, 353, 484, - 518, 0, 0, 344, 550, 528, 463, 343, 0, 511, - 383, 400, 380, 482, 0, 0, 549, 579, 379, 569, - 0, 560, 346, 0, 559, 481, 455, 546, 551, 464, - 457, 0, 345, 548, 462, 456, 435, 390, 595, 436, - 437, 438, 439, 440, 441, 404, 497, 453, 498, 405, - 469, 468, 470, 406, 407, 408, 409, 410, 411, 412, - 413, 414, 415, 0, 0, 0, 0, 0, 590, 591, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 724, 0, 0, 729, 0, - 562, 0, 0, 0, 0, 0, 0, 532, 0, 0, - 442, 0, 0, 0, 580, 0, 514, 487, 770, 0, - 0, 512, 451, 547, 499, 553, 535, 561, 506, 500, - 336, 536, 382, 465, 354, 356, 757, 384, 387, 391, - 392, 475, 476, 493, 520, 539, 540, 541, 381, 368, - 513, 369, 401, 370, 337, 373, 372, 374, 522, 375, - 339, 494, 544, 0, 397, 509, 460, 340, 459, 495, - 543, 542, 355, 570, 577, 578, 668, 0, 583, 771, - 772, 773, 592, 0, 501, 351, 348, 0, 0, 0, - 377, 496, 364, 366, 367, 365, 490, 492, 597, 598, - 599, 601, 0, 602, 603, 0, 0, 0, 0, 604, - 669, 685, 653, 622, 585, 677, 619, 623, 624, 418, - 419, 420, 688, 0, 0, 0, 576, 443, 444, 0, - 389, 388, 461, 341, 0, 0, 432, 417, 502, 347, - 385, 434, 422, 445, 446, 447, 448, 395, 331, 332, - 765, 378, 483, 690, 727, 728, 615, 0, 678, 616, - 625, 371, 650, 662, 661, 477, 575, 0, 673, 676, - 605, 764, 0, 670, 684, 769, 683, 761, 489, 0, - 519, 681, 628, 0, 674, 647, 648, 0, 675, 643, - 679, 0, 617, 0, 586, 589, 618, 703, 704, 705, - 338, 588, 707, 708, 709, 710, 711, 712, 713, 706, - 558, 651, 627, 654, 567, 630, 629, 0, 0, 665, - 584, 666, 667, 471, 472, 473, 474, 399, 691, 362, - 587, 504, 505, 0, 652, 0, 0, 0, 0, 0, - 0, 0, 0, 657, 658, 655, 774, 0, 714, 715, - 0, 0, 581, 582, 394, 0, 600, 402, 361, 488, - 396, 565, 425, 0, 593, 659, 594, 507, 508, 718, - 723, 719, 720, 722, 743, 478, 416, 421, 523, 433, - 452, 510, 564, 486, 515, 359, 554, 525, 458, 644, - 672, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 699, 698, - 697, 696, 695, 694, 693, 692, 0, 0, 641, 376, - 762, 756, 763, 744, 747, 746, 721, 0, 333, 621, - 450, 503, 393, 686, 687, 0, 742, 278, 279, 280, - 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, - 290, 291, 292, 297, 298, 299, 300, 301, 302, 303, - 304, 689, 295, 296, 305, 306, 307, 308, 309, 310, - 311, 312, 313, 314, 315, 316, 317, 318, 0, 0, - 0, 0, 327, 748, 749, 750, 751, 752, 0, 0, - 328, 329, 330, 0, 0, 293, 294, 321, 533, 322, - 323, 324, 325, 0, 0, 571, 572, 573, 596, 0, - 574, 556, 620, 403, 334, 537, 563, 759, 760, 0, - 0, 0, 0, 0, 0, 0, 671, 682, 716, 717, - 0, 730, 731, 733, 735, 734, 737, 530, 531, 745, - 0, 0, 426, 349, 350, 479, 480, 517, 0, 427, - 428, 429, 431, 430, 739, 740, 741, 738, 454, 516, - 538, 524, 758, 466, 0, 766, 611, 612, 767, 726, - 725, 335, 485, 0, 0, 626, 660, 649, 736, 614, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 386, 0, 0, 449, 664, 645, 656, 646, 491, 631, - 632, 633, 640, 398, 634, 768, 636, 606, 637, 607, - 638, 639, 0, 663, 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 262, - 0, 0, 0, 0, 0, 0, 357, 263, 608, 732, - 610, 609, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 881, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 882, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 527, 555, 0, 568, 0, 423, 424, 0, 0, - 0, 0, 0, 0, 0, 342, 534, 552, 358, 521, - 566, 363, 529, 545, 353, 484, 518, 0, 0, 344, - 550, 528, 463, 343, 0, 511, 383, 400, 380, 482, - 0, 0, 549, 579, 379, 569, 0, 560, 346, 0, - 559, 481, 455, 546, 551, 464, 457, 0, 345, 548, - 462, 456, 435, 390, 595, 436, 437, 438, 439, 440, - 441, 404, 497, 453, 498, 405, 469, 468, 470, 406, - 407, 408, 409, 410, 411, 412, 413, 414, 415, 0, - 0, 0, 0, 0, 590, 591, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 724, 0, 0, 729, 0, 562, 0, 0, 0, - 0, 0, 0, 532, 0, 0, 442, 0, 0, 0, - 580, 0, 514, 487, 770, 0, 0, 512, 451, 547, - 499, 553, 535, 561, 878, 500, 336, 536, 382, 465, - 354, 356, 757, 384, 387, 391, 392, 475, 476, 493, - 520, 539, 540, 541, 381, 368, 513, 369, 401, 370, - 337, 373, 372, 374, 522, 375, 339, 494, 544, 0, - 397, 509, 460, 340, 459, 495, 543, 542, 355, 570, - 577, 578, 668, 0, 583, 771, 772, 773, 592, 0, - 501, 351, 348, 0, 0, 0, 377, 496, 364, 366, - 367, 365, 490, 492, 597, 598, 599, 601, 0, 602, - 603, 0, 0, 0, 0, 604, 669, 685, 653, 622, - 585, 677, 619, 623, 624, 418, 419, 420, 688, 0, - 0, 0, 576, 443, 444, 0, 389, 388, 461, 341, - 0, 0, 432, 417, 502, 347, 385, 434, 422, 445, - 446, 447, 448, 395, 331, 332, 765, 378, 483, 690, - 727, 728, 615, 0, 678, 616, 625, 371, 650, 662, - 661, 477, 575, 0, 673, 676, 605, 764, 0, 670, - 684, 769, 683, 761, 489, 0, 519, 681, 628, 0, - 674, 647, 648, 0, 675, 643, 679, 0, 617, 0, - 586, 589, 618, 703, 704, 705, 338, 588, 707, 708, - 709, 710, 711, 712, 879, 706, 558, 651, 627, 654, - 567, 630, 629, 0, 0, 665, 584, 666, 667, 471, - 472, 473, 474, 399, 691, 362, 587, 504, 505, 0, - 652, 0, 0, 0, 0, 0, 0, 0, 0, 657, - 658, 655, 774, 0, 714, 715, 0, 0, 581, 582, - 394, 0, 600, 402, 361, 488, 396, 565, 425, 0, - 593, 659, 594, 507, 508, 718, 723, 719, 720, 722, - 743, 478, 416, 421, 523, 433, 452, 510, 564, 486, - 515, 359, 554, 525, 458, 644, 672, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 699, 698, 697, 696, 695, 694, - 693, 692, 0, 0, 641, 376, 762, 756, 763, 744, - 747, 746, 721, 0, 333, 621, 450, 503, 393, 686, - 687, 0, 742, 278, 279, 280, 281, 282, 283, 284, - 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, - 298, 299, 300, 301, 302, 303, 304, 689, 295, 296, - 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, - 315, 316, 317, 318, 0, 0, 0, 0, 327, 748, - 749, 750, 751, 752, 0, 0, 328, 329, 330, 0, - 0, 293, 294, 321, 533, 322, 323, 324, 325, 0, - 0, 571, 572, 573, 596, 0, 574, 556, 620, 403, - 334, 537, 563, 759, 760, 0, 0, 0, 0, 0, - 0, 0, 671, 682, 716, 717, 0, 730, 731, 733, - 735, 734, 737, 530, 531, 745, 0, 0, 426, 349, - 350, 479, 480, 517, 0, 427, 428, 429, 431, 430, - 739, 740, 741, 738, 454, 516, 538, 524, 758, 466, - 0, 766, 611, 612, 767, 726, 725, 335, 485, 0, - 0, 626, 660, 649, 736, 614, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 386, 0, 0, 449, - 664, 645, 656, 646, 491, 631, 632, 633, 640, 398, - 634, 768, 636, 606, 637, 607, 638, 639, 0, 663, - 613, 526, 467, 0, 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 262, 0, 0, 0, 0, - 0, 0, 357, 263, 608, 732, 610, 609, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 360, 0, 0, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 0, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 0, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 819, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, + 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, + 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, + 0, 0, 0, 0, 327, 751, 752, 753, 754, 755, + 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 0, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 0, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 728, 335, 488, 0, 0, 629, 663, 652, + 739, 617, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 386, 0, 0, 452, 667, 648, 659, 649, + 494, 634, 635, 636, 643, 398, 637, 771, 639, 609, + 640, 610, 641, 642, 0, 666, 616, 529, 470, 0, + 683, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, + 611, 735, 613, 612, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 527, 555, 0, - 568, 0, 423, 424, 0, 0, 0, 0, 0, 0, - 0, 342, 534, 552, 358, 521, 566, 363, 529, 545, - 353, 484, 518, 0, 0, 344, 550, 528, 463, 343, - 0, 511, 383, 400, 380, 482, 0, 0, 549, 579, - 379, 569, 0, 560, 346, 0, 559, 481, 455, 546, - 551, 464, 457, 0, 345, 548, 462, 456, 435, 390, - 595, 436, 437, 438, 439, 440, 441, 404, 497, 453, - 498, 405, 469, 468, 470, 406, 407, 408, 409, 410, - 411, 412, 413, 414, 415, 0, 0, 0, 0, 0, - 590, 591, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 724, 0, 0, - 729, 0, 562, 0, 0, 0, 0, 0, 0, 532, - 0, 0, 442, 0, 0, 0, 580, 0, 514, 487, - 770, 0, 0, 512, 451, 547, 499, 553, 535, 561, - 506, 500, 336, 536, 382, 465, 354, 356, 757, 384, - 387, 391, 392, 475, 476, 493, 520, 539, 540, 541, - 381, 368, 513, 369, 401, 370, 337, 373, 372, 374, - 522, 375, 339, 494, 544, 0, 397, 509, 460, 340, - 459, 495, 543, 542, 355, 570, 577, 578, 668, 0, - 583, 771, 772, 773, 592, 0, 501, 351, 348, 0, - 0, 0, 377, 496, 364, 366, 367, 365, 490, 492, - 597, 598, 599, 601, 0, 602, 603, 0, 0, 0, - 0, 604, 669, 685, 653, 622, 585, 677, 619, 623, - 624, 418, 419, 420, 688, 0, 0, 0, 576, 443, - 444, 0, 389, 388, 461, 341, 0, 0, 432, 417, - 502, 347, 385, 434, 422, 445, 446, 447, 448, 395, - 331, 332, 765, 378, 483, 690, 727, 728, 615, 0, - 678, 616, 625, 371, 650, 662, 661, 477, 575, 0, - 673, 676, 605, 764, 0, 670, 684, 769, 683, 761, - 489, 0, 519, 681, 628, 0, 674, 647, 648, 0, - 675, 643, 679, 0, 617, 0, 586, 589, 618, 703, - 704, 705, 338, 588, 707, 708, 709, 710, 711, 712, - 713, 706, 558, 651, 627, 654, 567, 630, 629, 0, - 0, 665, 584, 666, 667, 471, 472, 473, 474, 399, - 691, 362, 587, 504, 505, 0, 652, 0, 0, 0, - 0, 0, 0, 0, 0, 657, 658, 655, 774, 0, - 714, 715, 0, 0, 581, 582, 394, 0, 600, 402, - 361, 488, 396, 565, 425, 0, 593, 659, 594, 507, - 508, 718, 723, 719, 720, 722, 743, 478, 416, 421, - 523, 433, 452, 510, 564, 486, 515, 359, 554, 525, - 458, 644, 672, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 319, 320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 699, 698, 697, 696, 695, 694, 693, 692, 0, 0, - 641, 376, 762, 756, 763, 744, 747, 746, 816, 0, - 333, 621, 450, 503, 393, 686, 687, 0, 742, 278, + 0, 0, 0, 530, 558, 0, 571, 0, 424, 425, + 0, 0, 0, 0, 0, 0, 0, 342, 537, 555, + 358, 524, 569, 363, 532, 548, 353, 487, 521, 0, + 0, 344, 553, 531, 466, 343, 0, 514, 383, 400, + 380, 485, 0, 0, 552, 582, 379, 572, 0, 563, + 346, 0, 562, 484, 458, 549, 554, 467, 460, 0, + 345, 551, 465, 459, 436, 390, 598, 437, 438, 439, + 440, 441, 442, 404, 500, 456, 501, 405, 472, 471, + 473, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 0, 0, 0, 0, 0, 593, 594, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 727, 0, 0, 732, 0, 565, 0, + 0, 0, 0, 0, 0, 535, 0, 0, 443, 0, + 0, 0, 583, 0, 517, 490, 773, 0, 0, 515, + 454, 550, 502, 556, 538, 564, 509, 503, 336, 539, + 382, 468, 354, 356, 760, 384, 387, 391, 392, 478, + 479, 496, 523, 542, 543, 544, 381, 368, 516, 369, + 401, 370, 337, 373, 372, 374, 525, 375, 339, 497, + 547, 0, 397, 512, 463, 340, 462, 498, 546, 545, + 355, 573, 580, 581, 671, 0, 586, 774, 775, 776, + 595, 0, 504, 351, 348, 0, 0, 0, 377, 499, + 364, 366, 367, 365, 493, 495, 600, 601, 602, 604, + 0, 605, 606, 0, 0, 0, 0, 607, 672, 688, + 656, 625, 588, 680, 622, 626, 627, 418, 419, 420, + 421, 691, 0, 0, 0, 579, 444, 445, 0, 389, + 388, 464, 341, 0, 0, 433, 417, 505, 347, 385, + 435, 423, 446, 447, 448, 449, 450, 0, 451, 395, + 331, 332, 768, 378, 486, 693, 730, 731, 618, 0, + 681, 619, 628, 371, 653, 665, 664, 480, 578, 0, + 676, 679, 608, 767, 0, 673, 687, 772, 686, 764, + 492, 0, 522, 684, 631, 0, 677, 650, 651, 0, + 678, 646, 682, 0, 620, 0, 589, 592, 621, 706, + 707, 708, 338, 591, 710, 711, 712, 713, 714, 715, + 716, 709, 561, 654, 630, 657, 570, 633, 632, 2421, + 0, 668, 587, 669, 670, 474, 475, 476, 477, 399, + 694, 362, 590, 507, 508, 0, 655, 0, 0, 0, + 0, 0, 0, 0, 0, 660, 661, 658, 777, 0, + 717, 718, 0, 0, 584, 585, 394, 2423, 603, 402, + 361, 491, 396, 568, 426, 0, 596, 662, 597, 510, + 511, 721, 726, 722, 723, 725, 746, 481, 416, 422, + 526, 434, 455, 513, 567, 489, 518, 359, 557, 528, + 461, 647, 675, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4679, 0, 319, 320, 0, 0, 0, 0, + 0, 0, 2398, 0, 0, 0, 0, 0, 0, 0, + 702, 701, 700, 699, 698, 697, 696, 695, 0, 0, + 644, 376, 765, 759, 766, 747, 750, 749, 724, 0, + 333, 624, 453, 506, 393, 689, 690, 0, 745, 278, 279, 280, 281, 282, 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, 292, 297, 298, 299, 300, 301, - 302, 303, 304, 689, 295, 296, 305, 306, 307, 308, + 302, 303, 304, 692, 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, - 0, 0, 0, 0, 327, 748, 749, 750, 751, 752, + 0, 0, 2414, 2421, 327, 751, 752, 753, 754, 755, 0, 0, 328, 329, 330, 0, 0, 293, 294, 321, - 533, 322, 323, 324, 325, 0, 0, 571, 572, 573, - 596, 0, 574, 556, 620, 403, 334, 537, 563, 759, - 760, 0, 0, 0, 0, 0, 0, 0, 671, 682, - 716, 717, 0, 730, 731, 733, 735, 734, 737, 530, - 531, 745, 0, 0, 426, 349, 350, 479, 480, 517, - 0, 427, 428, 429, 431, 430, 739, 740, 741, 738, - 454, 516, 538, 524, 758, 466, 0, 766, 611, 612, - 767, 726, 725, 335, 485, 0, 0, 626, 660, 649, - 736, 614, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 386, 0, 0, 449, 664, 645, 656, 646, - 491, 631, 632, 633, 640, 398, 634, 768, 636, 606, - 637, 607, 638, 639, 0, 663, 613, 526, 467, 0, - 680, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 536, 322, 323, 324, 325, 0, 0, 574, 575, 576, + 599, 0, 577, 559, 623, 403, 334, 540, 566, 762, + 763, 2423, 0, 0, 0, 0, 0, 0, 674, 685, + 719, 720, 0, 733, 734, 736, 738, 737, 740, 533, + 534, 748, 2421, 0, 427, 349, 350, 482, 483, 520, + 0, 428, 429, 430, 432, 431, 742, 743, 744, 741, + 457, 519, 541, 527, 761, 469, 0, 769, 614, 615, + 770, 729, 0, 335, 0, 0, 2398, 0, 0, 0, + 2423, 2402, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2408, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 262, 0, 0, 0, 0, 0, 0, 357, 263, - 608, 732, 610, 609, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 360, 0, 0, 0, 0, 0, 0, + 0, 0, 2396, 2430, 0, 0, 2397, 2399, 2401, 0, + 2403, 2404, 2405, 2409, 2410, 2411, 2413, 2416, 2417, 2418, + 0, 0, 0, 0, 0, 2398, 0, 2406, 2415, 2407, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4645, 0, 0, 0, 2414, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 527, 555, 0, 568, 0, 423, 424, - 0, 0, 0, 0, 0, 0, 0, 342, 534, 552, - 358, 521, 566, 363, 529, 545, 353, 484, 518, 0, - 0, 344, 550, 528, 463, 343, 0, 511, 383, 400, - 380, 482, 0, 0, 549, 579, 379, 569, 0, 560, - 346, 0, 559, 481, 455, 546, 551, 464, 457, 0, - 345, 548, 462, 456, 435, 390, 595, 436, 437, 438, - 439, 440, 441, 404, 497, 453, 498, 405, 469, 468, - 470, 406, 407, 408, 409, 410, 411, 412, 413, 414, - 415, 0, 0, 0, 0, 0, 590, 591, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 724, 0, 0, 729, 0, 562, 0, - 0, 0, 0, 0, 0, 532, 0, 0, 442, 0, - 0, 0, 580, 0, 514, 487, 770, 0, 0, 512, - 451, 547, 499, 553, 535, 561, 506, 500, 336, 536, - 382, 465, 354, 356, 757, 384, 387, 391, 392, 475, - 476, 493, 520, 539, 540, 541, 381, 368, 513, 369, - 401, 370, 337, 373, 372, 374, 522, 375, 339, 494, - 544, 0, 397, 509, 460, 340, 459, 495, 543, 542, - 355, 570, 577, 578, 668, 0, 583, 771, 772, 773, - 592, 0, 501, 351, 348, 0, 0, 0, 377, 496, - 364, 366, 367, 365, 490, 492, 597, 598, 599, 601, - 0, 602, 603, 0, 0, 0, 0, 604, 669, 685, - 653, 622, 585, 677, 619, 623, 624, 418, 419, 420, - 688, 0, 0, 0, 576, 443, 444, 0, 389, 388, - 461, 341, 0, 0, 432, 417, 502, 347, 385, 434, - 422, 445, 446, 447, 448, 395, 331, 332, 765, 378, - 483, 690, 727, 728, 615, 0, 678, 616, 625, 371, - 650, 662, 661, 477, 575, 0, 673, 676, 605, 764, - 0, 670, 684, 769, 683, 761, 489, 0, 519, 681, - 628, 0, 674, 647, 648, 0, 675, 643, 679, 0, - 617, 0, 586, 589, 618, 703, 704, 705, 338, 588, - 707, 708, 709, 710, 711, 712, 713, 706, 558, 651, - 627, 654, 567, 630, 629, 2417, 0, 665, 584, 666, - 667, 471, 472, 473, 474, 399, 691, 362, 587, 504, - 505, 0, 652, 0, 0, 0, 0, 0, 0, 0, - 0, 657, 658, 655, 774, 0, 714, 715, 0, 0, - 581, 582, 394, 2419, 600, 402, 361, 488, 396, 565, - 425, 0, 593, 659, 594, 507, 508, 718, 723, 719, - 720, 722, 743, 478, 416, 421, 523, 433, 452, 510, - 564, 486, 515, 359, 554, 525, 458, 644, 672, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 319, 320, 0, 0, 0, 0, 0, 0, 2394, 0, - 0, 0, 0, 0, 0, 0, 699, 698, 697, 696, - 695, 694, 693, 692, 0, 0, 641, 376, 762, 756, - 763, 744, 747, 746, 721, 0, 333, 621, 450, 503, - 393, 686, 687, 0, 742, 278, 279, 280, 281, 282, - 283, 284, 285, 326, 286, 287, 288, 289, 290, 291, - 292, 297, 298, 299, 300, 301, 302, 303, 304, 689, - 295, 296, 305, 306, 307, 308, 309, 310, 311, 312, - 313, 314, 315, 316, 317, 318, 0, 0, 2410, 0, - 327, 748, 749, 750, 751, 752, 0, 0, 328, 329, - 330, 0, 0, 293, 294, 321, 533, 322, 323, 324, - 325, 0, 0, 571, 572, 573, 596, 0, 574, 556, - 620, 403, 334, 537, 563, 759, 760, 0, 0, 0, - 0, 0, 0, 0, 671, 682, 716, 717, 0, 730, - 731, 733, 735, 734, 737, 530, 531, 745, 0, 0, - 426, 349, 350, 479, 480, 517, 0, 427, 428, 429, - 431, 430, 739, 740, 741, 738, 454, 516, 538, 524, - 758, 466, 0, 766, 611, 612, 767, 726, 0, 335, - 0, 0, 0, 0, 0, 0, 0, 2398, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2404, 0, + 0, 2422, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2414, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2392, 2426, - 0, 0, 2393, 2395, 2397, 0, 2399, 2400, 2401, 2405, - 2406, 2407, 2409, 2412, 2413, 2414, 0, 0, 0, 0, - 0, 0, 0, 2402, 2411, 2403, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2402, 0, 0, 0, 0, + 2419, 0, 0, 0, 0, 0, 2408, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2395, 0, + 0, 0, 2394, 0, 0, 0, 2396, 2430, 0, 0, + 2397, 2399, 2401, 0, 2403, 2404, 2405, 2409, 2410, 2411, + 2413, 2416, 2417, 2418, 0, 0, 2412, 0, 0, 0, + 0, 2406, 2415, 2407, 2402, 2400, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2408, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2396, 2430, 0, 0, 2397, + 2399, 2401, 0, 2403, 2404, 2405, 2409, 2410, 2411, 2413, + 2416, 2417, 2418, 0, 0, 2422, 0, 0, 0, 0, + 2406, 2415, 2407, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 2418, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2422, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2419, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2395, 0, 0, 0, 2394, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2415, 0, 0, 0, 0, 0, 0, 0, + 2412, 0, 0, 0, 0, 0, 0, 0, 0, 2400, + 0, 0, 0, 2419, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2391, 0, 0, 0, 2390, 0, 0, 0, 0, 0, + 0, 2395, 0, 0, 0, 2394, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 2408, 0, - 0, 0, 0, 0, 0, 0, 0, 2396, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2412, + 0, 0, 0, 0, 0, 0, 0, 0, 2400, } var yyPact = [...]int{ - 5356, -1000, -1000, -390, -1000, -1000, -1000, -1000, -1000, 19554, + 5118, -1000, -1000, -387, -1000, -1000, -1000, -1000, -1000, 19566, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 70689, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 70401, -1000, -1000, -1000, -1000, + -1000, -1000, 591, 70689, 1197, -380, -1000, 3610, 1275, -1000, + -1000, -1000, -1000, 432, 68469, 21808, 70689, 777, 773, 75869, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 556, 70401, 1235, -383, -1000, 3836, 1295, -1000, - -1000, -1000, -1000, 441, 68193, 21784, 70401, 792, 788, 75553, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1214, 1210, + 5888, -1000, 75129, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 1081, 6234, 69949, 15068, 3632, -223, -1000, 2184, + -4, 3527, 557, 28, 27, 764, 1476, 1516, 1888, 1676, + 70689, 1438, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 4964, 38859, 69209, 1358, 4145, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1250, 1249, - 5606, -1000, 74817, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 1084, 5099, 69665, 15080, 3560, -227, -1000, 2191, - -34, 3468, 523, 50, 48, 777, 1471, 1488, 1699, 1283, - 70401, 1451, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 5950, 38743, 68929, 1321, 4144, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 537, 1201, 1358, + 27761, 209, 206, 2184, 1783, -1000, -1000, -99, 5194, -1000, + 2115, 5118, 249, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 15068, 15068, 19566, -427, 19566, 15068, + 70689, 70689, -1000, -1000, -1000, -1000, 1197, -380, 68469, 1081, + 6234, 15068, 3632, 3527, 557, 28, 27, 764, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 558, 1241, 1321, - 27705, 129, 122, 2191, 1895, -1000, -1000, -101, 497, -1000, - 2422, 5356, 241, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 15080, 15080, 19554, -424, 19554, 15080, - 70401, 70401, -1000, -1000, -1000, -1000, 1235, -383, 68193, 1084, - 5099, 15080, 3560, 3468, 523, 50, 48, 777, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -99, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -101, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, @@ -9905,15 +9970,15 @@ var yyPact = [...]int{ -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 122, -1000, -1000, + 206, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 4145, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 4144, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, @@ -9924,515 +9989,516 @@ var yyPact = [...]int{ -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 458, -1000, 2250, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 498, -1000, 2193, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3259, 3942, + 2248, 70689, 3526, -1000, -1000, -1000, -1000, -1000, 2184, 4386, + 67729, -1000, -1000, 4332, -1000, 70689, 180, 70689, 337, 2610, + -1000, 848, 753, 534, 1842, 482, 2229, -1000, -1000, -1000, + -1000, -1000, -1000, 3635, 942, 4331, -1000, 70689, 70689, 70689, + 3964, 70689, 70689, 474, 4261, -1000, 2, -118, -344, -355, + 3981, 2419, 2419, 4313, 4313, 4258, 4257, 995, 991, 988, + 2419, 854, -1000, 2520, 2520, 2520, 2520, 2419, 596, 998, + 4264, 4264, 214, 2520, 194, 2419, 2419, 194, 2419, 2419, + 650, -1000, 2566, 676, 346, -291, -1000, -1000, -1000, 2520, + 2520, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 4238, 4237, + -1000, 407, 981, -1000, 5505, 4167, 1937, 1213, 3980, -1000, + -1000, 3941, -1000, 504, 758, 466, 968, 588, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 455, -1000, 4244, -1000, -1000, + 492, -1000, -1000, 468, -1000, -1000, -1000, 204, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -46, + 70689, -1000, -1000, 1581, 2980, 15068, 2675, -1000, 4378, 2394, + -1000, -1000, -1000, 7574, 18813, 18813, 18813, 18813, 70689, -1000, + -1000, 3804, 15068, 3940, 3939, 3937, 3936, -1000, -1000, -1000, + -1000, -1000, -1000, 3935, 2214, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 2868, -1000, -1000, -1000, 18064, -1000, + 3934, 3933, 3932, 3931, 3930, 3926, 3925, 3924, 3922, 3918, + 3917, 3916, 3912, 3907, 3906, 3720, 21057, 3905, 3525, 3524, + 3904, 3903, 3902, 3522, 3901, 3900, 3899, 3720, 3720, 3898, + 3897, 3896, 3894, 3893, 3887, 3886, 3885, 3881, 3879, 3878, + 3877, 3875, 3873, 3872, 3869, 3868, 3867, 3866, 3865, 3863, + 3852, 3850, 3849, 3848, 3847, 3846, 3845, 3843, 3842, 3840, + 3838, 3836, 3834, 3833, 3830, 3824, 3821, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 3107, 3982, 2183, 70401, 3465, - -1000, -1000, -1000, -1000, -1000, 2191, 4369, 67457, -1000, -1000, - 4340, -1000, 70401, 181, 70401, 310, 2527, -1000, 812, 791, - 767, 1317, 469, 2182, -1000, -1000, -1000, -1000, -1000, -1000, - 3862, 922, 4339, -1000, 70401, 70401, 70401, 3994, 70401, 70401, - 454, 4261, -1000, -1, -81, -347, -364, 4008, 2436, 2436, - 4317, 4317, 4259, 4257, 975, 973, 966, 2436, 843, -1000, - 2475, 2475, 2475, 2475, 2436, 532, 985, 4268, 4268, 44, - 2475, 40, 2436, 2436, 40, 2436, 2436, 612, -1000, 2480, - 671, 292, -298, -1000, -1000, -1000, 2475, 2475, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 4234, 4233, -1000, 408, 956, - -1000, 6271, 4157, 1932, 1236, 4007, -1000, -1000, 3979, -1000, - 478, 578, 275, 859, 553, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 320, -1000, 4241, -1000, -1000, 463, -1000, -1000, - 465, -1000, -1000, -1000, 78, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -38, 70401, -1000, -1000, - 1577, 2624, 15080, 2730, -1000, 5012, 2243, -1000, -1000, -1000, - 7626, 18805, 18805, 18805, 18805, 70401, -1000, -1000, 3857, 15080, - 3978, 3977, 3976, 3975, -1000, -1000, -1000, -1000, -1000, -1000, - 3974, 2174, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 2803, -1000, -1000, -1000, 18060, -1000, 3973, 3970, 3965, - 3964, 3962, 3961, 3960, 3959, 3958, 3957, 3956, 3955, 3954, - 3952, 3951, 3737, 21037, 3950, 3460, 3459, 3949, 3948, 3946, - 3456, 3936, 3935, 3934, 3737, 3737, 3932, 3930, 3929, 3928, - 3926, 3925, 3920, 3918, 3917, 3914, 3913, 3911, 3910, 3908, - 3905, 3904, 3901, 3899, 3898, 3897, 3896, 3895, 3893, 3892, - 3891, 3889, 3888, 3886, 3884, 3883, 3882, 3881, 3877, 3875, - 3874, 3871, 3870, 3869, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2191, 3798, - 4382, 1993, -1000, 3867, 4331, 1489, -1000, 4227, 4205, 4203, - 4199, -289, 3865, 3012, -1000, -1000, 121, 70401, 70401, 334, - 70401, -308, 454, 641, -118, -121, 636, -124, 1171, -1000, - 555, -1000, -1000, 1437, -1000, 1428, 74081, 1156, -1000, -1000, - 70401, 1064, 1064, 1064, -327, 306, 1064, 70401, 666, 1110, - 1308, 1064, 1064, 1064, 1064, 1131, 1064, 1229, 1226, 1223, - 1064, -67, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2526, - 2516, 4074, 1019, 67457, 67457, 70401, -1000, 1969, 70401, -1000, - 3834, 1396, -1000, -1000, -1000, -1000, 57147, -328, 293, 1084, - 1084, 70401, 1084, 70401, 570, 233, 70401, 1084, 1084, 1084, - 70401, 1091, -370, 3, 73345, 72609, 3241, 408, 948, 945, - 1984, 2481, -1000, 2445, 70401, 70401, 2445, 2445, 32119, 31383, - -1000, 70401, -1000, 4331, 1489, 3718, 2173, 4369, 4369, 3798, - -1000, -1000, 70401, -227, -125, 1084, 1084, -330, 290, 1084, - 1084, 1084, 1084, 1084, 1222, 1084, 1084, 1084, 70401, 1084, - 66721, 1084, 630, 1084, 1084, 1084, 12100, 2422, -1000, -1000, + -1000, 2184, 3779, 4373, 1999, -1000, 3820, 4359, 1823, -1000, + 4228, 4214, 4208, 4204, -282, 3817, 3158, -1000, -1000, 128, + 70689, 70689, 339, 70689, -302, 474, 670, -114, -115, 666, + -119, 1130, -1000, 623, -1000, -1000, 1443, -1000, 1415, 74389, + 1165, -1000, -1000, 70689, 1080, 1080, 1080, -323, 333, 1080, + 70689, 682, 1146, 1334, 1080, 1080, 1080, 1080, 1108, 1080, + 1194, 1193, 1188, 1080, -62, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 2600, 2596, 4043, 1044, 67729, 67729, 70689, + -1000, 2094, 70689, -1000, 3791, 1392, -1000, -1000, -1000, -1000, + 57363, -326, 329, 1081, 1081, 70689, 1081, 70689, 648, 236, + 70689, 1081, 1081, 1081, 70689, 1097, -366, 72, 73649, 72909, + 3478, 407, 977, 973, 2150, 2515, -1000, 2485, 70689, 70689, + 2485, 2485, 32199, 31459, -1000, 70689, -1000, 4359, 1823, 3703, + 2275, 4386, 4386, 3779, -1000, -1000, 70689, -223, -121, 1081, + 1081, -327, 314, 1081, 1081, 1081, 1081, 1081, 1187, 1081, + 1081, 1081, 70689, 1081, 66989, 1081, 657, 1081, 1081, 1081, + 12072, 2115, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 19566, 2748, 2715, 248, 3, + -345, 342, -1000, -1000, 70689, 4117, 2355, -1000, -1000, -1000, + 3777, 3770, -1000, 3776, 3776, 3776, 3776, 1379, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3776, 3776, + 3776, 3776, 3776, 3776, 3775, 3814, -1000, -1000, 3772, 3772, + 3772, 3770, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1501, 3768, 3769, + 3769, 3768, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 19554, 2829, 2683, 240, 46, -346, 327, -1000, -1000, - 70401, 4126, 2299, -1000, -1000, -1000, 3818, 3809, -1000, 3814, - 3814, 3814, 3814, 1385, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 3814, 3814, 3814, 3814, 3814, 3814, - 3812, 3864, -1000, -1000, 3811, 3811, 3811, 3809, -1000, -1000, + 70689, 4353, -1000, -1000, 15068, 70689, 1160, 4143, 4359, 4123, + 4264, 4306, 1039, 3102, -1000, -338, 70689, 441, -1000, 2212, + 3157, 3521, -1000, 482, -1000, 813, 482, -1000, 821, 821, + 2415, -1000, 1790, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 70689, -46, 704, -1000, -1000, -1000, 3475, 2147, -1000, 3813, + 721, -1000, -1000, -1000, -1000, 70689, 4264, -137, 4256, 2419, + 2607, 2561, 2419, -1000, -1000, -1000, 964, 4313, 18813, 18813, + -1000, -1000, 15068, -1000, 348, 66249, 2520, 2419, 2419, -1000, + -1000, 70689, -1000, -1000, -1000, 2520, 70689, 2520, 2520, 4313, + 2520, -1000, -1000, -1000, 2419, 2419, -1000, -1000, 15068, -1000, + -1000, 2520, 2520, -1000, -1000, 4313, 70689, 202, 4313, 4313, + 103, -1000, -1000, 70689, -1000, 2419, 3519, -1000, 70689, 70689, + 1080, 70689, -1000, 70689, 70689, -1000, -1000, -1000, 834, 1728, + 2149, -1000, 369, 6458, 55143, 407, 55143, 70689, -1000, -1000, + -1000, -1000, -1000, -1000, 201, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 1538, 3806, 3808, 3808, 3806, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 503, + -1000, 4330, 15068, 15068, 15068, 15068, -1000, 1121, 17315, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 18813, 18813, 18813, 18813, + 18813, 18813, 18813, 18813, 18813, 18813, 18813, 18813, 18813, 18813, + 3803, 2489, 18813, 18813, 18813, 18813, 18813, 362, 34419, 2275, + 3410, 2102, 382, 2341, 2341, 2341, 2341, 15068, -1000, 2637, + 2980, 15068, 15068, 15068, 15068, 42559, 70689, -1000, -1000, 7574, + 5302, 15068, 15068, 5703, 18813, 15068, 4202, 15068, 15068, 15068, + 3700, 8323, 70689, 15068, -1000, 3698, 3697, -1000, -1000, 2822, + 15068, -1000, -1000, 15068, -1000, -1000, 15068, 18813, 15068, -1000, + 15068, 15068, 15068, -1000, -1000, 1803, 1803, 1301, 4202, 4202, + 4202, 2507, 15068, 15068, 4202, 4202, 4202, 2496, 4202, 4202, + 4202, 4202, 4202, 4202, 4202, 4202, 4202, 4202, 4202, 4202, + 4202, 3696, 3694, 3693, 3692, 15068, 3691, 15068, 15068, 15068, + 15068, 15068, 14319, 4359, 2184, 4264, -223, -1000, 12821, 4123, + 4264, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -286, 3812, 70689, 3517, 3516, -395, -398, + 1494, -398, 2211, -1000, -303, 1456, 330, 70689, -1000, 3513, + 3153, 70689, 3510, 3152, 268, 265, 70689, 70689, 70689, 0, + 1470, 1421, 1431, -1000, -1000, 70689, 72169, -1000, 70689, 2644, + 70689, 70689, 1080, 1080, 70689, 4192, -1000, 70689, 70689, 1080, + 1080, -1000, 64029, 3509, 55143, 70689, 70689, 407, 70689, 70689, + 1080, 1080, 1080, 70689, -1000, 4115, 55143, 4065, 3731, 3811, + 1044, 1044, -1000, 70689, 2094, 4186, 70689, 1097, -1000, 1345, + -1000, -1000, -1000, -1000, 50699, 50699, 24039, 50699, -1000, 251, + 70689, 70689, 70689, 70689, 6504, 70689, 480, 4164, 1255, 64029, + 65509, 4235, -1000, 55143, 70689, 70689, 2092, -1000, 1158, 28501, + -1000, 70689, 1928, -1000, 67, -1000, -31, 72, 2485, 72, + 2485, 1157, -1000, 832, 680, 29979, 763, 55143, 9825, -1000, + -1000, 2485, 2485, 9825, 9825, 2270, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 2083, -1000, 283, 4264, -1000, -1000, -1000, + -1000, -1000, -1000, 4264, 1839, 4264, 1823, -1000, 1878, 3149, + 64769, 70689, 1081, 1081, 70689, 64029, 55143, 407, 70689, 1081, + 70689, 70689, 70689, -1000, 70689, -1000, 4155, 70689, 1081, 70689, + 70689, 70689, 2044, -1000, -1000, 29241, 2204, -1000, -1000, 2671, + -1000, 15068, 19566, -271, 15068, 19566, 19566, 15068, 19566, -1000, + 15068, 2226, -1000, -1000, 5824, -1000, -1000, 3148, -1000, 3147, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 70401, 4367, -1000, -1000, - 15080, 70401, 1153, 4142, 4331, 4131, 4268, 4310, 1007, 2769, - -1000, -341, 70401, 370, -1000, 2170, 3005, 3454, -1000, 469, - -1000, 807, 469, -1000, 669, 669, 2385, -1000, 1910, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 70401, -38, 2395, -1000, - -1000, -1000, 3395, 1972, -1000, 3863, 638, -1000, -1000, -1000, - -1000, 70401, 4268, -140, 4256, 2436, 2550, 2499, 2436, -1000, - -1000, -1000, 941, 4317, 18805, 18805, -1000, -1000, 15080, -1000, - 413, 65985, 2475, 2436, 2436, -1000, -1000, 70401, -1000, -1000, - -1000, 2475, 70401, 2475, 2475, 4317, 2475, -1000, -1000, -1000, - 2436, 2436, -1000, -1000, 15080, -1000, -1000, 2475, 2475, -1000, - -1000, 4317, 70401, 61, 4317, 4317, 47, -1000, -1000, 70401, - -1000, 2436, 3453, -1000, 70401, 70401, 1064, 70401, -1000, 70401, - 70401, -1000, -1000, -1000, 867, 1739, 1942, -1000, 568, 6246, - 54939, 408, 54939, 70401, -1000, -1000, -1000, -1000, -1000, -1000, - 54, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 3508, 3508, -1000, 3146, -1000, -1000, -1000, -1000, 3768, + 3143, -1000, -1000, 3141, -1000, -1000, -1000, -1000, -151, 3690, + 1581, -1000, 3507, 3505, 4264, -1000, -228, 4303, 15068, 1907, + 3810, 1081, -1000, 217, -406, 2593, 2592, 2589, 4249, 70689, + -1000, 4254, -1000, -1000, 482, -1000, -1000, -1000, 821, 724, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 2198, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 440, -1000, 4338, 15080, 15080, - 15080, 15080, -1000, 878, 17315, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 18805, 18805, 18805, 18805, 18805, 18805, 18805, 18805, - 18805, 18805, 18805, 18805, 18805, 18805, 3854, 2457, 18805, 18805, - 18805, 18805, 18805, 395, 34327, 2173, 3672, 1971, 373, 2220, - 2220, 2220, 2220, 15080, -1000, 2555, 2624, 15080, 15080, 15080, - 15080, 42423, 70401, -1000, -1000, 7626, 6604, 15080, 15080, 5500, - 18805, 15080, 4197, 15080, 15080, 15080, 3715, 8371, 70401, 15080, - -1000, 3713, 3712, -1000, -1000, 2784, 15080, -1000, -1000, 15080, - -1000, -1000, 15080, 18805, 15080, -1000, 15080, 15080, 15080, -1000, - -1000, 824, 824, 1193, 4197, 4197, 4197, 2478, 15080, 15080, - 4197, 4197, 4197, 2462, 4197, 4197, 4197, 4197, 4197, 4197, - 4197, 4197, 4197, 4197, 4197, 4197, 4197, 3711, 3708, 3704, - 3703, 15080, 3700, 15080, 15080, 15080, 15080, 15080, 14335, 4331, - 2191, 4268, -227, -1000, 12845, 4131, 4268, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -293, - 3861, 70401, 3452, 3446, -396, -398, 1469, -398, 2168, -1000, - -309, 1462, 316, 70401, -1000, 3443, 3004, 70401, 3441, 2999, - 265, 261, 70401, 70401, 70401, -15, 1482, 1434, 1444, -1000, - -1000, 70401, 71873, -1000, 70401, 2579, 70401, 70401, 1064, 1064, - 70401, 4191, -1000, 70401, 70401, 1064, 1064, -1000, 63777, 3440, - 54939, 70401, 70401, 408, 70401, 70401, 1064, 1064, 1064, 70401, - -1000, 4090, 54939, 4078, 3781, 3860, 1019, 1019, -1000, 70401, - 1969, 4183, 70401, 1091, -1000, 3085, -1000, -1000, -1000, -1000, - 50519, 50519, 24003, 50519, -1000, 245, 70401, 70401, 70401, 70401, - 6485, 70401, 489, 4155, 1262, 63777, 65249, 4231, -1000, 54939, - 70401, 70401, 1967, -1000, 1151, 28441, -1000, 70401, 1875, -1000, - -11, -1000, -18, 3, 2445, 3, 2445, 1149, -1000, 844, - 458, 29911, 754, 54939, 9865, -1000, -1000, 2445, 2445, 9865, - 9865, 2242, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1961, - -1000, 282, 4268, -1000, -1000, -1000, -1000, -1000, -1000, 4268, - 1529, 4268, 1489, -1000, 1855, 2996, 64513, 70401, 1084, 1084, - 70401, 63777, 54939, 408, 70401, 1084, 70401, 70401, 70401, -1000, - 70401, -1000, 4150, 70401, 1084, 70401, 70401, 70401, 1807, -1000, - -1000, 29177, 2154, -1000, -1000, 2552, -1000, 15080, 19554, -275, - 15080, 19554, 19554, 15080, 19554, -1000, 15080, 2039, -1000, -1000, - 5271, -1000, -1000, 2995, -1000, 2989, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3438, 3438, -1000, - 2986, -1000, -1000, -1000, -1000, 3806, 2983, -1000, -1000, 2981, - -1000, -1000, -1000, -1000, -156, 3697, 1577, -1000, 3434, 3427, - 4268, -1000, -233, 4307, 15080, 2069, 3858, 1084, -1000, 197, - -405, 2515, 2514, 2513, 4247, 70401, -1000, 4255, -1000, -1000, - 469, -1000, -1000, -1000, 669, 732, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 2107, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 5910, -103, -104, - 1958, -1000, 70401, 70401, -1000, 70401, -1000, -154, -1000, -1000, - 2977, 4269, 70401, 4317, 70401, -331, 4317, 4317, 70401, -334, - 4317, 408, -1000, 2243, 2243, 2624, 70401, 70401, 70401, 3993, - 70401, 212, 70401, 4317, 4317, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 2475, 4317, 4317, 1907, 2436, 2475, -1000, -1000, - 2475, -405, -1000, 2475, -1000, -1000, -1000, -405, 2104, -405, - 70401, -1000, -1000, -1000, -1000, -1000, 568, 54939, 60091, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 1803, -1000, -1000, -1000, - 1140, 380, 2382, -1000, -1000, 253, 256, 332, 1289, 3425, - 2624, -1000, 2578, 2635, -1000, 939, -1000, -1000, -1000, -1000, - 3857, -1000, -1000, -1000, 3302, 4709, -1000, 2595, 2595, 2250, - 2250, 2250, 2250, 2250, 2615, 2615, 2243, 2243, -1000, -1000, - -1000, 7626, 3854, 18805, 18805, 18805, 18805, 1240, 1240, 5425, - 5206, 2220, -1000, -1000, 2204, 2204, -1000, -1000, -1000, -1000, - 15080, 219, 2544, -1000, 15080, 2938, 2124, 2906, 1759, 2378, - -1000, 3809, 15080, 2091, 4417, -1000, -1000, -1000, -1000, -1000, + -1000, 6112, -100, -101, 2079, -1000, 70689, 70689, -1000, 70689, + -1000, -147, -1000, -1000, 3140, 4265, 70689, 4313, 70689, -328, + 4313, 4313, 70689, -332, 4313, 407, -1000, 2394, 2394, 2980, + 70689, 70689, 70689, 3961, 70689, 222, 70689, 4313, 4313, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 2520, 4313, 4313, 2060, + 2419, 2520, -1000, -1000, 2520, -406, -1000, 2520, -1000, -1000, + -1000, -406, 2197, -406, 70689, -1000, -1000, -1000, -1000, -1000, + 369, 55143, 60323, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 1923, -1000, -1000, -1000, 1151, 379, 2414, -1000, -1000, 235, + 261, 344, 1299, 3504, 2980, -1000, 2665, 2697, -1000, 1035, + -1000, -1000, -1000, -1000, 3804, -1000, -1000, -1000, 2930, 5408, + -1000, 2413, 2413, 2279, 2279, 2279, 2279, 2279, 2583, 2583, + 2394, 2394, -1000, -1000, -1000, 7574, 3803, 18813, 18813, 18813, + 18813, 1290, 1290, 5335, 5293, 2341, -1000, -1000, 2348, 2348, + -1000, -1000, -1000, -1000, 15068, 219, 2668, -1000, 15068, 3051, + 2126, 2991, 2088, 2411, -1000, 3770, 15068, 2195, 3563, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 3694, 3685, 2864, 4337, 5098, - 3681, 15080, -1000, -1000, 2365, 2358, 2329, -1000, 2994, 13590, - -1000, -1000, -1000, 3679, 2090, 3674, -1000, -1000, -1000, 3671, - 2316, 1710, 3668, 3240, 3666, 3660, 3656, 3653, 1949, 1948, - 1941, -1000, -1000, -1000, -1000, 15080, 15080, 15080, 15080, 3651, - 2312, 2311, 15080, 15080, 15080, 15080, 3649, 15080, 15080, 15080, - 15080, 15080, 15080, 15080, 15080, 15080, 15080, 70401, 15080, 15080, - 51, 51, 51, 51, 3661, 51, 2233, 2195, 3657, 3646, - 2010, 1934, 1933, 4268, 4331, -1000, -1000, 2308, -1000, 2624, - -1000, -1000, 4307, -291, 3853, 2975, 1929, -1000, -1000, -380, - 3325, 1136, 70401, -310, 70401, 1136, 70401, 70401, 2508, 1136, - 70401, -312, 3422, -1000, -1000, 3417, -1000, -1000, 70401, 70401, - 70401, 70401, -130, 4139, 4136, -1000, -1000, 1461, 1417, 1431, - -1000, 70401, -1000, 3410, 4148, 4254, 1159, 70401, 70401, -111, - 70401, 3848, 3845, 2086, 70401, 70401, 427, -1000, -1000, 70401, - 1816, -1000, 380, -50, 800, 1618, 3992, 1128, 70401, 70401, - 70401, 4181, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 4006, -228, -1000, 26969, 76289, 70401, 3781, 3781, -1000, 3844, - 2307, -1000, 63041, 57147, 57147, 57147, 57147, 57147, 57147, 57147, - -1000, 4053, 4023, 3843, -1000, 4041, 4040, 4031, 602, 4051, - 4016, 3834, -1000, 55675, -1000, -1000, -1000, 2173, 2304, -1000, - 1277, 3196, 1315, 15080, 4226, 4106, 4179, 3834, 1878, -1000, - -1000, -1000, 4309, 1724, 1038, 1038, 1526, 1050, 4308, 24739, - -1000, 2345, 1719, 1135, 4110, 474, -1000, 2345, -152, 1018, - 2345, 2345, 2345, 2345, 2345, 2345, 2345, 918, 901, 2345, - 2345, 2345, 2345, 2345, 2345, 2345, 2345, 2345, 2345, 2345, - 1484, 2345, 2345, 2345, 2345, 2345, -1000, 2345, 3842, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 964, 860, -1000, -1000, - 354, 408, 1134, 11, 10, 423, 4219, 508, -1000, 506, - 1816, 833, 4215, 551, 70401, 70401, 2156, 1646, 2445, 3, - -35, -1000, -1000, 1875, 9865, 1875, 9865, 2973, -1000, -1000, - 1133, -1000, -1000, 1618, -1000, 70401, 70401, -1000, -1000, 3841, - 2506, -1000, -1000, 21037, -1000, 9865, 9865, -1000, -1000, 38007, - 70401, -1000, -40, -1000, -25, 4307, 3648, -1000, -1000, -1000, - -1000, -227, 3636, -1000, -1000, -1000, -1000, -1000, -1000, -321, - -1000, -1000, 70401, -1000, 70401, 70401, 1582, -1000, 1866, 1618, - 4005, 70401, 1582, -1000, -1000, 3840, 70401, -1000, 3404, -1000, - 4336, -321, 4317, 12100, -1000, 28441, -1000, -1000, 62299, -1000, - 61563, 2538, -1000, 19554, 2657, 236, -1000, 322, -352, 235, - 2605, 232, 2624, -1000, -1000, 3631, 3609, 3608, 2303, -1000, - 2298, 3607, -1000, 2296, 2295, 2970, -1000, -373, 36, 4307, - 3403, 4131, -201, 1861, -1000, 2846, -386, -228, -1000, 26222, - -1000, 76289, 70401, 70401, 3397, 2856, 4177, 3034, -341, 15080, - 60827, 15080, 1374, 2082, 263, -1000, -1000, -1000, 70401, 3395, - -1000, 2294, -1000, -1000, -1000, -1000, 2969, -1000, -1000, 2436, - 70401, -1000, -1000, 1617, 70401, 47575, -1000, -1000, -1000, -1000, - 70401, -1000, 408, -1000, 54939, -1000, 2436, -1000, -1000, 4317, - -1000, -1000, 15080, 15080, 4317, 2436, 2436, -1000, 2475, -1000, - 70401, -1000, -405, 60091, 1752, -1000, 1127, 2078, 2077, -1000, - 54939, 466, 54939, -1000, -1000, 4286, -1000, 70401, 4132, -1000, - -1000, -1000, 3325, 2505, -403, 70401, -1000, -1000, -1000, -1000, - -1000, -1000, 2287, -1000, 1240, 1240, 5425, 4956, -1000, 18805, - -1000, 18805, -1000, -1000, -1000, -1000, 3640, -1000, 2537, -1000, - 15080, 2619, 395, 15080, 395, 2707, 33591, 42423, -131, 4168, - 3625, 70401, 15080, -1000, -1000, 15080, 15080, 18805, -1000, 3600, - -1000, -1000, -1000, -1000, 15080, 15080, 2802, -1000, 70401, -1000, - -1000, -1000, -1000, 33591, -1000, 18805, -1000, -1000, -1000, -1000, - 15080, 15080, 15080, 1747, 1747, 3565, 2286, 51, 51, 51, - 3552, 3543, 3539, 2278, 51, 3526, 3519, 3511, 3494, 3442, - 3426, 3420, 3384, 3347, 3322, 2275, 3311, 1782, -1000, 3839, - -1000, -1000, -1000, 51, -1000, 51, 15080, 51, 15080, 51, - 51, 15080, 2741, 16570, 4307, 4268, 12845, -1000, 4131, -1000, - 3837, 1015, 1860, 2968, 3392, 156, -1000, 2501, -1000, 547, - -1000, 70401, 4335, -1000, 2065, 3390, 59355, -1000, 1470, 70401, - -1000, -1000, 4332, 4329, -1000, -1000, 70401, 70401, 70401, -1000, - -1000, -1000, 1402, -1000, 3389, -1000, 535, 490, 2862, 4358, - 4357, 2553, 3388, 443, 1658, 22520, 70401, 3834, 3834, 324, - 2345, 705, 803, 54939, 933, -1000, 58619, 3020, 2498, 4002, - 1572, 4123, 70401, 57883, 2087, 1129, 3833, 736, 5271, -386, - 4128, 1579, -1000, 3832, -1000, 2240, 4071, -1000, 1831, -1000, - 2487, 2239, 2217, 2486, -386, -386, 5356, -1000, 70401, 70401, - 1725, -1000, 2063, 1495, 3986, 3986, 3986, -1000, 3986, -1000, - -1000, -1000, -1000, 4049, 3386, -1000, 4048, -1000, 4047, -1000, - 4044, -1000, -1000, -1000, -1000, -1000, -1000, 28441, -1000, 57147, - -1000, 1315, -1000, 4252, 1336, 1336, 1336, 3606, 37271, 36535, - -1000, -1000, 736, 5271, 4176, 6827, 846, 3727, -1000, 70401, - -1000, -1000, -1000, 1098, -1000, 1336, 1064, 70401, 2663, 1336, - 2662, 3831, -1000, -1000, 70401, 70401, 70401, 70401, -1000, -1000, - 70401, -1000, 70401, 70401, 70401, 70401, 70401, 56411, -1000, 70401, - 70401, -1000, 70401, 2646, 70401, 2645, 4146, -1000, 2345, 2345, - 1354, -1000, -1000, 815, -1000, 56411, 2967, 2963, 2962, 2948, - 3385, 3383, 3382, 2345, 2345, 2947, 3381, 55675, 3378, 1648, - 2946, 2944, 2943, 2959, 3367, 1281, -1000, 3366, 2957, 2942, - 2909, 70401, 3830, 3249, -1000, -1000, 2862, 3350, 3829, 2936, - 3348, 1200, 408, 3339, 3999, 324, 2345, 482, 70401, 2473, - 2472, 803, 798, 798, 797, -52, 30647, -1000, -1000, -1000, - 70401, 70401, 9865, -1000, -1000, -20, -26, -1000, -1000, -1000, - -1000, 54939, 3338, 754, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 4131, -1000, 1855, -1000, -1000, -1000, -1000, -1000, 70401, - 1046, 22520, -1000, -1000, 70401, -1000, 4315, -1000, 1835, -1000, - -1000, 2041, -1000, 2570, -391, 19554, 2408, 2438, -1000, 15080, - 19554, 15080, -276, 491, -279, -1000, -1000, -1000, -1000, 3337, - -1000, -1000, -1000, 2934, -1000, 2933, -1000, -32, 190, 277, - 4131, 267, -1000, 4365, 15080, 4098, -1000, -1000, -1000, 12100, - 1579, 2216, 4069, 1831, 4331, 2215, -1000, -1000, -1000, -1000, - 4246, -1000, -1000, -1000, -1000, -1000, 213, -413, -415, 209, - 3334, 70401, 2930, -1000, -1000, -1000, -1000, 4317, 2436, 4317, - 47575, 2436, 2389, -1000, -1000, -1000, -1000, 1618, -1000, 4317, - -1000, 2624, 2624, -405, 4317, 4317, 2436, -1000, -1000, 4328, - 54939, 408, 2264, 54203, -1000, 461, 1714, 805, 3328, -1000, - 1214, 149, 3327, 3325, -1000, -1000, -1000, -1000, 18805, 2243, - -1000, -1000, -1000, 2624, 15080, 3598, 2777, 3597, 3596, -1000, - -1000, 3814, 3814, -1000, 3809, 3811, 3809, 2204, 2204, 3589, - -1000, 3828, -1000, 4168, -1000, 2212, 2765, 3262, 5133, 51, - 3252, 3244, 15080, -1000, 3588, 5115, 1744, 1591, 3238, -71, - -185, 51, 51, -1000, -1000, -1000, -1000, 51, 51, 51, - 51, -1000, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 15080, 1008, -1000, -1000, 2184, -1000, - 2105, -1000, -1000, 3230, -66, -302, -68, -303, 4131, 4307, - -1000, -1000, 410, 3587, 5500, 1817, 809, 809, 3325, 3323, - 70401, 3321, -313, 70401, -1000, -416, -417, -314, 70401, 3318, - 70401, 70401, 33, 2545, 2673, -1000, 3314, -1000, -1000, 53467, - 70401, 70401, 71137, 858, 70401, 70401, 3313, -1000, -1000, 3827, - -1000, 3824, -157, 3821, -114, 3307, 3583, 1815, -1000, -1000, - 70401, -1000, -1000, -1000, 3580, 1811, -1000, -1000, -1000, 5271, - 3818, 3814, 3814, 1385, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 3814, 3814, 3814, 3814, 3814, 3814, 3812, - -1000, -1000, 3811, 3811, 3811, 3809, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 1538, 3806, 3808, 3808, 3806, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3689, + 3688, 2758, 4329, 5134, 3686, 15068, -1000, -1000, 2404, 2385, + 2379, -1000, 2838, 13570, -1000, -1000, -1000, 3684, 2192, 3683, + -1000, -1000, -1000, 3682, 2376, 1837, 3681, 2518, 3679, 3678, + 3677, 3676, 2012, 2003, 1996, -1000, -1000, -1000, -1000, 15068, + 15068, 15068, 15068, 3672, 2370, 2369, 15068, 15068, 15068, 15068, + 3671, 15068, 15068, 15068, 15068, 15068, 15068, 15068, 15068, 15068, + 15068, 70689, 15068, 15068, 220, 220, 220, 220, 3366, 220, + 1947, 1919, 3352, 3347, 2119, 1995, 1990, 4264, 4359, -1000, + -1000, 2362, -1000, 2980, -1000, -1000, 4303, -284, 3802, 3136, + 1934, -1000, -1000, -377, 3442, 1134, 70689, -304, 70689, 1134, + 70689, 70689, 2586, 1134, 70689, -306, 3503, -1000, -1000, 3502, + -1000, -1000, 70689, 70689, 70689, 70689, -126, 4139, 4128, -1000, + -1000, 1454, 1409, 1440, -1000, 70689, -1000, 3501, 4151, 4253, + 1195, 70689, 70689, -106, 70689, 3801, 3800, 2190, 70689, 70689, + 411, -1000, -1000, 70689, 1956, -1000, 379, -52, 781, 1662, + 3959, 1167, 70689, 70689, 70689, 4185, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 3978, -224, -1000, 27021, 76609, 70689, + 3731, 3731, -1000, 3799, 2352, -1000, 63289, 57363, 57363, 57363, + 57363, 57363, 57363, 57363, -1000, 4015, 3997, 3798, -1000, 4002, + 4000, 3999, 712, 4014, 3987, 3791, -1000, 55883, -1000, -1000, + -1000, 2275, 2350, -1000, 1486, 3413, 1304, 15068, 4234, 4141, + 4184, 3791, 1931, -1000, -1000, -1000, 4305, 2491, 1061, 1061, + 1327, 865, 4304, 24779, -1000, 2440, 1804, 1133, 4081, 484, + -1000, 2440, -145, 1043, 2440, 2440, 2440, 2440, 2440, 2440, + 2440, 938, 936, 2440, 2440, 2440, 2440, 2440, 2440, 2440, + 2440, 2440, 2440, 2440, 1491, 2440, 2440, 2440, 2440, 2440, + -1000, 2440, 3797, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 971, 886, -1000, -1000, 351, 407, 1129, 91, 85, 397, + 4231, 544, -1000, 562, 1956, 897, 4217, 585, 70689, 70689, + 2085, 1809, 2485, 72, 163, -1000, -1000, 1928, 9825, 1928, + 9825, 3134, -1000, -1000, 1122, -1000, -1000, 1662, -1000, 70689, + 70689, -1000, -1000, 3796, 2584, -1000, -1000, 21057, -1000, 9825, + 9825, -1000, -1000, 38119, 70689, -1000, -47, -1000, -18, 4303, + 3670, -1000, -1000, -1000, -1000, -223, 3669, -1000, -1000, -1000, + -1000, -1000, -1000, -314, -1000, -1000, 70689, -1000, 70689, 70689, + 1625, -1000, 1917, 1662, 3976, 70689, 1625, -1000, -1000, 3794, + 70689, -1000, 3499, -1000, 4328, -314, 4313, 12072, -1000, 28501, + -1000, -1000, 62543, -1000, 61803, 2569, -1000, 19566, 2699, 241, + -1000, 338, -353, 239, 2653, 238, 2980, -1000, -1000, 3668, + 3666, 3664, 2349, -1000, 2339, 3662, -1000, 2338, 2336, 3133, + -1000, -370, 130, 4303, 3482, 4123, -197, 1890, -1000, 2789, + -382, -224, -1000, 26270, -1000, 76609, 70689, 70689, 3481, 2865, + 4183, 3195, -338, 15068, 61063, 15068, 1360, 2186, 303, -1000, + -1000, -1000, 70689, 3475, -1000, 2333, -1000, -1000, -1000, -1000, + 3129, -1000, -1000, 2419, 70689, -1000, -1000, 2146, 70689, 47739, + -1000, -1000, -1000, -1000, 70689, -1000, 407, -1000, 55143, -1000, + 2419, -1000, -1000, 4313, -1000, -1000, 15068, 15068, 4313, 2419, + 2419, -1000, 2520, -1000, 70689, -1000, -406, 60323, 1754, -1000, + 1114, 2182, 2172, -1000, 55143, 496, 55143, -1000, -1000, 4295, + -1000, 70689, 4126, -1000, -1000, -1000, 3442, 2577, -404, 70689, + -1000, -1000, -1000, -1000, -1000, -1000, 2323, -1000, 1290, 1290, + 5335, 5182, -1000, 18813, -1000, 18813, -1000, -1000, -1000, -1000, + 3338, -1000, 2521, -1000, 15068, 2663, 362, 15068, 362, 2256, + 33679, 42559, -128, 4138, 3333, 70689, 15068, -1000, -1000, 15068, + 15068, 18813, -1000, 3328, -1000, -1000, -1000, -1000, 15068, 15068, + 2752, -1000, 70689, -1000, -1000, -1000, -1000, 33679, -1000, 18813, + -1000, -1000, -1000, -1000, 15068, 15068, 15068, 1955, 1955, 3316, + 2322, 220, 220, 220, 3302, 3279, 3274, 2308, 220, 3261, + 3257, 3243, 3196, 3189, 3184, 3180, 3165, 3130, 3123, 2307, + 3112, 2097, -1000, 3793, -1000, -1000, -1000, 220, -1000, 220, + 15068, 220, 15068, 220, 220, 15068, 2933, 16566, 4303, 4264, + 12821, -1000, 4123, -1000, 3792, 1042, 1882, 3116, 3472, 158, + -1000, 2576, -1000, 584, -1000, 70689, 4327, -1000, 2162, 3471, + 59583, -1000, 1391, 70689, -1000, -1000, 4326, 4325, -1000, -1000, + 70689, 70689, 70689, -1000, -1000, -1000, 1406, -1000, 3470, -1000, + 481, 373, 3002, 4349, 4347, 2629, 3469, 451, 1658, 23288, + 70689, 3791, 3791, 246, 2440, 718, 866, 55143, 961, -1000, + 58843, 3054, 2570, 3972, 1426, 4108, 70689, 58103, 1825, 1159, + 3790, 740, 5824, -382, 4111, 1602, -1000, 3789, -1000, 2305, + 4037, -1000, 1874, -1000, 2564, 2297, 2293, 2563, -382, -382, + 5118, -1000, 70689, 70689, 2021, -1000, 2160, 806, 3952, 3952, + 3952, -1000, 3952, -1000, -1000, -1000, -1000, 4013, 3467, -1000, + 4011, -1000, 4010, -1000, 4009, -1000, -1000, -1000, -1000, -1000, + -1000, 28501, -1000, 57363, -1000, 1304, -1000, 4251, 1310, 1310, + 1310, 3661, 37379, 36639, -1000, -1000, 740, 5824, 4178, 6853, + 850, 3708, -1000, 70689, -1000, -1000, -1000, 1123, -1000, 1310, + 1310, 1080, 70689, 2765, 1310, 2760, 3788, -1000, -1000, 70689, + 70689, 70689, 70689, -1000, -1000, 70689, -1000, 70689, 70689, 70689, + 70689, 70689, 56623, -1000, 70689, 70689, -1000, 70689, 2750, 70689, + 2743, 4166, -1000, 2440, 2440, 1325, -1000, -1000, 845, -1000, + 56623, 3109, 3105, 3104, 3103, 3466, 3465, 3463, 2440, 2440, + 3100, 3460, 55883, 3459, 1599, 3099, 3094, 3092, 3008, 3458, + 1215, -1000, 3457, 3000, 2993, 2955, 70689, 3787, 3378, -1000, + -1000, 3002, 3456, 3786, 3089, 3455, 1233, 407, 3452, 3971, + 246, 2440, 538, 70689, 2559, 2557, 866, 794, 794, 779, + -54, 30719, -1000, -1000, -1000, 70689, 70689, 9825, -1000, -1000, + -11, -39, -1000, -1000, -1000, -1000, 55143, 3451, 763, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, 4123, -1000, 1878, -1000, + -1000, -1000, -1000, -1000, 70689, 1106, 23288, -1000, -1000, 70689, + -1000, 4311, -1000, 1875, -1000, -1000, 2158, -1000, 2527, -392, + 19566, 2587, 2454, -1000, 15068, 19566, 15068, -272, 514, -274, + -1000, -1000, -1000, -1000, 3450, -1000, -1000, -1000, 3088, -1000, + 3073, -1000, -21, 284, 332, 4123, 340, -1000, 4352, 15068, + 4070, -1000, -1000, -1000, 12072, 1602, 2292, 4034, 1874, 4359, + 2278, -1000, -1000, -1000, -1000, 4248, -1000, -1000, -1000, -1000, + -1000, 215, -416, -417, 211, 3449, 70689, 3068, -1000, -1000, + -1000, -1000, 4313, 2419, 4313, 47739, 2419, 2500, -1000, -1000, + -1000, -1000, 1662, -1000, 4313, -1000, 2980, 2980, -406, 4313, + 4313, 2419, -1000, -1000, 4323, 55143, 407, 2291, 54403, -1000, + 489, 1866, 814, 3448, -1000, 1186, 151, 3443, 3442, -1000, + -1000, -1000, -1000, 18813, 2394, -1000, -1000, -1000, 2980, 15068, + 3659, 2855, 3658, 3640, -1000, -1000, 3776, 3776, -1000, 3770, + 3772, 3770, 2348, 2348, 3636, -1000, 3785, -1000, 4138, -1000, + 2271, 2766, 3108, 5074, 220, 3098, 3062, 15068, -1000, 3633, + 5040, 2093, 2080, 2973, -69, -181, 220, 220, -1000, -1000, + -1000, -1000, 220, 220, 220, 220, -1000, 220, 220, 220, + 220, 220, 220, 220, 220, 220, 220, 220, 220, 15068, + 1041, -1000, -1000, 1871, -1000, 1800, -1000, -1000, 2934, -95, + -295, -96, -296, 4123, 4303, -1000, -1000, 384, 3615, 5703, + 1855, 790, 790, 3442, 3439, 70689, 3431, -308, 70689, -1000, + -419, -423, -309, 70689, 3428, 70689, 70689, 105, 2594, 2720, + -1000, 3426, -1000, -1000, 53663, 70689, 70689, 71429, 883, 70689, + 70689, 3425, -1000, -1000, 3783, -1000, 3781, -152, 3780, -111, + 3423, 3611, 1850, -1000, -1000, 70689, -1000, -1000, -1000, 3608, + 1844, -1000, -1000, -1000, 5824, 3777, 3776, 3776, 1379, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3776, 3776, + 3776, 3776, 3776, 3776, 3775, -1000, -1000, 3772, 3772, 3772, + 3770, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 1501, 3768, 3769, 3769, 3768, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 4175, - 4173, 3032, -1000, -1000, -1000, 35799, 70401, 798, -1000, -1000, - -1000, 937, 455, 2929, 770, -1000, 70401, 699, 546, 4085, - 2471, 3304, 70401, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 4123, -1000, 1483, -405, 70401, 686, 20301, - -1000, 3560, 70401, -1000, 70401, 52725, 23256, -1000, 2345, 23256, - 700, 2437, -1000, -1000, 2644, -228, 12845, 3795, 70401, -228, - 70401, 12845, -1000, 70401, -1000, 70401, 15080, -1000, -1000, 3575, - -1000, 1007, 1721, 170, 28441, 70401, -1000, 49783, 15080, -1000, - -1000, 15080, 3804, -1000, -1000, -1000, -1000, 3574, -1000, -1000, - -1000, -1000, -1000, 243, -1000, 3797, 4103, -1000, -1000, -1000, - -1000, 1802, 48311, -1000, 2468, -1000, -1000, 1800, 46839, -1000, - 2463, -1000, -1000, 700, -1000, 3560, -1000, 1177, 25475, 829, - 484, 473, -1000, 871, -1000, -1000, 1001, 4109, 5271, -1000, - 70401, -1000, 70401, -1000, 70401, 70401, 1064, 15080, 4109, 70401, - 1111, -1000, 1511, 645, 616, 1075, 1075, 1763, -1000, 4168, - -1000, -1000, 1755, -1000, -1000, -1000, -1000, 70401, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 33591, 33591, 4209, -1000, -1000, + -1000, -1000, -1000, -1000, 4176, 4174, 3173, -1000, -1000, -1000, + 35899, 70689, 794, -1000, -1000, -1000, 933, 508, 3064, 770, + -1000, 70689, 726, 576, 4053, 2556, 3420, 70689, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 4108, -1000, + 1764, -406, 70689, 717, 20317, -1000, 3632, 70689, -1000, 70689, + 52917, 22548, -1000, 2440, 22548, 732, 2499, -1000, -1000, 2737, + -224, 12821, 3674, 70689, -224, 70689, 12821, -1000, 70689, -1000, + 70689, 15068, -1000, -1000, 3604, -1000, 1039, 1845, 173, 28501, + 70689, -1000, 49959, 15068, -1000, -1000, 15068, 3767, -1000, -1000, + -1000, -1000, 3601, -1000, -1000, -1000, -1000, -1000, 245, -1000, + 3765, 4092, -1000, -1000, -1000, -1000, 1820, 48479, -1000, 2554, + -1000, -1000, 1811, 46999, -1000, 2550, -1000, -1000, 732, -1000, + 3632, -1000, 1945, 25519, 807, 663, 636, -1000, 923, -1000, + -1000, 1033, 4072, 5824, -1000, 70689, -1000, 70689, 70689, -1000, + 70689, 70689, 1080, 15068, 4072, 70689, 1112, -1000, 1478, 653, + 818, 1090, 1090, 1777, -1000, 4138, -1000, -1000, 1768, -1000, + -1000, -1000, -1000, 70689, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 33679, 33679, 4213, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3408, 3402, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 3285, 3281, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 70689, 2269, + -1000, 2549, 3400, -111, 9076, -1000, -1000, 1107, -1000, 3970, + 1177, 3173, 35899, 2526, 2485, 3399, 3395, 794, -1000, 3394, + 3393, -1000, 3054, 2525, 1172, 70689, -1000, 1661, 70689, 70689, + -1000, 1692, -1000, 2524, 1741, -1000, -1000, -1000, -1000, 1754, + 3600, -1000, -1000, 4110, -1000, -1000, -1000, -1000, 3595, 699, + 4309, 4302, 52177, -1000, -392, 2351, -1000, 2641, 233, 2636, + 70689, -1000, -1000, -1000, 3592, 3591, -1000, -231, 291, 4301, + 4300, 4110, -255, 3392, 465, -1000, -1000, 4083, 1575, 1738, + -224, 4264, -1000, 1507, -1000, -1000, -1000, -1000, -424, -1000, + -1000, -1000, 4313, -1000, 2419, 4313, 4313, -331, -1000, -1000, + -1000, -1000, 4313, 407, -1000, 1788, -1000, -1000, -1000, -1000, + -1000, -1000, 354, -1000, 70689, -1000, 1748, 137, -1000, 2980, + -1000, 362, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, 3222, -1000, -1000, -1000, 15068, -1000, -1000, -1000, + -1000, -1000, 2916, -1000, -1000, 15068, 15068, -1000, 3590, 3387, + 3586, 3386, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 70401, 2208, -1000, 2461, 3280, -114, 9120, -1000, - -1000, 1108, -1000, 3998, 1206, 3032, 35799, 2460, 2445, 3279, - 3278, 798, -1000, 3275, 3273, -1000, 3020, 2459, 1192, 70401, - -1000, 1603, 70401, 70401, -1000, 1765, -1000, 2455, 1874, -1000, - -1000, -1000, -1000, 1752, 3571, -1000, -1000, 4134, -1000, -1000, - -1000, -1000, 3567, 680, 4312, 4306, 51989, -1000, -391, 2531, - -1000, 2586, 230, 2598, 70401, -1000, -1000, -1000, 3563, 3562, - -1000, -235, 250, 4305, 4304, 4134, -259, 3270, 459, -1000, - -1000, 4105, 1541, 1856, -228, 4268, -1000, 1480, -1000, -1000, - -1000, -1000, -420, -1000, -1000, -1000, 4317, -1000, 2436, 4317, - 4317, -335, -1000, -1000, -1000, -1000, 4317, 408, -1000, 1706, - -1000, -1000, -1000, -1000, -1000, -1000, 365, -1000, 70401, -1000, - 1751, 148, -1000, 2624, -1000, 395, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 3070, -1000, -1000, -1000, - 15080, -1000, -1000, -1000, -1000, -1000, 3199, -1000, -1000, 15080, - 15080, -1000, 3547, 3267, 3544, 3264, -1000, -1000, -1000, -1000, + 2911, 4359, -1000, 4299, 220, 15068, 220, 15068, 220, 2260, + 3584, 3581, 2258, 3577, 3576, 4110, 4123, 3575, 1725, -1000, + -1000, -1000, -1000, -1000, -1000, 3574, 5703, 1348, 3385, 1348, + -1000, -1000, -1000, -1000, 70689, -1000, -1000, -1000, 70689, 4322, + 35159, 1091, -406, 745, 3764, -1000, 757, 2594, 1441, 3763, + 3384, -1000, 70689, 4321, 70689, 3002, 881, 3002, 926, 70689, + -314, 48479, 46999, -117, 3061, 9076, -1000, 3383, -1000, -132, + 1658, 5824, 1227, 23288, 4107, 3632, 3632, -1000, 3382, 374, + -1000, -1000, -1000, 655, -1000, 3060, -1000, -1000, 2950, 2218, + 390, -1000, -1000, -1000, -1000, -1000, -1000, 3170, 70689, 51437, + 3170, 3172, 2523, -411, -1000, 3762, -1000, 2440, 2440, 2440, + 1091, 696, 70689, 3573, 1696, -1000, -1000, -1000, -1000, -1000, + -1000, 4061, 70689, 3572, -319, 3571, 992, 2452, 2431, -1000, + 3059, 1356, -1000, 3570, 1675, -224, -1000, -1000, 1602, -1000, + -1000, -1000, 2522, 2980, -1000, 50699, 28501, 49959, 1740, -1000, + 2157, -1000, 2727, 2980, 70689, -1000, -1000, 49219, -1000, 4298, + 4297, 48479, 2257, 47739, 46999, 2243, 46259, 992, -1000, 825, + 3057, 18813, 3759, 18813, 3758, 847, 3757, 2227, -1000, 70689, + -1000, -1000, 70689, 691, 3756, -1000, 3755, 3745, 3956, 787, + 3744, 3743, 70689, 2906, -1000, 4072, 70689, 985, 4085, -1000, + 613, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + 893, -1000, 70689, -1000, 70689, -1000, 2264, -1000, 33679, -1000, + -1000, 2221, -1000, 3378, 3375, -1000, -1000, 3567, 2980, -1000, + 2184, 407, 1170, 70689, -1000, 374, 3368, 9825, -1000, -1000, + -1000, -1000, -1000, 4053, 3354, 3170, 70689, -1000, 70689, 1661, + 1661, 4359, 28501, 70689, 12821, -382, -1000, -1000, 4099, 2321, + -1000, 70689, -1000, 15068, 15817, -1000, 1066, 19566, -275, 513, + -1000, -1000, -1000, -234, 3351, -1000, -1000, 4296, 3349, 3210, + -1000, 130, 3344, -1000, 15068, -1000, -1000, -1000, -224, -1000, + 1602, -1000, 3339, -1000, -1000, -1000, 4313, -1000, -1000, 70689, + -1000, 1662, -1000, -1000, 1453, 941, -1000, 3565, 2588, -1000, + -1000, 2901, -1000, 2894, 2890, 220, -1000, 220, -1000, 220, + 402, 15068, -1000, 2878, -1000, 2869, -1000, -1000, 3335, -1000, + -1000, -1000, 3332, -1000, -1000, -1000, 4110, -1000, 15068, -1000, + 3552, -1000, 3330, -1000, -1000, 3325, 3319, -311, -1000, -1000, + 574, 1091, -1000, 479, 70689, 786, -1000, 45519, 9076, -413, + 692, 70689, 4320, 3318, 3002, 3314, 3002, 70689, 869, -1000, + 2219, 2217, 4171, 3312, -1000, 3550, -1000, 3309, 3307, -1000, + -1000, 5824, -1000, 2381, 4351, 4351, -1000, -1000, 4281, -1000, + 2207, 549, -1000, -1000, 2830, 859, -1000, -1000, 3306, 812, + -1000, 1661, -1000, -1000, 2511, 2804, 3230, 42559, 33679, 34419, + 3305, -1000, 70689, -1000, 6948, 22548, 1091, 4175, 687, 542, + 4349, 77336, 1487, 2410, -1000, 3055, -1000, 3052, -1000, 70689, + -1000, -1000, 1602, 15068, 4313, 1740, 168, -382, -1000, 2286, + 255, -1000, 44779, 2215, 2206, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1487, 3708, 4293, + -1000, 4938, 70689, 4696, 70689, 3742, 2509, 18813, -1000, 1033, + 4033, -1000, -1000, 691, -1000, -1000, 2726, 18813, -1000, -1000, + 3300, 3295, 3294, 34419, 1205, 2505, 2503, 1308, 3740, -1000, + 903, 4338, 3050, -1000, -1000, -1000, 1302, -260, 3739, 2724, + 2723, -1000, 70689, -1000, 42559, 42559, 42559, 1180, 1180, 42559, + 42559, 3737, 1090, -1000, -1000, 18813, -1000, -1000, -1000, 2502, + 7093, 7093, 7093, 7093, 7093, -1000, -1000, -1000, 2440, 2240, + -1000, -1000, -1000, -1000, -1000, 70689, 2116, -1000, -1000, -1000, + 3172, -1000, -1000, 1625, -1000, 4264, 1740, -1000, -1000, -1000, + -1000, -1000, 4027, -1000, 2980, 1720, 306, 3734, 3733, -1000, + -392, 70689, 70689, -237, 3049, -1000, 3292, 288, -1000, -1000, + 284, -1000, 1581, 1602, -1000, -1000, 2419, -241, 103, 33679, + 2494, -1000, 3548, 410, -138, -1000, -1000, -1000, -1000, -1000, + -1000, 3547, -1000, 1951, -1000, -1000, -1000, 1581, 220, 220, + 3546, 3542, -1000, 2862, -1000, -1000, -1000, -1000, 70689, 70689, + -1000, 70689, 3291, 3045, -1000, -1000, 2188, -1000, -1000, -1000, + 2695, 2679, 2180, 3540, 3225, 70689, 681, 70689, -314, 3288, + -314, 3284, 868, 3002, -1000, -1000, -288, -1000, -1000, -1000, + -1000, -133, -1000, -1000, -1000, 464, -1000, 870, 3193, 3033, + -1000, -1000, 545, -1000, -1000, -1000, 3170, 3280, -1000, -1000, + 133, -1000, 2487, 2176, -1000, -1000, -1000, 655, 1013, -1000, + 3632, 77277, -1000, 1804, -1000, -1000, -1000, 70689, -1000, -1000, + -1000, -1000, 1453, 2440, -1000, 1013, 916, 2532, -1000, 3011, + -1000, -1000, 1528, 2980, 4359, -382, -1000, 12072, -1000, 2677, + 70689, -1000, -1000, -1000, 44039, 911, -1000, 843, -1000, 2174, + -1000, 2152, 43299, 3009, 4596, -1000, 77103, 1190, -1000, -1000, + 5335, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3278, + 3275, -1000, -1000, -1000, -1000, -1000, 3007, 3732, -38, -1000, + 4207, 4170, 15068, -1000, -1000, 3730, 2151, 2141, 2137, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 3193, 4331, -1000, 4303, 51, 15080, - 51, 15080, 51, 2203, 3542, 3532, 2200, 3531, 3530, 4134, - 4131, 3527, 1729, -1000, -1000, -1000, -1000, -1000, -1000, 3525, - 5500, 1327, 3259, 1327, -1000, -1000, -1000, -1000, 70401, -1000, - -1000, -1000, 70401, 4326, 35063, 1094, -405, 745, 3796, -1000, - 769, 2545, 1454, 3792, 3258, -1000, 70401, 4325, 70401, 2862, - 856, 2862, 906, 70401, -321, 48311, 46839, -116, 2928, 9120, - -1000, 3256, -1000, -134, 1658, 5271, 1173, 22520, 4121, 3560, - 3560, -1000, 3253, 363, -1000, -1000, -1000, 615, -1000, 2926, - -1000, -1000, 2903, 2004, 412, -1000, -1000, -1000, -1000, -1000, - -1000, 3025, 70401, 51253, 3025, 3031, 2454, -406, -1000, 3785, - -1000, 2345, 2345, 2345, 1094, 662, 70401, 3522, 1716, -1000, - -1000, -1000, -1000, -1000, -1000, 4089, 70401, 3520, -325, 3518, - 1022, 2411, 2403, -1000, 2925, 1393, -1000, 3516, 1643, -228, - -1000, -1000, 1579, -1000, -1000, -1000, 2447, 2624, -1000, 50519, - 28441, 49783, 1680, -1000, 2037, -1000, 2712, 2624, 70401, -1000, - -1000, 49047, -1000, 4302, 4300, 48311, 2197, 47575, 46839, 2196, - 46103, 1022, -1000, 819, 2924, 18805, 3784, 18805, 3782, 834, - 3780, 2187, -1000, 70401, -1000, -1000, 70401, 5545, 3779, -1000, - 3777, 3990, 808, 3776, 3775, 70401, 3146, -1000, 4109, 70401, - 986, 4120, -1000, 545, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 865, -1000, 70401, -1000, 70401, -1000, 2228, -1000, - 33591, -1000, -1000, 2185, -1000, 3249, 3248, -1000, -1000, 3512, - 2624, -1000, 2191, 408, 1166, 70401, -1000, 363, 3236, 9865, - -1000, -1000, -1000, -1000, -1000, 4085, 3234, 3025, 70401, -1000, - 70401, 1603, 1603, 4331, 28441, 70401, 12845, -386, -1000, -1000, - 4112, 2279, -1000, 70401, -1000, 15080, 15825, -1000, 1054, 19554, - -282, 485, -1000, -1000, -1000, -237, 3232, -1000, -1000, 4299, - 3231, 3051, -1000, 36, 3226, -1000, 15080, -1000, -1000, -1000, - -228, -1000, 1579, -1000, 3220, -1000, -1000, -1000, 4317, -1000, - -1000, 70401, -1000, 1618, -1000, -1000, 1641, 921, -1000, 3508, - 2355, -1000, -1000, 3033, -1000, 3028, 3008, 51, -1000, 51, - -1000, 51, 337, 15080, -1000, 2899, -1000, 2868, -1000, -1000, - 3219, -1000, -1000, -1000, 3216, -1000, -1000, -1000, 4134, -1000, - 15080, -1000, 3506, -1000, 3214, -1000, -1000, 3201, 3195, -315, - -1000, -1000, 544, 1094, -1000, 457, 70401, 771, -1000, 45367, - 9120, -408, 654, 70401, 4324, 3186, 2862, 3185, 2862, 70401, - 854, -1000, 2176, 2172, 4166, 3183, -1000, 3505, -1000, 3174, - 3173, -1000, -1000, 5271, -1000, 2045, 4359, 4359, -1000, -1000, - 4283, -1000, 1999, 522, -1000, -1000, 2873, 838, -1000, -1000, - 3172, 780, -1000, 1603, -1000, -1000, 2427, 2728, 3135, 42423, - 33591, 34327, 3169, -1000, 70401, -1000, 6986, 23256, 1094, 4239, - 647, 526, 4358, 76779, 1499, 2372, -1000, 2919, -1000, 2916, - -1000, 70401, -1000, -1000, 1579, 15080, 4317, 1680, 167, -386, - -1000, 2248, 249, -1000, 44631, 2157, 2152, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 1499, - 3727, 4298, -1000, 5033, 70401, 4908, 70401, 3773, 2421, 18805, - -1000, 1001, 4068, -1000, -1000, 5545, -1000, -1000, 2674, 18805, - -1000, -1000, 3165, 3163, 3162, 34327, 1187, 2415, 2412, 1197, - 3769, -1000, 870, 4346, 2915, -1000, -1000, -1000, 1350, -267, - 3767, 2642, 2641, -1000, 70401, -1000, 42423, 42423, 1664, 1664, - 42423, 42423, 3766, 1075, -1000, -1000, 18805, -1000, -1000, -1000, - 2407, 4412, 4412, 4412, 4412, -1000, -1000, -1000, 2345, 2106, - -1000, -1000, -1000, -1000, -1000, 70401, 2017, -1000, -1000, -1000, - 3031, -1000, -1000, 1582, -1000, 4268, 1680, -1000, -1000, -1000, - -1000, -1000, 4063, -1000, 2624, 1669, 304, 3761, 3759, -1000, - -391, 70401, 70401, -242, 2912, -1000, 3161, 251, -1000, -1000, - 190, -1000, 1577, 1579, -1000, -1000, 2436, -245, 47, 33591, - 2406, -1000, 3502, 405, -142, -1000, -1000, -1000, -1000, -1000, - -1000, 3500, -1000, 1334, -1000, -1000, -1000, 1577, 51, 51, - 3490, 3482, -1000, 2860, -1000, -1000, -1000, -1000, 70401, 70401, - -1000, 70401, 3160, 2905, -1000, -1000, 2133, -1000, -1000, -1000, - 2609, 2603, 2093, 3480, 3076, 70401, 646, 70401, -321, 3157, - -321, 3155, 851, 2862, -1000, -1000, -295, -1000, -1000, -1000, - -1000, -136, -1000, -1000, -1000, 462, -1000, 818, 3042, 2893, - -1000, -1000, 514, -1000, -1000, -1000, 3025, 3153, -1000, -1000, - 135, -1000, 2405, 2084, -1000, -1000, -1000, 615, 995, -1000, - 3560, 7271, -1000, 1719, -1000, -1000, -1000, 70401, -1000, -1000, - -1000, -1000, 1641, 2345, -1000, 995, 923, 2404, -1000, 2892, - -1000, -1000, 1510, 2624, 4331, -386, -1000, 12100, -1000, 2600, - 70401, -1000, -1000, -1000, 43895, 919, -1000, 831, -1000, 2079, - -1000, 2075, 43159, 2890, 3557, -1000, 7067, 1158, -1000, -1000, - 5425, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3152, - 3151, -1000, -1000, -1000, -1000, -1000, 2888, 3746, -60, -1000, - 4202, 4164, 15080, -1000, -1000, 3745, 2071, 2070, -1000, -1000, + -1000, -1000, 2131, 2128, 42559, -1000, -1000, 5335, 7093, 2799, + -1000, 2440, 2440, 3273, 3272, 644, -1000, -1000, 2440, 2440, + 2440, 2440, 2440, 2440, 3729, 3266, 3262, 2440, 2440, 2440, + 2440, 2440, 2440, 2486, -1000, -1000, -1000, 2477, 2440, 2440, + 2440, 33679, 2440, 2086, 70689, -1000, -382, -382, 4022, -1000, + -358, 3727, 15068, 15068, -1000, -1000, -1000, 3726, -1000, -1000, + 4292, -231, 4313, -248, 3260, 252, 394, -1000, 3253, -1000, + -135, 4021, 4020, -141, -1000, -1000, 1293, -226, 247, 243, + 229, 225, -1000, -1000, -1000, 15068, -1000, -1000, -1000, -1000, + -1000, 3233, -1000, -1000, -1000, -1000, -1000, 70689, 3232, -1000, + -1000, 131, -1000, 2442, -1000, 70689, 673, -1000, -314, -1000, + -314, 3002, 3231, -1000, 70689, 898, -1000, -1000, -1000, -1000, + 353, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 3230, 3227, + -1000, -1000, 801, 4291, -1000, 77336, -1000, 2440, 655, -1000, + -335, 801, -1000, 735, -1000, 2401, -1000, 2999, -1000, 4264, + -1000, 1575, 2623, 15068, 2677, -1000, -1000, -1000, 693, -1000, + 804, -1000, -1000, -1000, 2113, -1000, -1000, -1000, 77103, 826, + -1000, 976, 3725, -1000, -1000, 3539, 15068, 3720, 2440, 3719, + 2847, -124, 42559, 3955, -1000, 3953, 3945, 3613, 2065, -1000, + -1000, 2997, 2989, -1000, -1000, 70689, 2957, 2954, 2953, 2943, + 2917, 2914, 70689, -1000, -1000, 2910, 2900, 2895, 2888, 2885, + 2880, 2788, 2778, 2872, 2866, 2860, -1000, 33679, 70689, -1000, + -1000, -1000, -1000, -1000, 3717, 2059, 2001, 70689, 3210, -234, + -1000, -1000, 3226, -1000, 1089, 302, 394, -1000, 4288, 271, + 4287, 4285, 1557, 4019, -1000, -1000, -1000, 2648, -1000, 263, + 234, 232, 227, -1000, -1000, -1000, -1000, -1000, -1000, 2698, + 2698, -314, 3225, 3219, -1000, 70689, -1000, -1000, 3217, -314, + 747, -1000, 459, -1000, -1000, -1000, 7093, -1000, 4273, 850, + -1000, 33679, -1000, 4347, 4344, -1000, -1000, -1000, 2829, -1000, + -382, 4319, 2980, 2617, -1000, -1000, 2821, -1000, -1000, -1000, + 1998, -1000, 70689, 1250, 11323, -1000, 2805, -1000, 70689, 15068, + -265, 3969, -1000, 321, 1997, 7093, 1180, 7093, 7093, 1180, + 7093, 1180, 7093, 1180, 460, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 1991, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 2032, 2029, 42423, -1000, -1000, 5425, 4412, 2724, -1000, 2345, - 2345, 3150, 3149, 603, -1000, -1000, 2345, 2345, 2345, 2345, - 2345, 2345, 3744, 3147, 3144, 2345, 2345, 2345, 2345, 2345, - -1000, -1000, 2388, 2345, 2345, 33591, 2345, 2013, 70401, -1000, - -386, -386, 4059, -1000, -359, 3743, 15080, 15080, -1000, -1000, - -1000, 3740, -1000, -1000, 4296, -235, 4317, -249, 3143, 175, - 374, -1000, 3142, -1000, -137, 4058, -148, -1000, -1000, 1312, - -229, 144, 139, 136, 134, -1000, -1000, -1000, 15080, -1000, - -1000, -1000, -1000, -1000, 3140, -1000, -1000, -1000, -1000, -1000, - 70401, 3137, -1000, -1000, 132, -1000, 2379, -1000, 70401, 644, - -1000, -321, -1000, -321, 2862, 3136, -1000, 70401, 861, -1000, - -1000, -1000, -1000, 362, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 3135, 3119, -1000, -1000, 816, 4295, -1000, 76779, -1000, - 2345, 615, -1000, -343, 816, -1000, 715, -1000, 2360, -1000, - 2859, -1000, 4268, -1000, 1541, 2549, 15080, 2600, -1000, -1000, - -1000, 711, -1000, 813, -1000, -1000, -1000, 2003, -1000, -1000, - -1000, 7067, 820, -1000, 983, 3739, -1000, -1000, 3478, 15080, - 3737, 2345, 3735, 2852, -128, 42423, 3989, 3987, 3428, 3330, - 2000, -1000, -1000, 2857, 2850, -1000, -1000, 70401, 2824, 2819, - 2808, 2799, 2788, 2772, 70401, -1000, -1000, 2768, 2764, 2746, - 2740, 2737, 2700, 2702, 2699, -1000, 33591, 70401, -1000, -1000, - -1000, -1000, -1000, 3734, 1998, 1989, 70401, 3051, -237, -1000, - -1000, 3082, -1000, 1089, 248, 374, -1000, 4294, 195, 4293, - 4288, 1534, 4057, -1000, -1000, 2590, -1000, 262, 259, 242, - 234, -1000, -1000, -1000, -1000, -1000, -1000, 2628, 2628, -321, - 3076, 3075, -1000, 70401, -1000, -1000, 3061, -321, 640, -1000, - 453, -1000, -1000, -1000, 4412, -1000, 4287, 846, -1000, 33591, - -1000, 4357, 4355, -1000, -1000, -1000, 2688, -1000, -386, 4323, - 2624, 2483, -1000, -1000, 2680, -1000, -1000, -1000, 1953, -1000, - 70401, 1215, 11355, -1000, 2840, -1000, 70401, 15080, -248, 3997, - -1000, 339, 1938, 4412, 1664, 4412, 1664, 4412, 1664, 4412, - 1664, 447, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 1906, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 1905, 15080, -1000, -1000, 1872, -1000, -1000, - -242, -1000, 3733, 2668, 250, 182, 4285, -1000, 3051, 4284, - 3051, 3051, -1000, 151, 4353, 1312, -1000, -1000, -1000, -1000, - -1000, 2545, -1000, 2545, -1000, -1000, -1000, -1000, -321, -1000, - 3059, -1000, -1000, -1000, 41687, 829, -1000, -1000, -1000, 3730, - -1000, -1000, 4196, -1000, 4349, -1000, -1000, 820, 76779, -1000, - 11355, 1862, -1000, 2624, -1000, 1075, -1000, 2669, -1000, -1000, - -1000, -1000, 3996, 3503, 4321, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 3728, 3431, -1000, 70401, - -1000, 4172, 32855, 239, -1000, -1000, -1000, 3054, -1000, 3051, - -1000, -1000, 2325, -145, -1000, -1000, -1000, -1000, -300, -1000, - 70401, 819, 40215, 70401, 3499, -1000, 76779, 1859, -1000, 11355, - -1000, -248, -1000, 4345, -1000, 4343, 1190, 1190, 4412, 4412, - 4412, 4412, 15080, -1000, -1000, -1000, 70401, -1000, 1845, -1000, - -1000, -1000, 2012, -1000, -1000, -1000, -1000, 3044, -149, -1000, - -1000, 3040, 1825, 3727, 1818, -1000, 2318, -1000, -1000, 1510, - 70401, 3561, -1000, -1000, -1000, -1000, -1000, -1000, 2691, 875, - -1000, 3400, 1509, -1000, 2313, -1000, 40951, 70401, -1000, -1000, - -1000, -1000, -1000, -1000, 40215, 39479, 1784, 15080, -1000, -1000, - -1000, -1000, 70401, 10610, -1000, 1527, -1000, -1000, -1000, -1000, - 4067, 1778, -1000, -1000, 2624, 70401, 3250, -1000, -1000, 15080, - 1732, -1000, + 1914, 15068, -1000, -1000, 1870, -1000, -1000, -237, -1000, 3713, + 2779, 291, 266, 4271, -1000, 3210, 4267, 3210, 3210, -1000, + 253, 4324, 1293, -1000, -1000, -1000, -1000, -1000, 2594, -1000, + 2594, -1000, -1000, -1000, -1000, -314, -1000, 3215, -1000, -1000, + -1000, 41819, 807, -1000, -1000, -1000, 3711, -1000, -1000, 4201, + -1000, 4340, -1000, -1000, 826, 77336, -1000, 11323, 1856, -1000, + 2980, -1000, 1090, -1000, 2801, -1000, -1000, -1000, -1000, 3968, + 3966, 4317, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, 3710, 3533, -1000, 70689, -1000, 4198, 32939, + 278, -1000, -1000, -1000, 3212, -1000, 3210, -1000, -1000, 2428, + -139, -1000, -1000, -1000, -1000, -293, -1000, 70689, 825, 40339, + 70689, 3569, -1000, 77336, 1854, -1000, 11323, -1000, -265, -1000, + 4337, -1000, 4334, 1268, 1268, 7093, 7093, 7093, 7093, 15068, + -1000, -1000, -1000, 70689, -1000, 1846, -1000, -1000, -1000, 1946, + -1000, -1000, -1000, -1000, 3207, -142, -1000, -1000, 3197, 1843, + 3708, 1722, -1000, 2368, -1000, -1000, 1528, 70689, 3706, -1000, + -1000, -1000, -1000, -1000, -1000, 2873, 900, -1000, 3145, 1524, + -1000, 2356, -1000, 41079, 70689, -1000, -1000, -1000, -1000, -1000, + -1000, 40339, 39599, 1721, 15068, -1000, -1000, -1000, -1000, 70689, + 10574, -1000, 1739, -1000, -1000, -1000, -1000, 4031, 1711, -1000, + -1000, 2980, 70689, 3675, -1000, -1000, 15068, 1614, -1000, } var yyPgo = [...]int{ - 0, 203, 66, 289, 209, 5129, 173, 292, 410, 313, - 352, 5126, 290, 288, 5125, 5124, 5122, 312, 311, 5121, - 5120, 5119, 5118, 5095, 5093, 5091, 5087, 5081, 5080, 5077, - 5075, 5074, 5073, 5071, 5070, 5069, 5068, 5067, 5066, 5050, - 5049, 5048, 5047, 5045, 5044, 5043, 5039, 5037, 5034, 5031, - 5030, 5029, 5028, 5027, 5023, 282, 5022, 5021, 5020, 5018, - 5017, 5013, 5011, 5010, 5005, 5004, 5003, 5002, 5001, 5000, - 4999, 4997, 4996, 4995, 4994, 4990, 4989, 4988, 4987, 4986, - 4985, 4984, 4983, 4982, 4981, 4980, 4979, 4977, 4975, 4974, - 4973, 4972, 4971, 4970, 4968, 291, 4965, 303, 4962, 4961, - 4957, 4956, 4954, 4953, 4115, 4952, 78, 46, 55, 4951, - 4949, 4947, 4946, 4945, 4942, 4939, 4938, 271, 4937, 4936, - 4935, 4934, 4933, 4927, 4926, 4922, 4921, 4919, 4918, 4916, - 4911, 263, 4910, 4909, 4908, 4907, 4906, 4905, 4904, 4903, - 243, 4900, 256, 4897, 280, 206, 4896, 4893, 4891, 4889, - 4888, 4887, 4884, 129, 140, 4883, 4882, 4881, 4880, 4879, - 4876, 4874, 4872, 4871, 4868, 4852, 4851, 224, 4849, 274, - 201, 95, 4848, 62, 4846, 270, 233, 4844, 245, 4840, - 168, 4838, 166, 4817, 4816, 4814, 4813, 4812, 4811, 4808, - 4807, 4805, 4804, 4801, 4800, 4799, 4798, 4796, 4795, 4794, - 4789, 4788, 4787, 4786, 4784, 4782, 4779, 4778, 4774, 4773, - 4772, 4771, 4769, 69, 4766, 301, 4765, 100, 4764, 163, - 4763, 120, 4759, 202, 4758, 89, 4756, 4755, 83, 4753, - 28, 44, 4752, 68, 102, 4751, 134, 294, 3513, 287, - 4750, 4749, 4748, 281, 4746, 4745, 4742, 254, 61, 36, - 222, 4741, 4740, 299, 199, 4739, 4737, 309, 4735, 314, - 193, 191, 107, 138, 4733, 164, 4732, 300, 81, 60, - 283, 174, 160, 4731, 4730, 123, 225, 151, 221, 1, - 121, 4729, 4728, 4727, 267, 4726, 196, 204, 103, 4725, - 4724, 241, 4721, 4719, 248, 178, 4718, 127, 4715, 4714, - 4713, 25, 4711, 4709, 234, 223, 4706, 4705, 126, 4702, - 4692, 109, 215, 175, 4689, 97, 148, 185, 147, 4687, - 3471, 150, 101, 4686, 144, 135, 4685, 211, 4682, 4680, - 4679, 4678, 216, 4677, 4673, 167, 4671, 73, 4670, 4669, - 4668, 82, 4667, 94, 91, 142, 125, 4666, 37, 128, - 117, 4665, 4662, 4661, 4660, 4659, 4, 4658, 4654, 50, - 4653, 75, 4652, 4651, 4650, 4649, 4648, 4644, 4642, 4640, - 4639, 4638, 4637, 4636, 42, 4635, 4634, 4633, 4632, 8, - 20, 22, 4631, 35, 4630, 183, 4628, 4626, 177, 4625, - 228, 4624, 4623, 119, 114, 4622, 115, 4621, 180, 4620, - 12, 4618, 4617, 205, 4616, 4615, 4614, 268, 4612, 4611, - 4609, 235, 4607, 4606, 4605, 796, 4604, 4603, 4602, 4601, - 4598, 4597, 65, 4596, 2, 244, 32, 4595, 162, 170, - 4594, 53, 39, 4593, 64, 182, 246, 156, 131, 4590, - 4589, 4588, 803, 232, 122, 40, 0, 130, 247, 187, - 4587, 4585, 4584, 293, 4583, 296, 255, 273, 104, 304, - 345, 4582, 4581, 80, 4580, 190, 92, 71, 171, 524, - 30, 607, 4579, 970, 14, 214, 4575, 238, 4559, 15, - 23, 6, 155, 4555, 4552, 45, 306, 4551, 4548, 4547, - 159, 4546, 4545, 186, 88, 4544, 4543, 4542, 4541, 4537, - 47, 4535, 213, 19, 4534, 143, 4533, 279, 113, 208, - 176, 217, 212, 179, 251, 264, 105, 16, 4532, 1672, - 169, 132, 21, 4530, 11, 253, 4529, 229, 146, 4528, - 106, 4527, 284, 302, 239, 4526, 219, 17, 58, 49, - 38, 57, 13, 84, 96, 4525, 4524, 29, 70, 4523, - 76, 4522, 26, 4521, 4520, 59, 51, 4519, 93, 9, - 4517, 4515, 24, 27, 4514, 48, 250, 189, 149, 124, - 77, 4513, 4512, 161, 522, 4510, 184, 194, 158, 4509, - 54, 4508, 4507, 4506, 4504, 3842, 1536, 4502, 4501, 4500, - 4498, 4497, 4495, 4494, 4493, 237, 4491, 99, 52, 4490, - 4489, 4488, 4486, 108, 152, 4483, 4482, 4481, 4480, 41, - 98, 4477, 18, 4476, 33, 31, 43, 4475, 63, 4474, - 4473, 4471, 5, 218, 4467, 4465, 7, 4464, 4463, 3, - 4462, 4461, 145, 4460, 116, 34, 188, 200, 4458, 4457, - 112, 220, 157, 4456, 4455, 133, 275, 4452, 236, 4451, - 276, 266, 298, 4450, 242, 4449, 4448, 4447, 4446, 4444, - 1553, 4439, 4437, 265, 79, 118, 4434, 249, 141, 4432, - 4431, 111, 192, 137, 139, 72, 110, 4430, 136, 240, - 4429, 227, 4428, 252, 4427, 4425, 4423, 4422, 195, 4420, - 4419, 4417, 4416, 230, 4415, 4414, 226, 258, 4413, 4411, - 272, 4410, 4408, 4407, 4406, 4403, 4402, 4401, 4400, 4398, - 4393, 4386, 297, 260, 4385, 4384, -} - -//line mysql_sql.y:15473 + 0, 214, 83, 279, 210, 5099, 106, 294, 359, 316, + 298, 5098, 292, 290, 5097, 5080, 5078, 313, 311, 5077, + 5076, 5066, 5060, 5057, 5056, 5054, 5051, 5050, 5049, 5048, + 5045, 5044, 5041, 5039, 5037, 5036, 5033, 5032, 5031, 5030, + 5029, 5027, 5023, 5022, 5020, 5017, 5015, 5014, 5013, 5012, + 5010, 5009, 5008, 5007, 5006, 286, 5005, 5004, 5003, 5001, + 5000, 4999, 4998, 4997, 4995, 4990, 4988, 4984, 4983, 4981, + 4980, 4979, 4978, 4977, 4976, 4975, 4974, 4972, 4969, 4967, + 4966, 4965, 4964, 4963, 4958, 4956, 4955, 4953, 4951, 4946, + 4943, 4942, 4941, 4937, 4936, 266, 4935, 309, 4934, 4933, + 4932, 4931, 4930, 4928, 4078, 4926, 79, 49, 58, 4925, + 4924, 4923, 4922, 4921, 4919, 4917, 4915, 369, 4914, 4912, + 4910, 4909, 4908, 4907, 4906, 4901, 4899, 4897, 4896, 4893, + 4891, 271, 4890, 4889, 4888, 4887, 4886, 4885, 4884, 4882, + 249, 4881, 260, 4880, 282, 204, 4879, 4878, 4877, 4876, + 4875, 4874, 4873, 129, 138, 4872, 4871, 4869, 4867, 4865, + 4863, 4862, 4859, 4858, 4857, 4856, 4855, 225, 4854, 278, + 140, 89, 4838, 61, 4837, 277, 236, 4836, 251, 4835, + 174, 4834, 170, 4832, 4829, 4828, 4826, 4825, 4824, 4823, + 4822, 4821, 4820, 4818, 4817, 4816, 4814, 4812, 4809, 4808, + 4807, 4806, 4805, 4803, 4802, 4801, 4800, 4799, 4795, 4794, + 4793, 4789, 4788, 69, 4787, 302, 4785, 100, 4783, 172, + 4782, 120, 4781, 200, 4780, 92, 4779, 4777, 99, 4775, + 27, 40, 4774, 68, 102, 4773, 132, 301, 3669, 265, + 4771, 4770, 4768, 258, 4766, 4765, 4764, 274, 66, 38, + 224, 4763, 4762, 285, 203, 4761, 4760, 297, 4759, 256, + 202, 196, 101, 157, 4758, 175, 4757, 304, 59, 72, + 287, 187, 166, 4756, 4754, 97, 219, 152, 222, 7, + 122, 4737, 4736, 4734, 276, 4733, 201, 199, 105, 4731, + 4726, 247, 4725, 4724, 253, 177, 4723, 126, 4722, 4721, + 4715, 24, 4712, 4711, 234, 226, 4710, 4709, 128, 4708, + 4705, 91, 221, 163, 4704, 96, 156, 188, 148, 4703, + 3366, 150, 104, 4702, 142, 135, 4701, 149, 4698, 4696, + 4695, 4688, 212, 4685, 4684, 169, 4683, 76, 4682, 4681, + 4680, 82, 4679, 88, 81, 139, 123, 4678, 36, 127, + 118, 4677, 4675, 4674, 4673, 4672, 5, 4671, 4670, 41, + 4669, 71, 4668, 4667, 4666, 4665, 4664, 4662, 4661, 4660, + 4659, 4658, 4656, 4655, 44, 4654, 4650, 4649, 4648, 9, + 17, 19, 4647, 35, 4646, 193, 4644, 4642, 192, 4641, + 230, 4640, 4639, 113, 112, 4638, 115, 4637, 186, 4634, + 11, 4632, 4631, 60, 4630, 4628, 4624, 288, 4623, 4622, + 4621, 235, 4620, 4619, 4618, 497, 4617, 4612, 4610, 4609, + 4607, 4606, 244, 4605, 2, 250, 32, 4604, 162, 191, + 4602, 52, 42, 4601, 64, 171, 246, 164, 136, 4600, + 4599, 4598, 723, 242, 121, 39, 0, 130, 252, 190, + 4597, 4594, 4590, 300, 4589, 280, 243, 284, 95, 310, + 345, 4588, 4587, 75, 4586, 182, 94, 67, 185, 125, + 28, 375, 4585, 1926, 12, 218, 4584, 238, 4580, 21, + 20, 1, 159, 4579, 4578, 47, 305, 4577, 4576, 4575, + 160, 4574, 4573, 151, 93, 4572, 4570, 4569, 4568, 4567, + 46, 4566, 209, 22, 4563, 146, 4560, 299, 111, 215, + 173, 213, 206, 179, 254, 270, 107, 15, 4559, 1718, + 178, 131, 29, 4558, 13, 267, 4557, 240, 158, 4556, + 133, 4551, 281, 307, 239, 4550, 216, 14, 62, 48, + 37, 55, 18, 116, 189, 4549, 4548, 30, 65, 4547, + 70, 4543, 26, 4541, 4540, 54, 50, 4537, 78, 8, + 4536, 4520, 23, 25, 4516, 51, 248, 194, 145, 119, + 77, 4511, 4508, 176, 519, 4507, 184, 180, 168, 4506, + 57, 4505, 4503, 4502, 4500, 3867, 1549, 4496, 4495, 4493, + 4492, 4491, 4490, 4487, 4486, 232, 4485, 114, 53, 4482, + 4481, 4480, 4479, 109, 161, 4475, 4469, 4467, 4466, 43, + 98, 4465, 16, 4464, 33, 31, 45, 4462, 63, 4461, + 4460, 4458, 4, 220, 4456, 4453, 6, 4452, 4450, 3, + 4449, 4447, 143, 4446, 117, 34, 223, 291, 4445, 4444, + 110, 229, 155, 4443, 4442, 147, 283, 4441, 237, 4440, + 207, 268, 289, 4439, 245, 4437, 4436, 4435, 4434, 4432, + 1552, 4431, 4430, 275, 80, 124, 4429, 264, 134, 4427, + 4426, 108, 183, 137, 144, 73, 103, 4425, 141, 241, + 4423, 233, 4421, 255, 4420, 4419, 4418, 4417, 195, 4416, + 4415, 4414, 4413, 227, 4412, 4408, 228, 272, 4404, 4402, + 303, 4401, 4400, 4398, 4396, 4390, 4389, 4388, 4386, 4383, + 4377, 4375, 314, 263, 4374, 4373, +} + +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:15563 type yySymType struct { union interface{} id int @@ -11642,123 +11708,124 @@ var yyR1 = [...]int{ 367, 367, 367, 367, 368, 368, 443, 443, 393, 393, 393, 395, 395, 394, 388, 386, 386, 386, 386, 386, 386, 386, 387, 387, 387, 387, 387, 387, 387, 387, - 396, 396, 397, 397, 92, 98, 98, 98, 98, 670, - 670, 93, 93, 93, 681, 681, 585, 585, 465, 465, - 464, 464, 464, 464, 464, 464, 464, 464, 464, 464, - 464, 464, 464, 464, 464, 464, 590, 591, 461, 55, + 387, 396, 396, 397, 397, 92, 98, 98, 98, 98, + 670, 670, 93, 93, 93, 681, 681, 585, 585, 465, + 465, 464, 464, 464, 464, 464, 464, 464, 464, 464, + 464, 464, 464, 464, 464, 464, 464, 590, 591, 461, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 53, 54, 156, 156, 158, 158, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, - 219, 89, 90, 91, 68, 62, 65, 66, 208, 211, - 211, 211, 211, 61, 61, 61, 506, 506, 60, 712, - 712, 436, 436, 78, 77, 64, 79, 80, 81, 82, - 83, 84, 59, 76, 76, 76, 76, 76, 76, 76, - 76, 87, 602, 602, 714, 714, 714, 85, 86, 584, - 584, 584, 75, 74, 73, 72, 71, 71, 58, 58, - 57, 57, 63, 193, 195, 67, 194, 194, 196, 196, - 458, 458, 458, 460, 460, 456, 713, 713, 550, 550, - 459, 459, 56, 56, 56, 56, 88, 457, 457, 435, - 455, 455, 455, 14, 14, 12, 19, 19, 19, 19, + 55, 55, 55, 55, 53, 54, 156, 156, 158, 158, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 70, 219, 89, 90, 91, 68, 62, 65, 66, 208, + 211, 211, 211, 211, 61, 61, 61, 506, 506, 60, + 712, 712, 436, 436, 78, 77, 64, 79, 80, 81, + 82, 83, 84, 59, 76, 76, 76, 76, 76, 76, + 76, 76, 87, 602, 602, 714, 714, 714, 85, 86, + 584, 584, 584, 75, 74, 73, 72, 71, 71, 58, + 58, 57, 57, 63, 193, 195, 67, 194, 194, 196, + 196, 458, 458, 458, 460, 460, 456, 713, 713, 550, + 550, 459, 459, 56, 56, 56, 56, 88, 457, 457, + 435, 455, 455, 455, 14, 14, 12, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 51, 28, 29, 27, - 30, 32, 514, 514, 511, 31, 22, 21, 24, 20, - 20, 23, 25, 26, 26, 10, 10, 10, 10, 17, - 17, 18, 254, 254, 321, 321, 664, 664, 660, 660, - 661, 661, 661, 662, 662, 663, 663, 715, 715, 715, - 142, 596, 596, 596, 596, 596, 596, 596, 596, 236, - 8, 8, 9, 9, 9, 249, 249, 11, 11, 104, - 104, 105, 105, 106, 106, 106, 106, 108, 107, 107, - 280, 280, 595, 595, 595, 595, 595, 595, 518, 518, - 518, 641, 641, 641, 642, 279, 279, 272, 272, 597, - 597, 482, 643, 643, 605, 605, 604, 604, 603, 603, - 277, 277, 284, 284, 284, 285, 285, 278, 278, 257, - 257, 170, 170, 619, 619, 620, 620, 610, 610, 610, - 610, 618, 618, 580, 580, 374, 374, 426, 426, 427, - 427, 233, 233, 234, 234, 234, 234, 234, 234, 700, - 700, 701, 702, 703, 703, 704, 704, 705, 705, 705, - 706, 706, 706, 706, 706, 650, 650, 652, 652, 651, - 276, 276, 269, 269, 270, 270, 270, 271, 271, 268, - 268, 267, 266, 266, 265, 263, 263, 263, 264, 264, - 264, 288, 288, 288, 288, 238, 238, 238, 238, 237, - 237, 237, 237, 237, 237, 237, 237, 237, 240, 241, - 241, 243, 242, 242, 242, 242, 244, 245, 245, 247, - 246, 246, 246, 246, 407, 407, 407, 407, 407, 407, - 407, 407, 407, 407, 407, 407, 239, 251, 251, 252, - 252, 253, 253, 253, 253, 253, 253, 253, 253, 253, - 253, 404, 404, 405, 405, 405, 405, 405, 168, 168, - 589, 589, 403, 403, 248, 248, 250, 250, 250, 250, - 402, 402, 401, 262, 262, 261, 261, 260, 260, 260, - 255, 255, 255, 255, 255, 256, 413, 413, 412, 412, - 411, 411, 411, 411, 411, 411, 414, 146, 167, 167, - 169, 274, 273, 410, 409, 409, 409, 409, 287, 287, - 286, 286, 275, 275, 259, 259, 259, 259, 408, 258, - 406, 690, 690, 689, 689, 688, 686, 686, 686, 687, - 687, 687, 687, 633, 633, 633, 633, 633, 444, 444, - 444, 449, 449, 447, 447, 447, 447, 447, 453, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 36, + 19, 19, 19, 19, 19, 19, 19, 51, 28, 29, + 27, 30, 32, 514, 514, 511, 31, 22, 21, 24, + 20, 20, 23, 25, 26, 26, 10, 10, 10, 10, + 17, 17, 18, 254, 254, 321, 321, 664, 664, 660, + 660, 661, 661, 661, 662, 662, 663, 663, 715, 715, + 715, 142, 596, 596, 596, 596, 596, 596, 596, 596, + 236, 8, 8, 9, 9, 9, 249, 249, 11, 11, + 104, 104, 105, 105, 106, 106, 106, 106, 108, 107, + 107, 280, 280, 595, 595, 595, 595, 595, 595, 518, + 518, 518, 641, 641, 641, 642, 279, 279, 272, 272, + 597, 597, 482, 643, 643, 605, 605, 604, 604, 603, + 603, 277, 277, 284, 284, 284, 285, 285, 278, 278, + 257, 257, 170, 170, 619, 619, 620, 620, 610, 610, + 610, 610, 618, 618, 580, 580, 374, 374, 426, 426, + 427, 427, 233, 233, 234, 234, 234, 234, 234, 234, + 700, 700, 701, 702, 703, 703, 704, 704, 705, 705, + 705, 706, 706, 706, 706, 706, 650, 650, 652, 652, + 651, 276, 276, 269, 269, 270, 270, 270, 271, 271, + 268, 268, 267, 266, 266, 265, 263, 263, 263, 264, + 264, 264, 288, 288, 288, 288, 238, 238, 238, 238, + 237, 237, 237, 237, 237, 237, 237, 237, 237, 240, + 241, 241, 243, 242, 242, 242, 242, 244, 245, 245, + 247, 246, 246, 246, 246, 407, 407, 407, 407, 407, + 407, 407, 407, 407, 407, 407, 407, 239, 251, 251, + 252, 252, 253, 253, 253, 253, 253, 253, 253, 253, + 253, 253, 404, 404, 405, 405, 405, 405, 405, 168, + 168, 589, 589, 403, 403, 248, 248, 250, 250, 250, + 250, 402, 402, 401, 262, 262, 261, 261, 260, 260, + 260, 255, 255, 255, 255, 255, 256, 413, 413, 412, + 412, 411, 411, 411, 411, 411, 411, 414, 146, 167, + 167, 169, 274, 273, 410, 409, 409, 409, 409, 287, + 287, 286, 286, 275, 275, 259, 259, 259, 259, 408, + 258, 406, 690, 690, 689, 689, 688, 686, 686, 686, + 687, 687, 687, 687, 633, 633, 633, 633, 633, 444, + 444, 444, 449, 449, 447, 447, 447, 447, 447, 453, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 45, 46, 50, 151, 151, 154, 154, 152, - 152, 153, 153, 157, 157, 155, 155, 42, 303, 304, - 43, 305, 305, 306, 306, 307, 307, 308, 309, 310, - 310, 310, 310, 498, 498, 41, 294, 294, 295, 295, - 296, 296, 297, 298, 298, 298, 302, 299, 300, 300, - 709, 709, 708, 40, 40, 33, 33, 214, 214, 215, - 215, 215, 217, 217, 370, 370, 370, 216, 216, 218, - 218, 218, 665, 667, 667, 669, 668, 668, 668, 671, - 671, 671, 671, 671, 672, 672, 672, 672, 673, 673, - 34, 190, 190, 190, 225, 225, 200, 676, 676, 676, - 675, 675, 556, 556, 677, 677, 678, 678, 430, 430, - 431, 431, 212, 213, 213, 202, 192, 224, 224, 224, - 224, 224, 226, 226, 323, 323, 191, 197, 198, 199, - 201, 203, 205, 205, 207, 666, 674, 674, 674, 515, - 515, 512, 513, 513, 510, 509, 509, 509, 680, 680, - 679, 679, 679, 445, 445, 35, 505, 505, 507, 508, - 508, 508, 508, 508, 508, 508, 508, 499, 499, 499, - 499, 39, 503, 503, 504, 504, 504, 504, 504, 504, + 36, 36, 36, 45, 46, 50, 151, 151, 154, 154, + 152, 152, 153, 153, 157, 157, 155, 155, 42, 303, + 304, 43, 305, 305, 306, 306, 307, 307, 308, 309, + 310, 310, 310, 310, 498, 498, 41, 294, 294, 295, + 295, 296, 296, 297, 298, 298, 298, 302, 299, 300, + 300, 709, 709, 708, 40, 40, 33, 33, 214, 214, + 215, 215, 215, 217, 217, 370, 370, 370, 216, 216, + 218, 218, 218, 665, 667, 667, 669, 668, 668, 668, + 671, 671, 671, 671, 671, 672, 672, 672, 672, 673, + 673, 34, 190, 190, 190, 225, 225, 200, 676, 676, + 676, 675, 675, 556, 556, 677, 677, 678, 678, 430, + 430, 431, 431, 212, 213, 213, 202, 192, 224, 224, + 224, 224, 224, 226, 226, 323, 323, 191, 197, 198, + 199, 201, 203, 205, 205, 207, 666, 674, 674, 674, + 515, 515, 512, 513, 513, 510, 509, 509, 509, 680, + 680, 679, 679, 679, 445, 445, 35, 505, 505, 507, + 508, 508, 508, 508, 508, 508, 508, 508, 499, 499, + 499, 499, 499, 39, 503, 503, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, - 504, 500, 500, 502, 502, 497, 497, 497, 497, 497, - 497, 497, 497, 497, 497, 38, 38, 38, 223, 223, - 496, 496, 493, 493, 301, 301, 491, 491, 492, 492, - 490, 490, 490, 494, 494, 47, 48, 49, 495, 495, - 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, - 227, 283, 283, 232, 232, 232, 232, 232, 232, 230, - 230, 230, 230, 231, 231, 228, 228, 229, 229, 37, + 504, 504, 504, 504, 504, 504, 504, 504, 500, 500, + 502, 502, 497, 497, 497, 497, 497, 497, 497, 497, + 497, 497, 38, 38, 38, 223, 223, 496, 496, 493, + 493, 301, 301, 491, 491, 492, 492, 490, 490, 490, + 494, 494, 47, 48, 49, 495, 495, 227, 227, 227, + 227, 227, 227, 227, 227, 227, 227, 227, 283, 283, + 232, 232, 232, 232, 232, 232, 230, 230, 230, 230, + 231, 231, 228, 228, 229, 229, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 172, 171, 171, 171, 171, 171, - 174, 174, 429, 429, 428, 428, 173, 371, 371, 44, - 334, 334, 572, 572, 567, 567, 567, 567, 567, 587, - 587, 587, 568, 568, 568, 569, 569, 569, 571, 571, - 571, 570, 570, 570, 570, 570, 586, 586, 588, 588, - 588, 538, 538, 539, 539, 539, 542, 542, 559, 559, - 560, 560, 558, 558, 565, 565, 564, 564, 563, 563, - 562, 562, 561, 561, 561, 561, 553, 553, 552, 552, - 540, 540, 540, 540, 540, 541, 541, 541, 551, 551, - 557, 557, 343, 344, 344, 345, 345, 346, 220, 220, - 221, 221, 347, 348, 348, 349, 349, 350, 351, 351, - 352, 352, 353, 354, 354, 355, 355, 356, 357, 357, - 358, 358, 341, 341, 342, 342, 342, 400, 400, 400, + 37, 172, 171, 171, 171, 171, 171, 174, 174, 429, + 429, 428, 428, 173, 371, 371, 44, 334, 334, 572, + 572, 567, 567, 567, 567, 567, 587, 587, 587, 568, + 568, 568, 569, 569, 569, 571, 571, 571, 570, 570, + 570, 570, 570, 586, 586, 588, 588, 588, 538, 538, + 539, 539, 539, 542, 542, 559, 559, 560, 560, 558, + 558, 565, 565, 564, 564, 563, 563, 562, 562, 561, + 561, 561, 561, 553, 553, 552, 552, 540, 540, 540, + 540, 540, 541, 541, 541, 551, 551, 557, 557, 343, + 344, 344, 345, 345, 346, 220, 220, 221, 221, 347, + 348, 348, 349, 349, 350, 351, 351, 352, 352, 353, + 354, 354, 355, 355, 356, 357, 357, 358, 358, 341, + 341, 342, 342, 342, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, - 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, - 400, 400, 639, 639, 640, 360, 360, 372, 372, 372, - 372, 372, 372, 359, 359, 361, 361, 322, 322, 320, - 320, 312, 312, 312, 312, 312, 312, 222, 222, 222, - 311, 311, 311, 311, 311, 311, 313, 313, 314, 314, - 315, 315, 315, 319, 319, 318, 318, 318, 318, 316, - 316, 317, 317, 317, 317, 317, 317, 523, 523, 636, - 636, 637, 637, 632, 632, 632, 635, 635, 635, 635, - 635, 635, 635, 635, 635, 635, 638, 638, 638, 634, - 634, 324, 423, 423, 423, 446, 446, 446, 446, 448, - 422, 422, 422, 340, 340, 339, 339, 337, 337, 337, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 639, + 639, 640, 360, 360, 372, 372, 372, 372, 372, 372, + 359, 359, 361, 361, 322, 322, 320, 320, 312, 312, + 312, 312, 312, 312, 222, 222, 222, 311, 311, 311, + 311, 311, 311, 313, 313, 314, 314, 315, 315, 315, + 319, 319, 318, 318, 318, 318, 318, 316, 316, 317, + 317, 317, 317, 317, 317, 523, 523, 636, 636, 637, + 637, 632, 632, 632, 635, 635, 635, 635, 635, 635, + 635, 635, 635, 635, 635, 638, 638, 638, 634, 634, + 324, 423, 423, 423, 446, 446, 446, 446, 448, 422, + 422, 422, 340, 340, 339, 339, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, 337, - 337, 337, 524, 524, 524, 522, 522, 462, 462, 463, - 463, 383, 382, 382, 382, 382, 382, 380, 381, 379, - 379, 379, 379, 379, 376, 376, 375, 375, 375, 377, + 337, 524, 524, 524, 522, 522, 462, 462, 463, 463, + 383, 382, 382, 382, 382, 382, 380, 381, 379, 379, + 379, 379, 379, 376, 376, 375, 375, 375, 377, 377, 377, 377, 377, 377, 501, 501, 373, 373, 363, 363, 363, 362, 362, 362, 566, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, @@ -11849,13 +11916,13 @@ var yyR1 = [...]int{ 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, - 441, 440, 440, 440, 440, 440, 440, 440, 440, 440, - 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, + 441, 441, 441, 441, 440, 440, 440, 440, 440, 440, + 440, 440, 440, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, 439, - 439, 439, 439, + 439, 439, 439, 439, 439, 439, } var yyR2 = [...]int{ @@ -11917,125 +11984,126 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 2, 1, 3, 2, 1, 2, 2, 1, 2, 3, - 2, 2, 3, 5, 4, 4, 4, 4, 3, 3, - 1, 1, 3, 3, 7, 7, 7, 8, 8, 0, - 4, 7, 6, 6, 0, 3, 0, 2, 0, 1, - 1, 1, 1, 4, 2, 2, 3, 3, 4, 5, - 3, 4, 4, 2, 2, 2, 3, 0, 1, 1, + 2, 2, 3, 5, 4, 4, 4, 4, 4, 3, + 3, 1, 1, 3, 3, 7, 7, 7, 8, 8, + 0, 4, 7, 6, 6, 0, 3, 0, 2, 0, + 1, 1, 1, 1, 4, 2, 2, 3, 3, 4, + 5, 3, 4, 4, 2, 2, 2, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 2, 5, 0, 2, 0, 2, 5, - 7, 8, 5, 5, 7, 9, 8, 8, 11, 5, - 1, 3, 3, 3, 5, 4, 3, 3, 3, 4, - 5, 6, 5, 2, 5, 5, 0, 2, 7, 0, - 1, 0, 1, 5, 5, 3, 3, 2, 4, 4, - 4, 4, 4, 1, 1, 1, 3, 3, 1, 1, - 1, 6, 0, 1, 1, 1, 1, 5, 5, 0, - 1, 1, 3, 3, 3, 4, 7, 7, 5, 4, - 7, 8, 3, 3, 4, 2, 3, 4, 4, 3, - 0, 2, 2, 0, 2, 2, 1, 1, 1, 1, - 0, 1, 5, 5, 6, 4, 3, 1, 3, 1, - 1, 3, 5, 2, 3, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 5, 0, 2, 0, 2, + 5, 7, 8, 5, 5, 7, 9, 8, 8, 11, + 5, 1, 3, 3, 3, 5, 4, 3, 3, 3, + 4, 5, 6, 5, 2, 5, 5, 0, 2, 7, + 0, 1, 0, 1, 5, 5, 3, 3, 2, 4, + 4, 4, 4, 4, 1, 1, 1, 3, 3, 1, + 1, 1, 6, 0, 1, 1, 1, 1, 5, 5, + 0, 1, 1, 3, 3, 3, 4, 7, 7, 5, + 4, 7, 8, 3, 3, 4, 2, 3, 4, 4, + 3, 0, 2, 2, 0, 2, 2, 1, 1, 1, + 1, 0, 1, 5, 5, 6, 4, 3, 1, 3, + 1, 1, 3, 5, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 4, 5, 5, 4, - 4, 4, 1, 3, 1, 4, 6, 6, 4, 4, - 4, 3, 7, 3, 5, 1, 1, 2, 2, 12, - 9, 10, 1, 3, 2, 4, 0, 2, 0, 1, - 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, - 6, 2, 1, 4, 1, 5, 4, 4, 2, 4, - 1, 2, 6, 6, 6, 0, 2, 1, 2, 8, - 10, 1, 2, 7, 5, 13, 10, 1, 0, 2, - 1, 3, 2, 1, 5, 4, 4, 2, 0, 5, - 4, 0, 1, 3, 3, 1, 3, 1, 3, 1, - 3, 4, 0, 1, 0, 1, 1, 3, 1, 1, - 0, 4, 0, 4, 4, 3, 5, 1, 3, 2, - 1, 0, 10, 0, 2, 0, 2, 0, 4, 7, - 4, 0, 2, 0, 2, 0, 2, 0, 4, 1, - 3, 1, 1, 7, 4, 6, 8, 4, 6, 0, - 1, 4, 8, 0, 6, 0, 4, 0, 4, 6, - 1, 1, 1, 1, 1, 2, 3, 1, 3, 6, - 0, 3, 0, 1, 2, 4, 4, 0, 5, 0, - 1, 3, 1, 3, 3, 0, 1, 1, 0, 2, - 2, 0, 2, 2, 4, 3, 3, 1, 1, 1, - 3, 3, 3, 3, 3, 3, 3, 3, 5, 1, - 3, 2, 1, 1, 1, 2, 5, 1, 3, 2, - 1, 1, 1, 1, 1, 2, 2, 1, 2, 2, - 1, 2, 2, 1, 2, 2, 7, 0, 1, 1, - 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 2, 0, 4, 7, 6, 6, 3, 5, - 0, 2, 0, 2, 1, 3, 1, 2, 3, 5, - 0, 1, 2, 1, 3, 1, 4, 1, 1, 1, - 4, 4, 4, 3, 4, 3, 2, 2, 2, 2, - 2, 3, 2, 3, 2, 3, 2, 4, 1, 3, - 4, 1, 3, 1, 1, 2, 2, 3, 0, 1, - 2, 4, 1, 3, 1, 3, 2, 3, 1, 4, - 3, 0, 1, 1, 2, 5, 2, 2, 2, 0, - 2, 3, 3, 0, 1, 3, 1, 3, 0, 1, - 2, 1, 1, 0, 1, 2, 1, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 4, 5, 5, + 4, 4, 4, 1, 3, 1, 4, 6, 6, 4, + 4, 4, 3, 7, 3, 5, 1, 1, 2, 2, + 12, 9, 10, 1, 3, 2, 4, 0, 2, 0, + 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, + 1, 6, 2, 1, 4, 1, 5, 4, 4, 2, + 4, 1, 2, 6, 6, 6, 0, 2, 1, 2, + 8, 10, 1, 2, 7, 5, 13, 10, 1, 0, + 2, 1, 3, 2, 1, 5, 4, 4, 2, 0, + 5, 4, 0, 1, 3, 3, 1, 3, 1, 3, + 1, 3, 4, 0, 1, 0, 1, 1, 3, 1, + 1, 0, 4, 0, 4, 4, 3, 5, 1, 3, + 2, 1, 0, 10, 0, 2, 0, 2, 0, 4, + 7, 4, 0, 2, 0, 2, 0, 2, 0, 4, + 1, 3, 1, 1, 7, 4, 6, 8, 4, 6, + 0, 1, 4, 8, 0, 6, 0, 4, 0, 4, + 6, 1, 1, 1, 1, 1, 2, 3, 1, 3, + 6, 0, 3, 0, 1, 2, 4, 4, 0, 5, + 0, 1, 3, 1, 3, 3, 0, 1, 1, 0, + 2, 2, 0, 2, 2, 4, 3, 3, 1, 1, + 1, 3, 3, 3, 3, 3, 3, 3, 3, 5, + 1, 3, 2, 1, 1, 1, 2, 5, 1, 3, + 2, 1, 1, 1, 1, 1, 2, 2, 1, 2, + 2, 1, 2, 2, 1, 2, 2, 7, 0, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 2, 0, 4, 7, 6, 6, 3, + 5, 0, 2, 0, 2, 1, 3, 1, 2, 3, + 5, 0, 1, 2, 1, 3, 1, 4, 1, 1, + 1, 4, 4, 4, 3, 4, 3, 2, 2, 2, + 2, 2, 3, 2, 3, 2, 3, 2, 4, 1, + 3, 4, 1, 3, 1, 1, 2, 2, 3, 0, + 1, 2, 4, 1, 3, 1, 3, 2, 3, 1, + 4, 3, 0, 1, 1, 2, 5, 2, 2, 2, + 0, 2, 3, 3, 0, 1, 3, 1, 3, 0, + 1, 2, 1, 1, 0, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 6, 6, 10, 0, 3, 0, 2, 0, - 4, 1, 1, 0, 2, 0, 2, 7, 1, 1, - 9, 1, 3, 0, 1, 1, 3, 1, 3, 0, - 1, 1, 1, 0, 2, 14, 1, 3, 0, 1, - 1, 3, 1, 1, 2, 4, 1, 1, 1, 1, - 0, 1, 2, 9, 9, 7, 8, 1, 2, 3, - 3, 3, 0, 4, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 1, 1, 4, 1, 1, 1, 3, - 3, 4, 3, 3, 0, 1, 1, 1, 0, 2, - 7, 8, 10, 8, 2, 2, 8, 0, 3, 3, - 0, 3, 0, 3, 0, 3, 0, 5, 1, 3, - 0, 3, 3, 0, 2, 9, 8, 0, 2, 2, - 3, 3, 0, 2, 0, 2, 4, 5, 4, 4, - 4, 6, 4, 8, 5, 1, 0, 2, 2, 1, - 3, 2, 1, 3, 2, 1, 3, 2, 0, 1, - 3, 4, 3, 1, 1, 4, 1, 3, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, - 1, 11, 0, 2, 3, 3, 2, 2, 3, 1, - 1, 3, 3, 3, 3, 3, 3, 4, 2, 2, - 3, 3, 3, 3, 3, 1, 1, 3, 3, 3, - 3, 1, 3, 3, 4, 0, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 6, 8, 10, 0, 4, - 1, 1, 0, 3, 0, 1, 0, 1, 1, 2, - 4, 4, 4, 0, 1, 4, 4, 4, 0, 2, - 8, 9, 5, 5, 8, 7, 8, 12, 12, 13, - 13, 0, 4, 0, 3, 3, 3, 2, 2, 0, - 3, 3, 3, 4, 4, 0, 3, 0, 2, 11, - 9, 9, 9, 9, 6, 11, 6, 9, 7, 10, - 7, 6, 9, 11, 2, 2, 9, 4, 5, 3, - 0, 4, 1, 3, 0, 3, 6, 0, 2, 10, - 0, 2, 0, 2, 0, 3, 2, 4, 3, 0, - 2, 1, 0, 2, 3, 0, 2, 3, 0, 2, - 1, 0, 3, 2, 4, 3, 0, 1, 0, 1, - 1, 0, 6, 0, 3, 5, 0, 4, 0, 3, - 1, 3, 4, 5, 0, 3, 1, 3, 2, 3, - 1, 2, 0, 4, 6, 5, 0, 2, 0, 2, - 4, 5, 4, 5, 1, 5, 6, 5, 0, 3, - 0, 1, 4, 0, 4, 1, 3, 3, 1, 1, - 1, 1, 4, 0, 4, 1, 3, 3, 1, 1, - 1, 1, 4, 0, 4, 1, 3, 3, 1, 1, - 1, 1, 0, 1, 1, 3, 2, 3, 3, 3, - 4, 4, 3, 3, 3, 3, 4, 4, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 3, 3, 3, 3, 3, 3, 3, 3, 1, - 5, 4, 1, 3, 3, 2, 2, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, - 5, 0, 3, 3, 6, 5, 3, 1, 1, 1, - 0, 5, 5, 5, 5, 6, 0, 1, 1, 3, - 1, 1, 1, 1, 1, 7, 9, 7, 9, 2, - 1, 7, 9, 7, 9, 8, 5, 0, 1, 0, - 1, 1, 1, 1, 3, 3, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, - 1, 3, 1, 3, 5, 1, 1, 1, 1, 1, - 1, 3, 5, 0, 1, 1, 2, 1, 2, 2, - 1, 1, 2, 2, 2, 2, 2, 3, 3, 2, - 2, 1, 5, 6, 4, 2, 1, 1, 1, 5, - 7, 5, 0, 1, 1, 1, 2, 0, 1, 1, - 2, 5, 0, 1, 1, 2, 2, 3, 3, 1, - 1, 2, 2, 2, 0, 1, 2, 2, 2, 0, - 4, 7, 3, 3, 0, 3, 0, 3, 1, 1, + 1, 1, 1, 6, 6, 10, 0, 3, 0, 2, + 0, 4, 1, 1, 0, 2, 0, 2, 7, 1, + 1, 9, 1, 3, 0, 1, 1, 3, 1, 3, + 0, 1, 1, 1, 0, 2, 14, 1, 3, 0, + 1, 1, 3, 1, 1, 2, 4, 1, 1, 1, + 1, 0, 1, 2, 9, 9, 7, 8, 1, 2, + 3, 3, 3, 0, 4, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 4, 1, 1, 1, + 3, 3, 4, 3, 3, 0, 1, 1, 1, 0, + 2, 7, 8, 10, 8, 2, 2, 8, 0, 3, + 3, 0, 3, 0, 3, 0, 3, 0, 5, 1, + 3, 0, 3, 3, 0, 2, 9, 8, 0, 2, + 2, 3, 3, 0, 2, 0, 2, 4, 5, 4, + 4, 4, 6, 4, 8, 5, 1, 0, 2, 2, + 1, 3, 2, 1, 3, 2, 1, 3, 2, 0, + 1, 3, 4, 3, 1, 1, 4, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 1, 1, 1, 11, 0, 2, 3, 3, 2, 2, + 3, 1, 1, 3, 3, 3, 3, 3, 3, 4, + 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 1, 1, 1, 3, 3, 3, 3, 3, 1, 3, + 3, 4, 0, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 6, 8, 10, 0, 4, 1, 1, 0, + 3, 0, 1, 0, 1, 1, 2, 4, 4, 4, + 0, 1, 4, 4, 4, 0, 2, 8, 9, 5, + 5, 8, 7, 8, 12, 12, 13, 13, 0, 4, + 0, 3, 3, 3, 2, 2, 0, 3, 3, 3, + 4, 4, 0, 3, 0, 2, 11, 9, 9, 9, + 9, 6, 11, 6, 9, 7, 10, 7, 6, 9, + 11, 2, 2, 9, 4, 5, 3, 0, 4, 1, + 3, 0, 3, 6, 0, 2, 10, 0, 2, 0, + 2, 0, 3, 2, 4, 3, 0, 2, 1, 0, + 2, 3, 0, 2, 3, 0, 2, 1, 0, 3, + 2, 4, 3, 0, 1, 0, 1, 1, 0, 6, + 0, 3, 5, 0, 4, 0, 3, 1, 3, 4, + 5, 0, 3, 1, 3, 2, 3, 1, 2, 0, + 4, 6, 5, 0, 2, 0, 2, 4, 5, 4, + 5, 1, 5, 6, 5, 0, 3, 0, 1, 4, + 0, 4, 1, 3, 3, 1, 1, 1, 1, 4, + 0, 4, 1, 3, 3, 1, 1, 1, 1, 4, + 0, 4, 1, 3, 3, 1, 1, 1, 1, 0, + 1, 1, 3, 2, 3, 3, 3, 4, 4, 3, + 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 1, 5, 4, 1, + 3, 3, 2, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 3, 5, 0, 3, + 3, 6, 5, 3, 1, 1, 1, 0, 5, 5, + 5, 5, 6, 0, 1, 1, 3, 1, 1, 1, + 1, 1, 7, 9, 7, 7, 9, 2, 1, 7, + 9, 7, 9, 8, 5, 0, 1, 0, 1, 1, + 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, + 3, 1, 3, 5, 1, 1, 1, 1, 1, 1, + 3, 5, 0, 1, 1, 2, 1, 2, 2, 1, + 1, 2, 2, 2, 2, 2, 3, 3, 2, 2, + 1, 5, 6, 4, 2, 1, 1, 1, 5, 7, + 5, 0, 1, 1, 1, 2, 0, 1, 1, 2, + 5, 0, 1, 1, 2, 2, 3, 3, 1, 1, + 2, 2, 2, 0, 1, 2, 2, 2, 0, 4, + 7, 3, 3, 3, 0, 3, 0, 3, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 3, 3, 5, 2, 2, 2, 2, @@ -12131,18 +12199,18 @@ var yyR2 = [...]int{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, + 1, 1, 1, 1, 1, 1, } var yyChk = [...]int{ - -1000, -707, -710, -5, -1, -2, -4, -145, -109, 719, + -1000, -707, -710, -5, -1, -2, -4, -145, -109, 723, -7, -16, -147, -133, -148, -8, -142, -10, -11, -12, -203, -14, -116, -137, -139, -141, -140, -55, -13, -134, -135, -136, -95, -96, -118, -126, -129, -130, -131, -132, -149, -143, -144, -146, -233, -150, -159, -160, -210, -163, - -165, -166, -198, -199, -227, 708, -110, -111, -112, -113, + -165, -166, -198, -199, -227, 712, -110, -111, -112, -113, -114, -115, -36, -35, -34, -33, -190, -200, -204, -206, - -161, -50, 624, 714, 751, 525, -9, -650, 578, -17, + -161, -50, 628, 718, 755, 529, -9, -650, 582, -17, -18, -104, -19, 270, 297, -450, -451, -452, -454, -711, -56, -57, -58, -71, -72, -73, -74, -75, -85, -86, -87, -59, -60, -61, -64, -62, -78, -77, -79, -80, @@ -12151,495 +12219,497 @@ var yyChk = [...]int{ -53, -54, -69, -70, -93, -92, -98, -94, -99, -192, -202, -15, -209, -100, -52, -102, -103, -101, 271, 342, 290, -97, 80, -119, -120, -121, -122, -123, -124, -125, - -127, -128, 450, 456, 512, 706, 707, 65, -234, -238, - 753, 754, 757, 609, 611, 315, 178, 179, 181, 182, + -127, -128, 454, 460, 516, 710, 711, 65, -234, -238, + 757, 758, 761, 613, 615, 315, 178, 179, 181, 182, 186, 189, -37, -38, -39, -40, -41, -42, -44, -43, -45, -46, -47, -48, -49, 266, 16, 14, 18, 58, -20, -23, -21, -24, -22, -31, -32, -30, -25, -27, -191, -197, -26, -201, -205, -207, -164, -51, -28, -29, - 292, 291, 41, 358, 359, 360, 454, 267, 269, 17, - 34, 45, 429, -237, 89, -244, -240, 610, 268, -239, - 15, 761, -6, -3, -2, -177, -181, -185, -188, -189, - -186, -187, -4, -145, 124, 282, 709, -446, 446, 710, - 712, 711, 92, 100, -439, -441, 751, 525, 297, 450, - 456, 706, 707, 754, 757, 609, 611, 315, 626, 627, - 628, 629, 630, 631, 632, 633, 635, 636, 637, 638, - 639, 640, 641, 684, 685, 651, 652, 642, 643, 644, - 645, 646, 647, 648, 649, 653, 654, 655, 656, 657, - 658, 659, 660, 661, 662, 663, 664, 665, 666, 581, - 582, 686, 688, 689, 690, 691, 634, 671, 679, 680, - 681, 427, 428, 617, 703, 760, 309, 333, 479, 339, - 346, 412, 178, 196, 192, 221, 211, 418, 365, 732, - 733, 364, 610, 187, 313, 351, 314, 99, 181, 564, - 114, 537, 508, 184, 371, 374, 372, 373, 328, 330, - 332, 440, 335, 334, 336, 338, 608, 369, 430, 207, - 201, 327, 311, 199, 316, 419, 43, 317, 410, 409, - 226, 318, 319, 621, 533, 426, 539, 343, 56, 506, - 200, 331, 536, 702, 234, 238, 242, 243, 244, 245, - 246, 247, 248, 249, 250, 251, 555, 416, 398, 399, - 400, 556, 421, 169, 170, 541, 731, 738, 739, 740, - 742, 741, 415, 558, 420, 225, 228, 229, 230, 231, - 232, 233, 289, 406, 407, 422, 423, 424, 425, 46, - 619, 301, 559, 236, 747, 215, 224, 219, 567, 347, - 345, 411, 223, 195, 218, 312, 752, 69, 240, 239, - 241, 502, 503, 504, 505, 320, 321, 444, 554, 734, - 735, 214, 202, 431, 188, 25, 562, 296, 538, 457, - 375, 51, 376, 322, 340, 348, 370, 235, 237, 303, - 308, 363, 417, 620, 510, 511, 307, 546, 547, 344, - 560, 198, 300, 329, 295, 563, 748, 736, 189, 459, - 323, 182, 337, 557, 750, 566, 68, 164, 194, 185, - 726, 727, 286, 687, 179, 305, 310, 704, 749, 324, - 325, 326, 350, 349, 341, 186, 216, 302, 222, 205, - 193, 217, 180, 304, 565, 165, 700, 429, 489, 213, - 210, 306, 279, 705, 561, 540, 183, 493, 167, 208, - 352, 694, 695, 696, 699, 445, 405, 353, 354, 206, - 293, 531, 532, 357, 499, 393, 473, 509, 480, 474, - 257, 258, 361, 543, 545, 227, 697, 377, 378, 379, - 535, 380, 382, 383, 388, 449, 60, 62, 101, 104, - 103, 755, 756, 67, 32, 435, 438, 471, 475, 395, - 701, 618, 392, 396, 397, 439, 28, 491, 461, 495, - 494, 52, 53, 54, 57, 58, 59, 61, 63, 64, - 55, 607, 454, 468, 568, 48, 50, 464, 465, 30, - 441, 490, 513, 391, 492, 524, 49, 522, 523, 544, - 29, 443, 442, 66, 47, 498, 500, 501, 355, 389, - 452, 715, 569, 447, 463, 467, 448, 394, 437, 469, - 71, 460, 716, 455, 453, 390, 622, 623, 401, 650, - 432, 507, 604, 603, 602, 601, 600, 599, 598, 597, - 358, 359, 360, 476, 477, 478, 488, 481, 482, 483, - 484, 485, 486, 487, 527, 528, 717, 718, 548, 550, - 551, 615, 552, 549, 274, 759, 758, 433, 434, 277, - 720, 721, 102, 722, 724, 723, 31, 725, 746, 743, - 744, 745, 625, 553, 612, 728, 614, 613, 672, 673, - 674, 675, 676, -529, -527, -446, 610, 315, 751, 706, - 707, 456, 609, 611, 450, 429, 754, 757, 58, 454, - 297, 358, 359, 360, 525, 427, -305, -446, 276, 758, - -97, -18, -17, -9, -104, -237, -238, -715, 263, 265, - 465, -320, 276, -446, -455, 26, 506, -117, 507, 271, - 272, 89, 81, -446, -10, -131, -8, -142, -95, -233, - -650, 512, -453, -446, 358, 358, 615, -453, 276, 276, - 730, 511, 552, 615, 731, 738, 747, 547, 440, 103, - 104, 726, 727, 30, 276, 451, 303, 545, 543, 544, - 548, 549, 550, 551, -76, -602, -584, 540, 539, -459, - 532, 538, 530, 542, 533, 428, 383, 380, 624, 382, - 387, 266, 720, 606, -434, 473, 509, 568, 569, 452, - 510, 555, 557, 534, 114, 212, 209, -448, 307, 487, - -446, -585, 282, -533, -505, 308, -532, -507, -535, -508, - 35, 266, 268, 267, 624, 304, 18, 454, 278, 16, - 15, 455, 290, 28, 29, 31, 17, 456, 458, 32, - 459, 462, 463, 464, 45, 468, 469, 297, 92, 100, - 95, 672, 673, 674, 675, 676, 315, 276, -304, -446, - -481, -473, 121, -476, -468, -469, -471, -424, -623, -466, - 89, 150, 151, 158, 122, 762, -470, -566, 39, 124, - 630, 634, 671, 579, -416, -417, -418, -419, -420, -421, - 616, -446, -624, -622, 95, 105, 107, 111, 112, 110, - 108, 172, 203, 109, 96, 173, -238, 92, -644, 640, - 646, -440, 663, 688, 689, 690, 691, 662, 65, -592, - -600, 275, -598, 171, 209, 293, 205, 16, 156, 499, - 206, 679, 680, 681, 637, 659, 581, 582, 686, 641, - 651, 666, 632, 633, 635, 627, 628, 629, 631, 642, - 644, 658, -601, 654, 664, 665, 650, 682, 683, 743, - 684, 685, 667, 668, 669, 678, 677, 670, 672, 673, - 674, 675, 676, 724, 94, 93, 657, 656, 643, 638, - 639, 645, 626, 636, 647, 655, 660, 661, 438, 114, - 439, 440, 571, 430, 84, 441, 282, 506, 74, 442, - 443, 444, 445, 446, 578, 447, 75, 448, 437, 297, - 489, 449, 208, 227, 584, 583, 585, 575, 572, 570, - 573, 574, 576, 577, 648, 649, 653, -235, -237, -650, - -238, -167, -169, 692, -700, -407, -701, 6, 7, 8, - 9, -702, 173, -691, 508, 620, 95, 571, 276, 351, - 427, 19, 730, 387, 608, 730, 387, 608, 365, 183, - 180, -519, 183, 120, 189, 188, 280, 183, -519, -446, - 186, 730, 185, 726, 731, 738, 615, 361, -495, -214, - 427, 489, 380, 101, 307, -499, -496, -586, 355, 351, - 277, 117, -215, 287, 286, 115, 571, 275, 466, 346, - 60, 62, -257, 51, 281, 42, -320, -652, 605, -651, - -446, -660, -661, 263, 264, 265, 42, 731, 738, 277, - 279, 276, 726, 615, 307, 427, 571, 489, 101, 380, - 276, -660, 747, 180, 555, 557, 508, 307, 487, 44, - -526, 499, -525, -527, 556, 567, 93, 94, 554, -434, - 114, 531, 531, -700, -407, -234, -238, -241, -245, -650, - -243, -247, 276, 65, 608, 730, 615, 731, 738, 277, - 427, 489, 307, 278, -586, 279, 571, 275, 358, 451, - 303, 380, 387, 101, 185, 726, -251, -252, -253, 259, - 260, 261, 73, 264, 262, 70, 35, 36, 37, -1, - 128, 761, -473, -473, -6, 764, -6, -473, -446, -446, - 175, -327, -331, -328, -330, -329, 760, -333, -332, 209, - 210, 171, 213, 215, 220, 216, 217, 218, 219, 221, - 222, 223, 224, 225, 228, 229, 230, 231, 232, 233, - 226, 34, 227, 293, 205, 206, 207, 208, -336, 192, - 211, 618, 252, 193, 253, 194, 254, 195, 255, 169, - 170, 256, 196, 199, 200, 201, 202, 198, 234, 235, - 236, 237, 238, 239, 240, 241, 243, 242, 244, 245, - 246, 247, 248, 249, 250, 251, 174, -289, 95, 35, - 89, 174, -320, 95, -700, -267, -268, 11, -257, 19, - -320, -311, 174, 762, -422, -446, 508, 131, -117, 81, - -117, 507, 81, -117, 507, 271, -653, -654, -655, -657, - 271, 507, 506, 272, 342, -140, 174, 315, 19, -453, - -453, -446, 87, -290, -291, -320, -322, -320, -684, -683, - 35, 615, -293, 26, 553, 733, 735, 531, 741, 748, - 749, 750, 86, -458, 139, 143, -458, -403, 20, -403, - 26, 26, 305, 305, 305, -458, 345, -712, -713, 19, - 141, -456, -713, -456, -456, -458, -714, 278, 541, 46, - 306, 305, -269, -270, 24, -269, 535, 531, -550, 536, - 537, -460, -713, -459, -458, -458, -459, -458, -458, 386, - -458, 35, 381, 382, 276, 279, 571, 380, 721, -712, - -712, 34, 34, -507, 307, -533, -505, 39, 86, 175, - 280, 175, 86, 89, 452, 427, 489, 453, 571, 276, - 466, 279, 307, 467, 427, 489, 276, 279, 571, 307, - 427, 276, 279, 489, 307, 467, 427, 530, 531, 279, - 30, 457, 460, 461, 531, -606, 567, -320, 175, 120, - 117, 119, -473, 138, -488, 131, 132, 133, 134, 135, - 136, 137, 145, 144, 157, 150, 151, 152, 153, 154, - 155, 156, 146, 147, 148, 149, 141, 121, 139, 143, - 140, 123, 118, 162, 161, -238, -473, -481, 65, -471, - -471, -471, -471, -446, -566, -478, -473, 89, 89, 89, - 89, 89, 174, 108, 95, 89, -473, 89, 89, 89, + 292, 291, 41, 358, 359, 360, 458, 267, 269, 17, + 34, 45, 433, -237, 89, -244, -240, 614, 268, -239, + 15, 765, -6, -3, -2, -177, -181, -185, -188, -189, + -186, -187, -4, -145, 124, 282, 713, -446, 450, 714, + 716, 715, 92, 100, -439, -441, 755, 529, 297, 454, + 460, 710, 711, 758, 761, 613, 615, 315, 630, 631, + 632, 633, 634, 635, 636, 637, 639, 640, 641, 642, + 643, 644, 645, 688, 689, 655, 656, 646, 647, 648, + 649, 650, 651, 652, 653, 657, 658, 659, 660, 661, + 662, 663, 664, 665, 666, 667, 668, 669, 670, 585, + 586, 690, 692, 693, 694, 695, 638, 675, 683, 684, + 685, 431, 432, 621, 707, 764, 309, 333, 483, 339, + 346, 413, 178, 196, 192, 221, 211, 419, 365, 736, + 737, 364, 614, 187, 313, 351, 314, 99, 181, 568, + 114, 541, 512, 184, 371, 374, 372, 373, 328, 330, + 332, 444, 335, 334, 336, 338, 612, 369, 434, 207, + 201, 327, 311, 199, 316, 420, 43, 317, 411, 410, + 226, 318, 319, 625, 537, 430, 543, 343, 56, 510, + 200, 331, 540, 706, 234, 238, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 559, 417, 398, 399, + 400, 401, 560, 422, 169, 170, 545, 735, 742, 743, + 744, 746, 745, 416, 562, 421, 225, 228, 229, 230, + 231, 232, 233, 289, 407, 408, 423, 424, 425, 426, + 427, 429, 46, 623, 301, 563, 236, 751, 215, 224, + 219, 571, 347, 345, 412, 223, 195, 218, 312, 756, + 69, 240, 239, 241, 506, 507, 508, 509, 320, 321, + 448, 558, 738, 739, 214, 202, 435, 188, 25, 566, + 296, 542, 461, 375, 51, 376, 322, 340, 348, 370, + 235, 237, 303, 308, 363, 418, 624, 514, 515, 307, + 550, 551, 344, 564, 198, 300, 329, 295, 567, 752, + 740, 189, 463, 323, 182, 337, 561, 754, 570, 68, + 164, 194, 185, 730, 731, 286, 691, 179, 305, 310, + 708, 753, 324, 325, 326, 350, 349, 341, 186, 216, + 302, 222, 205, 193, 217, 180, 304, 569, 165, 704, + 433, 493, 213, 210, 306, 279, 709, 565, 544, 183, + 497, 167, 208, 352, 698, 699, 700, 703, 449, 406, + 353, 354, 206, 293, 535, 536, 357, 503, 393, 477, + 513, 484, 478, 257, 258, 361, 547, 549, 227, 701, + 377, 378, 379, 539, 380, 382, 383, 388, 453, 60, + 62, 101, 104, 103, 759, 760, 67, 32, 439, 442, + 475, 479, 395, 705, 622, 392, 396, 397, 443, 28, + 495, 465, 499, 498, 52, 53, 54, 57, 58, 59, + 61, 63, 64, 55, 611, 458, 472, 572, 48, 50, + 468, 469, 30, 445, 494, 517, 391, 496, 528, 49, + 526, 527, 548, 29, 447, 446, 66, 47, 502, 504, + 505, 355, 389, 456, 719, 573, 451, 467, 471, 452, + 394, 441, 473, 71, 464, 720, 459, 457, 390, 626, + 627, 402, 654, 436, 511, 608, 607, 606, 605, 604, + 603, 602, 601, 358, 359, 360, 480, 481, 482, 492, + 485, 486, 487, 488, 489, 490, 491, 531, 532, 721, + 722, 552, 554, 555, 619, 556, 553, 274, 763, 762, + 437, 438, 277, 724, 725, 102, 726, 728, 727, 31, + 729, 750, 747, 748, 749, 629, 557, 616, 732, 618, + 617, 676, 677, 678, 679, 680, -529, -527, -446, 614, + 315, 755, 710, 711, 460, 613, 615, 454, 433, 758, + 761, 58, 458, 297, 358, 359, 360, 529, 431, -305, + -446, 276, 762, -97, -18, -17, -9, -104, -237, -238, + -715, 263, 265, 469, -320, 276, -446, -455, 26, 510, + -117, 511, 271, 272, 89, 81, -446, -10, -131, -8, + -142, -95, -233, -650, 516, -453, -446, 358, 358, 619, + -453, 276, 276, 734, 515, 556, 619, 735, 742, 751, + 551, 444, 103, 104, 730, 731, 30, 276, 455, 303, + 549, 547, 548, 552, 553, 554, 555, -76, -602, -584, + 544, 543, -459, 536, 542, 534, 546, 537, 432, 383, + 380, 628, 382, 387, 266, 724, 610, -434, 477, 513, + 572, 573, 456, 514, 559, 561, 538, 114, 212, 209, + -448, 307, 491, -446, -585, 282, -533, -505, 308, -532, + -507, -535, -508, 35, 266, 268, 267, 628, 304, 18, + 458, 278, 16, 15, 459, 290, 28, 29, 31, 17, + 460, 462, 32, 463, 466, 467, 468, 45, 472, 473, + 297, 92, 100, 95, 676, 677, 678, 679, 680, 315, + 276, -304, -446, -481, -473, 121, -476, -468, -469, -471, + -424, -623, -466, 89, 150, 151, 158, 122, 766, -470, + -566, 39, 124, 634, 638, 675, 583, -416, -417, -418, + -419, -420, -421, 620, -446, -624, -622, 95, 105, 107, + 111, 112, 110, 108, 172, 203, 109, 96, 173, -238, + 92, -644, 644, 650, -440, 667, 692, 693, 694, 695, + 666, 65, -592, -600, 275, -598, 171, 209, 293, 205, + 16, 156, 503, 206, 683, 684, 685, 641, 663, 585, + 586, 690, 645, 655, 670, 636, 637, 639, 631, 632, + 633, 635, 646, 648, 662, -601, 658, 668, 669, 654, + 686, 687, 747, 688, 689, 671, 672, 673, 682, 681, + 674, 676, 677, 678, 679, 680, 728, 94, 93, 661, + 660, 647, 642, 643, 649, 630, 640, 651, 659, 664, + 665, 442, 114, 443, 444, 575, 434, 84, 445, 282, + 510, 74, 446, 447, 448, 449, 450, 582, 451, 75, + 452, 441, 297, 493, 453, 208, 227, 588, 587, 589, + 579, 576, 574, 577, 578, 580, 581, 652, 653, 657, + -235, -237, -650, -238, -167, -169, 696, -700, -407, -701, + 6, 7, 8, 9, -702, 173, -691, 512, 624, 95, + 575, 276, 351, 431, 19, 734, 387, 612, 734, 387, + 612, 365, 183, 180, -519, 183, 120, 189, 188, 280, + 183, -519, -446, 186, 734, 185, 730, 735, 742, 619, + 361, -495, -214, 431, 493, 380, 101, 307, -499, -496, + -586, 355, 351, 277, 117, -215, 287, 428, 286, 115, + 575, 275, 470, 346, 60, 62, -257, 51, 281, 42, + -320, -652, 609, -651, -446, -660, -661, 263, 264, 265, + 42, 735, 742, 277, 279, 276, 730, 619, 307, 431, + 575, 493, 101, 380, 276, -660, 751, 180, 559, 561, + 512, 307, 491, 44, -526, 503, -525, -527, 560, 571, + 93, 94, 558, -434, 114, 535, 535, -700, -407, -234, + -238, -241, -245, -650, -243, -247, 276, 65, 612, 734, + 619, 735, 742, 277, 431, 493, 307, 278, -586, 279, + 575, 275, 358, 455, 303, 380, 387, 101, 185, 730, + -251, -252, -253, 259, 260, 261, 73, 264, 262, 70, + 35, 36, 37, -1, 128, 765, -473, -473, -6, 768, + -6, -473, -446, -446, 175, -327, -331, -328, -330, -329, + 764, -333, -332, 209, 210, 171, 213, 215, 220, 216, + 217, 218, 219, 221, 222, 223, 224, 225, 228, 229, + 230, 231, 232, 233, 226, 34, 227, 293, 205, 206, + 207, 208, -336, 192, 211, 622, 252, 193, 253, 194, + 254, 195, 255, 169, 170, 256, 196, 199, 200, 201, + 202, 198, 234, 235, 236, 237, 238, 239, 240, 241, + 243, 242, 244, 245, 246, 247, 248, 249, 250, 251, + 174, -289, 95, 35, 89, 174, -320, 95, -700, -267, + -268, 11, -257, 19, -320, -311, 174, 766, -422, -446, + 512, 131, -117, 81, -117, 511, 81, -117, 511, 271, + -653, -654, -655, -657, 271, 511, 510, 272, 342, -140, + 174, 315, 19, -453, -453, -446, 87, -290, -291, -320, + -322, -320, -684, -683, 35, 619, -293, 26, 557, 737, + 739, 535, 745, 752, 753, 754, 86, -458, 139, 143, + -458, -403, 20, -403, 26, 26, 305, 305, 305, -458, + 345, -712, -713, 19, 141, -456, -713, -456, -456, -458, + -714, 278, 545, 46, 306, 305, -269, -270, 24, -269, + 539, 535, -550, 540, 541, -460, -713, -459, -458, -458, + -459, -458, -458, 386, -458, 35, 381, 382, 276, 279, + 575, 380, 725, -712, -712, 34, 34, -507, 307, -533, + -505, 39, 86, 175, 280, 175, 86, 89, 456, 431, + 493, 457, 575, 276, 470, 279, 307, 471, 431, 493, + 276, 279, 575, 307, 431, 276, 279, 493, 307, 471, + 431, 534, 535, 279, 30, 461, 464, 465, 535, -606, + 571, -320, 175, 120, 117, 119, -473, 138, -488, 131, + 132, 133, 134, 135, 136, 137, 145, 144, 157, 150, + 151, 152, 153, 154, 155, 156, 146, 147, 148, 149, + 141, 121, 139, 143, 140, 123, 118, 162, 161, -238, + -473, -481, 65, -471, -471, -471, -471, -446, -566, -478, + -473, 89, 89, 89, 89, 89, 174, 108, 95, 89, + -473, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, -599, 89, 89, -485, -486, 89, + 89, -466, -422, 89, 95, 95, 89, 89, 89, 95, + 89, 89, 89, -486, -486, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - -599, 89, 89, -485, -486, 89, 89, -466, -422, 89, - 95, 95, 89, 89, 89, 95, 89, 89, 89, -486, - -486, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 89, 89, -700, - -237, -268, 175, -267, 89, -267, -268, -239, -238, -243, - -247, 35, 36, 35, 36, 35, 36, 35, 36, -703, - 716, 89, 105, 755, 257, -281, -446, -282, -446, -175, - 19, 762, -446, 726, -683, 381, 615, 615, 381, 615, - 266, 18, 369, 58, 370, 560, 14, 187, 188, 189, - -446, 186, 280, -446, -493, 282, -493, -493, 732, 508, - -493, -303, -446, 303, 451, 279, 279, -215, -493, 19, - -493, -493, -493, -493, 278, -493, 276, 276, 276, -493, - 578, 131, 131, 63, -284, 299, -257, -257, -320, 175, - -652, -276, 89, -662, 191, -260, -259, -255, -256, -406, - -408, -258, 89, -320, -238, -446, 732, 508, -585, -585, - -320, -585, -446, 282, -508, -585, 606, -435, -446, -585, - -585, -585, -389, -390, -320, -663, 281, 750, -697, -696, - 558, -699, 560, 180, -527, 180, -527, 92, -507, 307, - 307, 175, 131, 26, -528, 131, 142, -527, -527, -528, - -528, -359, 44, -445, 171, -446, 95, -359, 44, -694, - -693, -320, -268, -239, -238, -243, -247, 90, 90, -268, - -407, -268, -407, -320, -167, 615, -585, -585, 732, 508, - -585, -585, -585, -585, -585, 276, -585, -585, -585, -453, - -585, -305, 282, -585, 381, -585, -585, -585, -248, -250, - 152, -473, -446, -253, -3, -179, -178, 125, 126, 128, - 710, 446, 709, 713, 706, -527, 44, -579, 165, 164, - 89, -573, -575, 89, -574, 89, -574, -574, -574, 210, - 213, -574, -574, -574, -574, -574, -574, 89, 89, -576, - 89, -576, -576, -573, -577, 204, 89, -577, -578, 89, - -578, -577, -446, -554, 14, -479, -481, -446, 280, 42, - -268, -170, 42, -270, 23, -277, 299, 101, -312, 737, - -446, 206, 185, 725, 38, 174, 105, 95, -140, -117, - 81, -140, -117, -117, 90, 175, -656, 111, 112, -658, - 95, 225, 216, -446, -138, 95, -622, -7, -13, -8, - -142, -10, -12, -55, -95, -144, -233, 290, 609, 611, - -625, -623, 175, 89, -292, 175, 533, 26, -446, -269, - 620, -156, 26, -458, 19, 141, -458, -458, 19, 141, - -458, 307, -403, -471, -471, -473, 427, 571, 276, -509, - 307, -445, -712, -458, -458, -436, -435, -460, -455, -460, - -460, -403, -456, -458, -458, -473, -460, -456, -403, -446, - 531, -403, -403, -550, -435, -458, 95, -457, -446, -457, - -493, -435, -436, -436, 35, 498, 86, 19, -534, 276, - 571, 451, 303, 279, 427, -532, -514, -511, -509, -507, - -510, -509, -537, -422, 531, -171, 514, 513, 357, 19, - -473, -473, -473, -473, 110, 121, 405, 111, 112, -468, - -489, 35, 353, 354, -469, -469, -469, -469, -469, -469, - -469, -469, -469, -469, -469, -469, -471, -471, -477, -487, - -566, 89, 141, 139, 143, 140, 123, -471, -471, -469, - -469, -471, -325, -327, 164, 165, -361, -445, 171, 90, - 175, -473, -649, -648, 125, -473, -473, -473, -473, -500, - -502, -422, 89, -446, -469, -645, -646, 586, 587, 588, - 589, 590, 591, 592, 593, 594, 595, 596, 442, 437, - 443, 441, 430, 449, 444, 445, 208, 603, 604, 597, - 598, 599, 600, 601, 602, -479, -479, -473, -645, -469, - -479, -415, 36, 35, -481, -481, -481, 90, -473, -659, - 403, 402, 404, -272, -446, -479, 90, 90, 90, 105, - -481, -481, -479, -469, -479, -479, -479, -479, -646, -646, - -647, 293, 205, 207, 206, -415, -415, -415, -415, 152, - -481, -481, -415, -415, -415, -415, 152, -415, -415, -415, - -415, -415, -415, -415, -415, -415, -415, -415, -415, -415, - 90, 90, 90, 90, -473, 90, -473, -473, -473, -473, - -473, 152, -481, -268, -700, -269, -169, -604, -603, -473, - 44, -170, -270, -704, 718, 89, -422, -692, 95, 95, - 762, -175, 174, 19, 276, -175, 174, 726, 185, -175, - 571, 19, -446, 95, 105, -446, 95, 105, 276, 571, - 276, 571, -320, -320, -320, 561, 562, 184, 188, 187, - -446, 186, -446, -446, 121, -446, -446, -493, -493, -446, - 38, -305, -294, -446, -493, -493, -667, -446, 96, 95, - -515, -512, -509, -446, -446, -505, -446, -435, -493, -493, - -493, -320, -370, 57, 58, 59, -509, -216, 60, 61, - -595, 65, -233, 89, 34, 89, -284, -284, -651, 38, - -275, -446, -663, 87, -409, -410, -411, -412, -414, -413, - 72, 76, 78, 82, 73, 74, 75, 534, 79, 84, - 77, -444, -449, 38, -446, 95, -444, -238, -262, -261, - -260, 762, -444, 89, -446, -446, -320, -320, -384, -391, - -385, -392, 299, 273, 435, 436, 269, 267, 11, 268, - -400, 346, -494, 579, -365, -366, 81, 45, -368, 297, - 475, 471, 309, 313, 99, 314, 508, 315, 278, 317, - 318, 319, 334, 336, 289, 320, 321, 322, 499, 323, - 179, 335, 324, 325, 326, 453, -360, 6, 388, 44, - 55, 56, 523, 522, 622, 14, 310, -446, 478, 611, - 34, 39, 269, 273, 268, -667, -665, 34, -446, 34, - -515, -509, -446, -446, 175, 280, -260, -262, -527, 175, - 559, 561, 562, -697, -528, -697, -528, 280, 35, 498, - -531, 498, 35, -505, -525, 555, 557, -520, 95, 499, - -510, -530, 86, 171, -603, -528, -528, -530, -530, 161, - 175, -695, 560, 561, 263, -269, -269, -242, -239, -243, - -238, 65, -269, -246, -239, -247, -243, -238, 105, -685, - -683, -446, 615, -446, -585, -585, -322, -667, -514, -505, - -446, -585, -322, -448, -448, -294, 39, -446, -585, -446, - -446, -446, -402, 175, -401, 19, -447, -446, 38, 95, - 174, -180, -178, 127, -473, -6, 709, -473, -6, -6, - -473, -6, -473, -583, 167, -327, 105, 105, -425, 95, - -425, 105, -577, 105, 105, 625, 90, 95, 95, -269, - 693, -271, 23, -266, -265, -473, -596, 65, -236, 89, - -233, 34, 276, 89, -585, 185, 206, 736, -311, 131, - 131, 131, 27, -446, 26, -140, -117, -654, 174, 175, - -291, -275, -320, 624, 105, -158, 24, -446, -403, -446, - 732, -403, -403, -446, 732, 734, -403, -508, -446, -446, - -446, -506, 87, -446, 765, 93, -436, -403, -403, -460, - -403, -403, 175, 25, -458, -460, -460, -311, -456, -311, - 174, -311, -435, -534, -513, -510, -536, 152, -446, -521, - 175, 14, 280, -680, -679, 490, 90, 175, -607, 281, - 578, 95, 762, 506, 257, 258, 95, 110, 405, 111, - 112, -566, -481, -477, -471, -471, -469, -469, -475, 294, - -475, 120, -335, 170, 169, -335, -473, 763, -472, -648, - 127, -473, 38, 175, 38, 175, 87, 175, 90, -573, - -473, 174, 175, 90, 90, 19, 19, 141, 90, -473, - 90, 90, 90, 90, 19, 19, -473, 90, 174, 90, - 90, 90, 90, 87, 90, 175, 90, 90, 90, 90, - 175, 175, 175, -481, -481, -473, -481, 90, 90, 90, - -473, -473, -473, -481, 90, -473, -473, -473, -473, -473, - -473, -473, -473, -473, -473, -275, -473, -473, -544, 526, - -544, -544, -544, 90, -544, 90, 175, 90, 175, 90, - 90, 175, 175, 175, -269, -268, 175, 90, -271, -705, - 717, 89, 105, 175, 756, -429, -428, 95, -176, 280, - -446, 726, -446, -176, -446, -446, 131, -176, -446, 726, - 95, 95, -320, -435, -320, -435, 617, 42, 42, 185, - 189, 189, 188, -446, 95, 39, 26, 26, 344, -446, - -446, -151, 612, -304, 89, 89, 174, -320, -320, -669, - 476, -446, -681, 175, 44, -679, 571, -212, 357, -497, - 87, -223, 364, 19, -320, -320, -320, -334, 38, -518, - 86, -597, -482, -643, 692, -279, 90, -272, -641, -642, - -272, -278, -285, -446, -595, -595, 89, 90, 175, 19, - -254, -321, -446, -260, -259, -259, -259, -259, -259, -259, - 72, 83, 72, -273, 89, 72, 77, 72, 77, 72, - 77, 72, -411, 72, 83, -276, -449, 175, 90, 92, - -690, -689, -688, -686, 80, 281, 81, -479, 34, 34, - 56, 55, -572, 38, -276, 175, 23, 299, -319, -443, - -316, -318, 284, -463, -317, 287, -637, 285, 283, 115, - 288, 342, 116, 278, -443, -443, 284, -369, 280, 38, - -443, -387, 278, 408, 342, 285, 23, 299, -386, 278, - 116, -446, 284, 288, 285, 283, -442, 131, -434, 161, - 280, 46, 453, -442, 623, 299, -442, -442, -442, -442, - -442, -442, -442, 316, 316, -442, -442, -442, -442, -442, - -442, -442, -442, -442, -442, -442, 180, -442, -442, -442, - -442, -442, -442, 89, 311, 312, 344, 612, 125, 625, - 614, -508, 280, 546, 546, -670, 476, 34, 433, 433, - 434, -681, 429, 45, 34, -224, 427, -390, -388, -457, - 34, 34, -528, -696, -698, 563, 560, 566, -530, -530, - 105, 280, 89, 131, -530, -530, 44, -445, -693, 567, - 561, -271, 90, -167, 90, -301, 728, -446, -446, 86, - -322, 89, -305, 95, 19, -301, -403, -250, -262, -446, - 95, -446, 152, 128, -6, 126, -184, -183, -182, 129, - 706, 713, 128, 128, 128, 90, 90, 90, 90, 175, - 90, 90, 90, 175, 90, 175, 105, 752, -610, 536, - -271, 95, -170, 667, 175, -263, 40, 41, -249, 759, - -597, -279, 90, -641, -320, -278, -446, 95, 105, 95, - 38, -222, 92, 100, 95, -312, -473, -446, 95, -473, - 206, 174, 508, -446, -623, 90, 105, -458, -446, -458, - 174, -446, -219, -221, -446, 95, -446, -505, -445, -458, - -403, -473, -473, -403, -458, -458, -460, -446, -311, -536, - 175, 280, 174, 174, -511, 456, -513, 23, 14, -422, - 42, -429, 131, 762, -446, 90, -475, -475, 120, -471, - -468, 90, 128, -473, 126, -325, -473, -325, -326, -332, - 225, 171, 209, 293, 208, 207, 205, 164, 165, -359, - -502, 617, -263, 90, -446, -481, -473, -473, -469, 90, - -473, -473, 19, -446, -359, -469, -473, -473, -473, -268, - -268, 90, 90, -543, -544, -543, -543, 90, 90, 90, - 90, -543, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 175, 89, -544, -544, -473, -544, - -473, -544, -544, -473, 105, 107, 105, 107, -271, -269, - -603, -170, 89, 299, 175, 105, 95, 763, 175, 131, - 427, -446, 19, 174, 95, -446, 95, 19, 276, -446, - 19, 19, -320, -320, -320, 189, 95, -682, 351, 427, - 571, 276, 427, 351, 571, 276, -555, 105, -344, 14, - -348, 14, -152, 125, 95, 464, -306, -307, -308, -309, - -310, 141, 176, 177, -295, -296, -297, -298, -327, -422, - 760, 210, 213, 215, 216, 217, 218, 219, 221, 222, - 223, 224, 225, 228, 229, 230, 231, 232, 233, 226, - 227, 293, 205, 206, 207, 208, 192, 211, 618, 193, - 194, 195, 169, 170, 196, 199, 200, 201, 202, 198, - 234, 235, 236, 237, 238, 239, 240, 241, 243, 242, - 244, 245, 246, 247, 248, 249, 250, 251, -446, -276, - -276, -672, 538, 478, 488, -442, 380, -465, -464, 429, - 45, -590, 499, 484, 485, -512, 307, -435, 152, -678, - 102, 131, 86, 392, 396, 398, 400, 399, 397, 393, - 394, 395, -491, -492, -490, -494, -435, 95, -665, 89, - -233, 38, 139, -223, 364, 19, 89, -343, 319, 89, - -567, 377, -327, -249, 43, 175, 89, 90, 175, 65, - 175, 131, 90, 175, 90, 175, 131, -249, -249, -1, - -446, -320, -254, -446, 19, 175, -664, 174, 86, -287, - -286, 86, 87, -287, -287, -286, 72, -274, 95, 72, - 72, 72, -261, -260, -688, -687, 26, -637, -637, -637, - 90, -345, 89, -346, -220, -446, 95, -349, 89, -350, - -351, -446, 95, -567, -327, 38, -385, 273, 268, -540, - 344, 345, -541, -557, 347, -559, 89, -324, -422, -317, - -636, -637, -493, -446, 116, -636, 116, 89, -324, -422, - -422, -388, -422, -446, -446, -446, -446, -395, -394, -422, - -398, 35, -399, -446, -446, -446, -446, 116, -446, 116, - -364, 44, 52, 53, 54, -442, -442, 212, -367, 44, - 498, 500, 501, -398, 105, 105, 105, 105, 95, 95, - 95, -442, -442, 105, 95, -449, 95, -638, 188, 48, - 49, 105, 105, 105, 105, 44, 95, -372, 44, 327, - 331, 328, 329, 330, 95, 105, 44, 105, 44, 105, - 44, -446, 89, -639, -640, 95, -555, 95, 89, 105, - 95, 269, -508, 95, 86, -672, -442, 433, -527, 131, - 131, -465, -674, 99, 479, -674, -677, 357, -226, 571, - 35, -280, 273, 268, -665, -517, -516, -422, -517, -530, - 560, 564, 565, -513, -461, 95, -520, -170, -320, -588, - 337, 338, -295, -446, -405, 21, 174, 124, -6, -180, - -182, -473, -6, -473, 709, 446, 710, 95, 105, 105, - 562, -618, 520, 515, 517, -170, -619, 506, 14, -265, - -264, 47, -248, 90, 65, -268, 90, 27, 763, 763, - 763, 763, 95, -446, 105, -403, -458, -403, -219, -458, - -458, 141, -403, -311, -403, -403, -458, 19, -510, -505, - 152, 152, -446, 457, -521, 95, 477, 95, 276, 763, - 95, -429, -468, -473, 90, 38, 90, 90, -574, -574, - -573, -576, -573, -335, -335, 90, 89, -263, 90, 90, - 26, 90, 90, -543, 90, 90, -473, 90, 90, 175, - 175, 90, -593, 580, -594, 652, -543, -543, -543, -543, + 89, 89, 89, -700, -237, -268, 175, -267, 89, -267, + -268, -239, -238, -243, -247, 35, 36, 35, 36, 35, + 36, 35, 36, -703, 720, 89, 105, 759, 257, -281, + -446, -282, -446, -175, 19, 766, -446, 730, -683, 381, + 619, 619, 381, 619, 266, 18, 369, 58, 370, 564, + 14, 187, 188, 189, -446, 186, 280, -446, -493, 282, + -493, -493, 736, 512, -493, -303, -446, 303, 455, 279, + 279, -215, -493, 19, -493, -493, -493, -493, 278, -493, + 276, 276, 276, -493, 582, 131, 131, 63, -284, 299, + -257, -257, -320, 175, -652, -276, 89, -662, 191, -260, + -259, -255, -256, -406, -408, -258, 89, -320, -238, -446, + 736, 512, -585, -585, -320, -585, -446, 282, -508, -585, + 610, -435, -446, -585, -585, -585, -389, -390, -320, -663, + 281, 754, -697, -696, 562, -699, 564, 180, -527, 180, + -527, 92, -507, 307, 307, 175, 131, 26, -528, 131, + 142, -527, -527, -528, -528, -359, 44, -445, 171, -446, + 95, -359, 44, -694, -693, -320, -268, -239, -238, -243, + -247, 90, 90, -268, -407, -268, -407, -320, -167, 619, + -585, -585, 736, 512, -585, -585, -585, -585, -585, 276, + -585, -585, -585, -453, -585, -305, 282, -585, 381, -585, + -585, -585, -248, -250, 152, -473, -446, -253, -3, -179, + -178, 125, 126, 128, 714, 450, 713, 717, 710, -527, + 44, -579, 165, 164, 89, -573, -575, 89, -574, 89, + -574, -574, -574, 210, 213, -574, -574, -574, -574, -574, + -574, 89, 89, -576, 89, -576, -576, -573, -577, 204, + 89, -577, -578, 89, -578, -577, -446, -554, 14, -479, + -481, -446, 280, 42, -268, -170, 42, -270, 23, -277, + 299, 101, -312, 741, -446, 206, 185, 729, 38, 174, + 105, 95, -140, -117, 81, -140, -117, -117, 90, 175, + -656, 111, 112, -658, 95, 225, 216, -446, -138, 95, + -622, -7, -13, -8, -142, -10, -12, -55, -95, -144, + -233, 290, 613, 615, -625, -623, 175, 89, -292, 175, + 537, 26, -446, -269, 624, -156, 26, -458, 19, 141, + -458, -458, 19, 141, -458, 307, -403, -471, -471, -473, + 431, 575, 276, -509, 307, -445, -712, -458, -458, -436, + -435, -460, -455, -460, -460, -403, -456, -458, -458, -473, + -460, -456, -403, -446, 535, -403, -403, -550, -435, -458, + 95, -457, -446, -457, -493, -435, -436, -436, 35, 502, + 86, 19, -534, 276, 575, 455, 303, 279, 431, -532, + -514, -511, -509, -507, -510, -509, -537, -422, 535, -171, + 518, 517, 357, 19, -473, -473, -473, -473, 110, 121, + 406, 111, 112, -468, -489, 35, 353, 354, -469, -469, + -469, -469, -469, -469, -469, -469, -469, -469, -469, -469, + -471, -471, -477, -487, -566, 89, 141, 139, 143, 140, + 123, -471, -471, -469, -469, -471, -325, -327, 164, 165, + -361, -445, 171, 90, 175, -473, -649, -648, 125, -473, + -473, -473, -473, -500, -502, -422, 89, -446, -469, -645, + -646, 590, 591, 592, 593, 594, 595, 596, 597, 598, + 599, 600, 446, 441, 447, 445, 434, 453, 448, 449, + 208, 607, 608, 601, 602, 603, 604, 605, 606, -479, + -479, -473, -645, -469, -479, -415, 36, 35, -481, -481, + -481, 90, -473, -659, 404, 403, 405, -272, -446, -479, + 90, 90, 90, 105, -481, -481, -479, -469, -479, -479, + -479, -479, -646, -646, -647, 293, 205, 207, 206, -415, + -415, -415, -415, 152, -481, -481, -415, -415, -415, -415, + 152, -415, -415, -415, -415, -415, -415, -415, -415, -415, + -415, -415, -415, -415, 90, 90, 90, 90, -473, 90, + -473, -473, -473, -473, -473, 152, -481, -268, -700, -269, + -169, -604, -603, -473, 44, -170, -270, -704, 722, 89, + -422, -692, 95, 95, 766, -175, 174, 19, 276, -175, + 174, 730, 185, -175, 575, 19, -446, 95, 105, -446, + 95, 105, 276, 575, 276, 575, -320, -320, -320, 565, + 566, 184, 188, 187, -446, 186, -446, -446, 121, -446, + -446, -493, -493, -446, 38, -305, -294, -446, -493, -493, + -667, -446, 96, 95, -515, -512, -509, -446, -446, -505, + -446, -435, -493, -493, -493, -320, -370, 57, 58, 59, + -509, -216, 60, 61, -595, 65, -233, 89, 34, 89, + -284, -284, -651, 38, -275, -446, -663, 87, -409, -410, + -411, -412, -414, -413, 72, 76, 78, 82, 73, 74, + 75, 538, 79, 84, 77, -444, -449, 38, -446, 95, + -444, -238, -262, -261, -260, 766, -444, 89, -446, -446, + -320, -320, -384, -391, -385, -392, 299, 273, 439, 440, + 269, 267, 11, 268, -400, 346, -494, 583, -365, -366, + 81, 45, -368, 297, 479, 475, 309, 313, 99, 314, + 512, 315, 278, 317, 318, 319, 334, 336, 289, 320, + 321, 322, 503, 323, 179, 335, 324, 325, 326, 457, + -360, 6, 388, 44, 55, 56, 527, 526, 626, 14, + 310, -446, 482, 615, 34, 39, 269, 273, 268, -667, + -665, 34, -446, 34, -515, -509, -446, -446, 175, 280, + -260, -262, -527, 175, 563, 565, 566, -697, -528, -697, + -528, 280, 35, 502, -531, 502, 35, -505, -525, 559, + 561, -520, 95, 503, -510, -530, 86, 171, -603, -528, + -528, -530, -530, 161, 175, -695, 564, 565, 263, -269, + -269, -242, -239, -243, -238, 65, -269, -246, -239, -247, + -243, -238, 105, -685, -683, -446, 619, -446, -585, -585, + -322, -667, -514, -505, -446, -585, -322, -448, -448, -294, + 39, -446, -585, -446, -446, -446, -402, 175, -401, 19, + -447, -446, 38, 95, 174, -180, -178, 127, -473, -6, + 713, -473, -6, -6, -473, -6, -473, -583, 167, -327, + 105, 105, -425, 95, -425, 105, -577, 105, 105, 629, + 90, 95, 95, -269, 697, -271, 23, -266, -265, -473, + -596, 65, -236, 89, -233, 34, 276, 89, -585, 185, + 206, 740, -311, 131, 131, 131, 27, -446, 26, -140, + -117, -654, 174, 175, -291, -275, -320, 628, 105, -158, + 24, -446, -403, -446, 736, -403, -403, -446, 736, 738, + -403, -508, -446, -446, -446, -506, 87, -446, 769, 93, + -436, -403, -403, -460, -403, -403, 175, 25, -458, -460, + -460, -311, -456, -311, 174, -311, -435, -534, -513, -510, + -536, 152, -446, -521, 175, 14, 280, -680, -679, 494, + 90, 175, -607, 281, 582, 95, 766, 510, 257, 258, + 95, 110, 406, 111, 112, -566, -481, -477, -471, -471, + -469, -469, -475, 294, -475, 120, -335, 170, 169, -335, + -473, 767, -472, -648, 127, -473, 38, 175, 38, 175, + 87, 175, 90, -573, -473, 174, 175, 90, 90, 19, + 19, 141, 90, -473, 90, 90, 90, 90, 19, 19, + -473, 90, 174, 90, 90, 90, 90, 87, 90, 175, + 90, 90, 90, 90, 175, 175, 175, -481, -481, -473, + -481, 90, 90, 90, -473, -473, -473, -481, 90, -473, + -473, -473, -473, -473, -473, -473, -473, -473, -473, -275, + -473, -473, -544, 530, -544, -544, -544, 90, -544, 90, + 175, 90, 175, 90, 90, 175, 175, 175, -269, -268, + 175, 90, -271, -705, 721, 89, 105, 175, 760, -429, + -428, 95, -176, 280, -446, 730, -446, -176, -446, -446, + 131, -176, -446, 730, 95, 95, -320, -435, -320, -435, + 621, 42, 42, 185, 189, 189, 188, -446, 95, 39, + 26, 26, 344, -446, -446, -151, 616, -304, 89, 89, + 174, -320, -320, -669, 480, -446, -681, 175, 44, -679, + 575, -212, 357, -497, 87, -223, 364, 19, -320, -320, + -320, -334, 38, -518, 86, -597, -482, -643, 696, -279, + 90, -272, -641, -642, -272, -278, -285, -446, -595, -595, + 89, 90, 175, 19, -254, -321, -446, -260, -259, -259, + -259, -259, -259, -259, 72, 83, 72, -273, 89, 72, + 77, 72, 77, 72, 77, 72, -411, 72, 83, -276, + -449, 175, 90, 92, -690, -689, -688, -686, 80, 281, + 81, -479, 34, 34, 56, 55, -572, 38, -276, 175, + 23, 299, -319, -443, -316, -318, 284, -463, -317, 287, + 428, -637, 285, 283, 115, 288, 342, 116, 278, -443, + -443, 284, -369, 280, 38, -443, -387, 278, 409, 342, + 285, 23, 299, -386, 278, 116, -446, 284, 288, 285, + 283, -442, 131, -434, 161, 280, 46, 457, -442, 627, + 299, -442, -442, -442, -442, -442, -442, -442, 316, 316, + -442, -442, -442, -442, -442, -442, -442, -442, -442, -442, + -442, 180, -442, -442, -442, -442, -442, -442, 89, 311, + 312, 344, 616, 125, 629, 618, -508, 280, 550, 550, + -670, 480, 34, 437, 437, 438, -681, 433, 45, 34, + -224, 431, -390, -388, -457, 34, 34, -528, -696, -698, + 567, 564, 570, -530, -530, 105, 280, 89, 131, -530, + -530, 44, -445, -693, 571, 565, -271, 90, -167, 90, + -301, 732, -446, -446, 86, -322, 89, -305, 95, 19, + -301, -403, -250, -262, -446, 95, -446, 152, 128, -6, + 126, -184, -183, -182, 129, 710, 717, 128, 128, 128, + 90, 90, 90, 90, 175, 90, 90, 90, 175, 90, + 175, 105, 756, -610, 540, -271, 95, -170, 671, 175, + -263, 40, 41, -249, 763, -597, -279, 90, -641, -320, + -278, -446, 95, 105, 95, 38, -222, 92, 100, 95, + -312, -473, -446, 95, -473, 206, 174, 512, -446, -623, + 90, 105, -458, -446, -458, 174, -446, -219, -221, -446, + 95, -446, -505, -445, -458, -403, -473, -473, -403, -458, + -458, -460, -446, -311, -536, 175, 280, 174, 174, -511, + 460, -513, 23, 14, -422, 42, -429, 131, 766, -446, + 90, -475, -475, 120, -471, -468, 90, 128, -473, 126, + -325, -473, -325, -326, -332, 225, 171, 209, 293, 208, + 207, 205, 164, 165, -359, -502, 621, -263, 90, -446, + -481, -473, -473, -469, 90, -473, -473, 19, -446, -359, + -469, -473, -473, -473, -268, -268, 90, 90, -543, -544, + -543, -543, 90, 90, 90, 90, -543, 90, 90, 90, + 90, 90, 90, 90, 90, 90, 90, 90, 90, 175, + 89, -544, -544, -473, -544, -473, -544, -544, -473, 105, + 107, 105, 107, -271, -269, -603, -170, 89, 299, 175, + 105, 95, 767, 175, 131, 431, -446, 19, 174, 95, + -446, 95, 19, 276, -446, 19, 19, -320, -320, -320, + 189, 95, -682, 351, 431, 575, 276, 431, 351, 575, + 276, -555, 105, -344, 14, -348, 14, -152, 125, 95, + 468, -306, -307, -308, -309, -310, 141, 176, 177, -295, + -296, -297, -298, -327, -422, 764, 210, 213, 215, 216, + 217, 218, 219, 221, 222, 223, 224, 225, 228, 229, + 230, 231, 232, 233, 226, 227, 293, 205, 206, 207, + 208, 192, 211, 622, 193, 194, 195, 169, 170, 196, + 199, 200, 201, 202, 198, 234, 235, 236, 237, 238, + 239, 240, 241, 243, 242, 244, 245, 246, 247, 248, + 249, 250, 251, -446, -276, -276, -672, 542, 482, 492, + -442, 380, -465, -464, 433, 45, -590, 503, 488, 489, + -512, 307, -435, 152, -678, 102, 131, 86, 392, 396, + 398, 400, 399, 397, 393, 394, 395, -491, -492, -490, + -494, -435, 95, -665, 89, -233, 38, 139, -223, 364, + 19, 89, -343, 319, 89, -567, 377, -327, -249, 43, + 175, 89, 90, 175, 65, 175, 131, 90, 175, 90, + 175, 131, -249, -249, -1, -446, -320, -254, -446, 19, + 175, -664, 174, 86, -287, -286, 86, 87, -287, -287, + -286, 72, -274, 95, 72, 72, 72, -261, -260, -688, + -687, 26, -637, -637, -637, 90, -345, 89, -346, -220, + -446, 95, -349, 89, -350, -351, -446, 95, -567, -327, + 38, -385, 273, 268, -540, 344, 345, -541, -557, 347, + -559, 89, -324, -422, -317, -636, -637, -636, -493, -446, + 116, -636, 116, 89, -324, -422, -422, -388, -422, -446, + -446, -446, -446, -395, -394, -422, -398, 35, -399, -446, + -446, -446, -446, 116, -446, 116, -364, 44, 52, 53, + 54, -442, -442, 212, -367, 44, 502, 504, 505, -398, + 105, 105, 105, 105, 95, 95, 95, -442, -442, 105, + 95, -449, 95, -638, 188, 48, 49, 105, 105, 105, + 105, 44, 95, -372, 44, 327, 331, 328, 329, 330, + 95, 105, 44, 105, 44, 105, 44, -446, 89, -639, + -640, 95, -555, 95, 89, 105, 95, 269, -508, 95, + 86, -672, -442, 437, -527, 131, 131, -465, -674, 99, + 483, -674, -677, 357, -226, 575, 35, -280, 273, 268, + -665, -517, -516, -422, -517, -530, 564, 568, 569, -513, + -461, 95, -520, -170, -320, -588, 337, 338, -295, -446, + -405, 21, 174, 124, -6, -180, -182, -473, -6, -473, + 713, 450, 714, 95, 105, 105, 566, -618, 524, 519, + 521, -170, -619, 510, 14, -265, -264, 47, -248, 90, + 65, -268, 90, 27, 767, 767, 767, 767, 95, -446, + 105, -403, -458, -403, -219, -458, -458, 141, -403, -311, + -403, -403, -458, 19, -510, -505, 152, 152, -446, 461, + -521, 95, 481, 95, 276, 767, 95, -429, -468, -473, + 90, 38, 90, 90, -574, -574, -573, -576, -573, -335, + -335, 90, 89, -263, 90, 90, 26, 90, 90, -543, + 90, 90, -473, 90, 90, 175, 175, 90, -593, 584, + -594, 656, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, -543, - -543, -543, -543, -543, -473, -484, -483, 299, 90, 175, - 90, 175, 90, 521, 723, 723, 521, 723, 723, -170, - -271, -706, 67, 715, 66, 498, 110, 347, 90, -645, - 175, -437, 352, -437, -428, 95, -446, 95, 726, -446, - 763, 763, 726, -446, 95, -320, -435, -283, 537, -230, - 125, -231, 123, 46, 95, -446, 19, -446, -446, 344, - -446, 344, -446, -446, 95, 89, 89, -157, 625, 89, - -154, 613, 95, 90, 175, -422, 90, 175, -327, 38, - 38, -673, 99, -668, 95, -446, 96, -446, -674, 173, - 431, 44, 480, 481, 496, 426, 105, 105, 486, -666, - -446, -225, 276, 427, -225, -676, 56, 131, 95, -320, - -490, -434, 161, 318, -311, -446, 380, -313, -314, -315, - -324, -316, -318, -233, -320, -320, 95, -313, -442, -313, - -568, 379, 23, 105, 151, 116, -482, -605, -604, 65, - -233, -272, -597, -642, -603, -446, -446, -473, 90, -277, - 87, 174, -262, -321, -446, 152, -473, -473, 89, 90, - 763, 89, 72, 11, 21, 175, -345, 131, 175, -349, - 131, -568, -233, -552, 348, 89, -550, 89, -550, 116, - 393, -560, -558, 299, -393, 48, 50, -327, -634, -446, - -632, -634, -446, -632, -632, -493, -473, -393, -324, 280, - 34, 268, -396, 396, 390, 391, 396, 398, 400, 399, - -522, 343, 121, -522, 175, -263, 175, -446, -359, -359, - 34, 95, 95, -322, 90, 175, 131, 95, -154, -153, - -473, -234, -238, 280, 86, 276, -673, -668, 131, -528, - 95, 95, -674, 95, 95, -678, 131, -323, 276, -435, - 175, -280, -280, -403, 19, 175, 131, -403, 90, -288, - 26, 45, 90, 380, -404, 22, 23, 152, 128, 126, - 128, 128, -446, 90, 90, -580, 694, -614, -616, 515, - 23, 23, -288, -620, 699, 95, 457, 48, 49, -236, - 65, -233, -597, -269, 206, 95, 763, -403, -458, -403, - -403, 732, -403, -505, -521, 499, -320, 175, 763, -325, - -378, 95, 96, -473, 90, -473, -473, 90, 95, 90, - 95, 90, -268, 23, -544, -473, -544, -473, -544, 90, - 175, 90, 90, 90, 175, 90, 90, -288, -170, 90, - 175, 90, -645, -438, 206, 95, -438, -446, -446, 19, - -447, -228, 280, -311, -232, 375, 89, 371, -230, 185, - 89, 95, -446, 19, -446, -555, 344, -555, 344, 276, - -446, -301, -345, -349, -155, 614, 105, -153, 95, -498, - 619, -308, -327, 274, -297, 44, -233, -233, 95, -671, - 490, -556, 385, 105, 44, 105, 173, 482, -591, -213, - 99, -322, 35, -280, -213, -675, 99, 131, 762, 89, - -442, -442, -442, -228, 380, -446, 90, 175, -229, 54, - -446, 90, 731, 90, -569, 298, 105, 151, 105, 151, - 105, 17, 281, 90, -597, 131, -444, -262, -446, -403, - -664, 174, -105, -106, 125, -275, -633, -446, 283, 23, - 23, -346, 90, -221, -350, 90, -352, -446, 95, -569, - -542, 349, 105, -469, 89, -469, 89, -551, 346, 89, - 90, 175, -446, -422, -340, -339, -337, 110, 121, 44, - 471, -338, 99, 739, 740, 161, 332, 335, 334, 310, - 333, -383, -462, 86, 687, 474, 390, 391, -494, 702, - 38, 283, 115, 116, 458, -463, 89, 89, 87, 352, - 89, 89, -634, 90, -393, -422, 44, -396, 44, -397, - 414, -503, -503, -503, -503, 343, -394, -446, 161, -359, + -473, -484, -483, 299, 90, 175, 90, 175, 90, 525, + 727, 727, 525, 727, 727, -170, -271, -706, 67, 719, + 66, 502, 110, 347, 90, -645, 175, -437, 352, -437, + -428, 95, -446, 95, 730, -446, 767, 767, 730, -446, + 95, -320, -435, -283, 541, -230, 125, -231, 123, 46, + 95, -446, 19, -446, -446, 344, -446, 344, -446, -446, + 95, 89, 89, -157, 629, 89, -154, 617, 95, 90, + 175, -422, 90, 175, -327, 38, 38, -673, 99, -668, + 95, -446, 96, -446, -674, 173, 435, 44, 484, 485, + 500, 430, 105, 105, 490, -666, -446, -225, 276, 431, + -225, -676, 56, 131, 95, -320, -490, -434, 161, 318, + -311, -446, 380, -313, -314, -315, -324, -316, -318, -233, + -320, -320, 95, -313, -442, -313, -568, 379, 23, 105, + 151, 116, -482, -605, -604, 65, -233, -272, -597, -642, + -603, -446, -446, -473, 90, -277, 87, 174, -262, -321, + -446, 152, -473, -473, 89, 90, 767, 89, 72, 11, + 21, 175, -345, 131, 175, -349, 131, -568, -233, -552, + 348, 89, -550, 89, -550, 116, 393, -560, -558, 299, + -393, 48, 50, -327, -634, -446, -634, -632, -634, -446, + -632, -632, -493, -473, -393, -324, 280, 34, 268, -396, + 396, 390, 391, 396, 398, 400, 399, 428, -522, 343, + 121, -522, 175, -263, 175, -446, -359, -359, 34, 95, + 95, -322, 90, 175, 131, 95, -154, -153, -473, -234, + -238, 280, 86, 276, -673, -668, 131, -528, 95, 95, + -674, 95, 95, -678, 131, -323, 276, -435, 175, -280, + -280, -403, 19, 175, 131, -403, 90, -288, 26, 45, + 90, 380, -404, 22, 23, 152, 128, 126, 128, 128, + -446, 90, 90, -580, 698, -614, -616, 519, 23, 23, + -288, -620, 703, 95, 461, 48, 49, -236, 65, -233, + -597, -269, 206, 95, 767, -403, -458, -403, -403, 736, + -403, -505, -521, 503, -320, 175, 767, -325, -378, 95, + 96, -473, 90, -473, -473, 90, 95, 90, 95, 90, + -268, 23, -544, -473, -544, -473, -544, 90, 175, 90, + 90, 90, 175, 90, 90, -288, -170, 90, 175, 90, + -645, -438, 206, 95, -438, -446, -446, 19, -447, -228, + 280, -311, -232, 375, 89, 371, -230, 185, 89, 95, + -446, 19, -446, -555, 344, -555, 344, 276, -446, -301, + -345, -349, -155, 618, 105, -153, 95, -498, 623, -308, + -327, 274, -297, 44, -233, -233, 95, -671, 494, -556, + 385, 105, 44, 105, 173, 486, -591, -213, 99, -322, + 35, -280, -213, -675, 99, 131, 766, 89, -442, -442, + -442, -228, 380, -446, 90, 175, -229, 54, -446, 90, + 735, 90, -569, 298, 105, 151, 105, 151, 105, 17, + 281, 90, -597, 131, -444, -262, -446, -403, -664, 174, + -105, -106, 125, -275, -633, -446, 283, 23, 23, -346, + 90, -221, -350, 90, -352, -446, 95, -569, -542, 349, + 105, -469, 89, -469, 89, -551, 346, 89, 90, 175, + -446, -422, -340, -339, -337, 110, 121, 44, 475, -338, + 99, 743, 744, 161, 332, 335, 334, 310, 333, -383, + -462, 86, 691, 478, 390, 391, -494, 706, 38, 283, + 115, 116, 462, -463, 89, 89, 89, 87, 352, 89, + 89, -634, 90, -393, -422, 44, -396, 44, -397, 415, + -503, -503, -503, -503, -503, 343, -394, -446, 161, -359, 90, -640, 95, 90, -508, 276, -446, -671, 95, -530, -676, 95, -213, -322, -665, -268, -262, -516, -603, -249, - 17, 68, 141, -446, -473, -481, 743, 745, 746, 282, - -6, 710, 446, -374, 695, 95, 23, 95, -612, 95, + 17, 68, 141, -446, -473, -481, 747, 749, 750, 282, + -6, 714, 450, -374, 699, 95, 23, 95, -612, 95, -610, 95, -481, -597, 95, -403, -446, -173, -371, -434, 315, 90, -377, 141, 14, 90, 90, 90, -543, -543, - -543, -546, -545, -549, 521, 344, 529, -481, 90, 90, - 95, 95, -288, -473, 90, 95, 95, 95, 726, 427, - -228, 38, 464, 24, 631, 376, -275, 372, 373, 374, - -446, 95, -481, -234, 762, 380, -446, 19, 95, -555, + -543, -546, -545, -549, 525, 344, 533, -481, 90, 90, + 95, 95, -288, -473, 90, 95, 95, 95, 730, 431, + -228, 38, 468, 24, 635, 376, -275, 372, 373, 374, + -446, 95, -481, -234, 766, 380, -446, 19, 95, -555, 95, -555, -446, 344, 90, 90, 38, 95, 90, 95, 95, -299, -327, -466, -217, 14, -217, 23, 14, 173, - 430, 44, 105, 44, 483, 95, -225, 131, 111, 112, + 434, 44, 105, 44, 487, 95, -225, 131, 111, 112, -430, -431, 95, -500, -359, -361, 95, -446, -341, -233, 38, -342, -400, -494, -315, -228, 30, 380, -172, -343, -347, -353, -171, 319, -344, -341, -570, 179, 105, 151, - 105, 105, -517, -473, -403, -403, -249, 759, -106, -108, + 105, 105, -517, -473, -403, -403, -249, 763, -106, -108, 121, -446, 90, 90, 175, -570, -559, 23, 90, -537, 90, -537, 89, 131, -469, -558, -561, 65, -337, 110, -469, 95, 95, 95, -361, -362, 44, 331, 327, 131, 131, -363, 44, 311, 312, -373, 89, 342, 17, 105, - 212, 703, 89, 116, 116, -320, -500, -500, -635, 392, - 393, 394, 401, 396, 397, 395, 398, 399, 400, -635, - -500, -500, 89, -523, -522, -469, -503, 131, -504, 289, - 406, 407, 99, 14, 390, 391, 411, 410, 409, 415, - 416, 420, 421, 417, 419, 418, 422, 425, 423, 424, - 412, 413, 414, 430, 441, -442, 161, -446, 174, -675, - -269, -403, 68, -589, 14, 744, 89, 89, -446, -446, - -426, 696, 105, 95, 517, -618, -458, -581, 697, -608, - -550, -359, 131, 90, 79, 618, 620, 90, -548, 123, - 482, 486, -467, 96, -470, 105, 107, 203, 173, -544, - -544, 90, 90, 90, -446, -446, -320, 95, 105, 90, - 120, 120, 90, 90, -433, -432, 95, -446, 380, -446, - -301, 95, -301, 95, 344, -555, -2, 619, -218, 64, - 567, 95, 96, 477, 95, 96, 105, 430, -213, 95, - 763, 175, 131, 90, -556, -538, 299, -233, 175, -400, - -434, -446, -173, -442, -538, -587, 188, 378, 14, 105, - 151, 105, -268, -249, -248, -107, 120, -108, -446, 283, - -571, 188, 378, -541, 90, 90, 90, -537, 105, 90, - -565, -562, 89, -400, 301, 141, 95, 95, 105, 89, - -598, 34, 38, -473, -501, 89, 90, 90, 90, 90, - -500, 111, 112, -442, -442, 95, 95, 389, -442, -442, - -442, -442, -442, -442, 89, 95, 95, -442, -442, -442, - -442, -442, 131, -442, -442, -359, -442, 174, -446, -249, - -249, 69, 746, 89, -481, -481, 89, 23, -580, -403, - -582, 698, 95, -617, 520, -611, -609, 515, 516, 517, - 518, 95, 619, 69, 621, -547, -548, 486, -467, 96, - -470, 692, 527, 527, 527, 527, 95, -446, 95, 763, - 175, 131, -446, 380, -301, -301, -555, 95, -302, -446, - 342, 499, -431, 95, -503, -539, 351, 23, -400, -442, - -556, 738, 742, -539, 378, 105, 151, 105, -269, 126, - -473, -107, 378, -553, 350, 90, -565, -400, -564, -563, - 349, 302, 89, 90, -473, -485, -442, 89, 90, -376, - -375, 616, -500, -503, 87, -503, 87, -503, 87, -503, - 87, 90, 105, 105, -446, 105, 105, 105, 105, 105, - 105, -537, 105, 105, 105, 105, 105, 111, 112, 105, - 105, -359, -446, -168, 89, 90, 90, -427, -446, -612, - -374, 95, -621, 281, -615, -616, 519, -609, 23, 517, - 23, 23, -174, 175, 69, 120, 528, 528, 528, 528, - -230, -231, -230, -231, -301, -432, 95, -446, 95, -301, - -300, 38, 522, 457, 23, -540, -359, -348, -354, 14, - 105, -249, 17, 18, 126, 105, 90, 175, -446, 298, - 89, -480, -474, -473, 298, 90, -446, -473, -524, 705, - 704, -382, -380, -381, 86, 534, 340, 341, 90, -635, - -635, -635, -635, -383, 90, 90, 175, -479, 90, 175, - -426, -628, 89, 105, -614, -613, -615, 23, -612, 23, - -612, -612, 524, 14, -547, -230, -230, -301, 95, -422, - 89, -552, 89, 34, 16, -563, -562, -480, 90, 175, - -522, 90, -381, 86, -380, 86, 18, 17, -503, -503, - -503, -503, 89, 90, -446, -631, 34, 90, -627, -626, - -423, -622, -446, 520, 521, 95, -612, 131, 620, -709, - -708, 722, -537, -542, -355, -356, -357, -446, 95, -517, - 89, 65, 90, -474, -524, -379, 337, 338, 34, 188, - -379, -479, -630, -629, -424, 90, 175, 174, 95, 621, - 95, 90, -559, 90, 175, 131, -279, 89, 110, 44, - 339, 90, 175, 131, -626, -446, -356, -358, -446, 95, - 90, -481, -629, 44, -473, 174, 65, 90, -446, 89, - -481, 90, + 212, 707, 89, 116, 116, -320, -500, -500, -500, -635, + 392, 393, 394, 402, 396, 397, 395, 398, 399, 400, + 401, -635, -500, -500, 89, -523, -522, -469, -503, 131, + -504, 289, 407, 408, 99, 14, 390, 391, 412, 411, + 410, 416, 417, 421, 422, 418, 420, 419, 423, 427, + 424, 425, 426, 429, 413, 414, 58, 415, 434, 445, + 441, -442, 161, -446, 174, -675, -269, -403, 68, -589, + 14, 748, 89, 89, -446, -446, -426, 700, 105, 95, + 521, -618, -458, -581, 701, -608, -550, -359, 131, 90, + 79, 622, 401, 624, 90, -548, 123, 486, 490, -467, + 96, -470, 105, 107, 203, 173, -544, -544, 90, 90, + 90, -446, -446, -320, 95, 105, 90, 120, 120, 90, + 90, -433, -432, 95, -446, 380, -446, -301, 95, -301, + 95, 344, -555, -2, 623, -218, 64, 571, 95, 96, + 481, 95, 96, 105, 434, -213, 95, 767, 175, 131, + 90, -556, -538, 299, -233, 175, -400, -434, -446, -173, + -442, -538, -587, 188, 378, 14, 105, 151, 105, -268, + -249, -248, -107, 120, -108, -446, 283, -571, 188, 378, + -541, 90, 90, 90, -537, 105, 90, -565, -562, 89, + -400, 301, 141, 95, 95, 105, 89, -598, 34, 38, + -473, -501, 89, 90, 90, 90, 90, 90, -500, 111, + 112, -442, -442, 95, 95, 389, -442, -442, -442, -442, + -442, -442, 89, 95, 95, -442, -442, -442, -442, -442, + -442, 131, 131, -442, -442, -442, -359, -442, 174, -446, + -249, -249, 69, 750, 89, -481, -481, 89, 23, -580, + -403, -582, 702, 95, -617, 524, -611, -609, 519, 520, + 521, 522, 95, 623, 69, 69, 625, -547, -548, 490, + -467, 96, -470, 696, 531, 531, 531, 531, 95, -446, + 95, 767, 175, 131, -446, 380, -301, -301, -555, 95, + -302, -446, 342, 503, -431, 95, -503, -539, 351, 23, + -400, -442, -556, 742, 746, -539, 378, 105, 151, 105, + -269, 126, -473, -107, 378, -553, 350, 90, -565, -400, + -564, -563, 349, 302, 89, 90, -473, -485, -442, 89, + 90, -376, -375, 620, -500, -503, 87, -503, -503, 87, + -503, 87, -503, 87, 90, 105, 105, -446, 105, 105, + 105, 105, 105, 105, -537, 105, 105, 105, 105, 105, + 105, 111, 112, 111, 112, 105, 105, 105, -359, -446, + -168, 89, 90, 90, -427, -446, -612, -374, 95, -621, + 281, -615, -616, 523, -609, 23, 521, 23, 23, -174, + 175, 69, 120, 532, 532, 532, 532, -230, -231, -230, + -231, -301, -432, 95, -446, 95, -301, -300, 38, 526, + 461, 23, -540, -359, -348, -354, 14, 105, -249, 17, + 18, 126, 105, 90, 175, -446, 298, 89, -480, -474, + -473, 298, 90, -446, -473, -524, 709, 708, -382, -380, + -381, 86, 538, 340, 341, 90, -635, -635, -635, -635, + -383, 90, 90, 175, -479, 90, 175, -426, -628, 89, + 105, -614, -613, -615, 23, -612, 23, -612, -612, 528, + 14, -547, -230, -230, -301, 95, -422, 89, -552, 89, + 34, 16, -563, -562, -480, 90, 175, -522, 90, -381, + 86, -380, 86, 18, 17, -503, -503, -503, -503, 89, + 90, -446, -631, 34, 90, -627, -626, -423, -622, -446, + 524, 525, 95, -612, 131, 624, -709, -708, 726, -537, + -542, -355, -356, -357, -446, 95, -517, 89, 65, 90, + -474, -524, -379, 337, 338, 34, 188, -379, -479, -630, + -629, -424, 90, 175, 174, 95, 625, 95, 90, -559, + 90, 175, 131, -279, 89, 110, 44, 339, 90, 175, + 131, -626, -446, -356, -358, -446, 95, 90, -481, -629, + 44, -473, 174, 65, 90, -446, 89, -481, 90, } var yyDef = [...]int{ @@ -12649,54 +12719,53 @@ var yyDef = [...]int{ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 0, 345, 346, 347, 348, - 349, 350, 1149, 1150, 1151, 1152, 1153, 1154, 1155, 1156, - 1157, 1158, 0, 0, 0, 0, 860, 0, 847, 825, - 826, 867, 785, 0, 0, 0, 0, 0, 0, 0, - 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, - 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, - 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, - 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, - 669, 670, 671, 672, 477, 478, 479, 480, 481, 482, + 349, 350, 1150, 1151, 1152, 1153, 1154, 1155, 1156, 1157, + 1158, 1159, 0, 0, 0, 0, 861, 0, 848, 826, + 827, 868, 786, 0, 0, 0, 0, 0, 0, 0, + 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, + 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, + 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, + 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, + 670, 671, 672, 673, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 0, 0, -2, 379, 375, 287, 288, 289, 290, 291, 292, 293, - 387, 388, 606, 0, 0, 0, 0, 0, 941, -2, + 387, 388, 607, 0, 0, 0, 0, 0, 942, -2, 128, 0, 0, 0, 0, 0, 368, 0, 359, 359, - 0, 0, 1159, 1160, 1161, 1162, 1163, 1164, 1165, 1166, - 1167, 1168, 1169, 1170, 1171, -2, 0, 0, 838, 0, - 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, - 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, - 449, 450, 451, 445, 446, 448, 447, 0, 0, 838, - 0, 0, 0, 949, 0, 997, 998, 0, 1496, 999, - 1037, 23, 0, 6, 8, 9, 10, 11, 12, 13, + 0, 0, 1160, 1161, 1162, 1163, 1164, 1165, 1166, 1167, + 1168, 1169, 1170, 1171, 1172, -2, 0, 0, 839, 0, + 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, + 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, + 449, 450, 451, 445, 446, 448, 447, 0, 0, 839, + 0, 0, 0, 950, 0, 998, 999, 0, 1503, 1000, + 1038, 23, 0, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0, 0, 18, 0, 18, 0, - 0, 0, 1695, 1696, 1697, 1698, 2280, 2621, 2591, -2, - 2322, 2281, 2282, 2515, 2516, 2405, 2421, 2273, 2670, 2671, - 2672, 2673, 2674, 2675, 2676, 2677, 2678, 2679, 2680, 2681, - 2682, 2683, 2684, 2685, 2686, 2687, 2688, 2689, 2690, 2691, - 2692, 2693, 2694, 2695, 2696, 2697, 2698, 2699, 2700, 2701, - 2702, 2703, 2704, 2705, 2706, 2707, 2708, 2709, 2710, 2711, - 2712, 2713, 2714, 2715, 2716, 2717, 2718, 2719, 2720, 2721, - 2722, 2227, 2228, 2229, 2230, 2231, 2232, 2233, 2234, 2235, - 2236, 2237, 2238, 2239, 2240, 2241, 2242, 2243, 2244, 2245, + 0, 0, 1704, 1705, 1706, 1707, 2290, 2634, 2604, -2, + 2333, 2291, 2292, 2528, 2529, 2418, 2434, 2283, 2683, 2684, + 2685, 2686, 2687, 2688, 2689, 2690, 2691, 2692, 2693, 2694, + 2695, 2696, 2697, 2698, 2699, 2700, 2701, 2702, 2703, 2704, + 2705, 2706, 2707, 2708, 2709, 2710, 2711, 2712, 2713, 2714, + 2715, 2716, 2717, 2718, 2719, 2720, 2721, 2722, 2723, 2724, + 2725, 2726, 2727, 2728, 2729, 2730, 2731, 2732, 2733, 2734, + 2735, 2237, 2238, 2239, 2240, 2241, 2242, 2243, 2244, 2245, 2246, 2247, 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, 2259, 2260, 2261, 2262, 2263, 2264, 2265, - 2266, 2267, 2268, 2269, 2270, 2271, 2272, 2274, 2275, 2276, - 2277, 2278, 2279, 2283, 2284, 2285, 2286, 2287, 2288, 2289, - 2290, 2291, 2292, 2293, 2294, 2295, 2296, 2297, 2298, 2299, + 2266, 2267, 2268, 2269, 2270, 2271, 2272, 2273, 2274, 2275, + 2276, 2277, 2278, 2279, 2280, 2281, 2282, 2284, 2285, 2286, + 2287, 2288, 2289, 2293, 2294, 2295, 2296, 2297, 2298, 2299, 2300, 2301, 2302, 2303, 2304, 2305, 2306, 2307, 2308, 2309, 2310, 2311, 2312, 2313, 2314, 2315, 2316, 2317, 2318, 2319, - 2320, 2321, 2323, 2324, 2325, 2326, 2327, 2328, 2329, 2330, - 2331, 2332, 2333, 2334, 2335, 2336, 2337, 2338, 2339, 2340, + 2320, 2321, 2322, 2323, 2324, 2325, 2326, 2327, 2328, 2329, + 2330, 2331, 2332, 2334, 2335, 2336, 2337, 2338, 2339, 2340, 2341, 2342, 2343, 2344, 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358, 2359, 2360, 2361, 2362, 2363, 2364, 2365, 2366, 2367, 2368, 2369, 2370, 2371, 2372, 2373, 2374, 2375, 2376, 2377, 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390, 2391, 2392, 2393, 2394, 2395, 2396, 2397, 2398, 2399, 2400, - 2401, 2402, 2403, 2404, 2406, 2407, 2408, 2409, 2410, 2411, - 2412, 2413, 2414, 2415, 2416, 2417, 2418, 2419, 2420, 2423, - 2424, 2425, 2426, 2427, 2428, 2429, 2430, 2431, 2432, 2433, - 2434, 2435, 2436, 2437, 2438, 2439, 2440, 2441, 2442, 2443, + 2401, 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2409, 2410, + 2411, 2412, 2413, 2414, 2415, 2416, 2417, 2419, 2420, 2421, + 2422, 2423, 2424, 2425, 2426, 2427, 2428, 2429, 2430, 2431, + 2432, 2433, 2436, 2437, 2438, 2439, 2440, 2441, 2442, 2443, 2444, 2445, 2446, 2447, 2448, 2449, 2450, 2451, 2452, 2453, 2454, 2455, 2456, 2457, 2458, 2459, 2460, 2461, 2462, 2463, 2464, 2465, 2466, 2467, 2468, 2469, 2470, 2471, 2472, 2473, @@ -12704,450 +12773,453 @@ var yyDef = [...]int{ 2484, 2485, 2486, 2487, 2488, 2489, 2490, 2491, 2492, 2493, 2494, 2495, 2496, 2497, 2498, 2499, 2500, 2501, 2502, 2503, 2504, 2505, 2506, 2507, 2508, 2509, 2510, 2511, 2512, 2513, - 2514, 2517, 2518, 2519, 2520, 2521, 2522, 2523, 2524, 2525, - 2526, 2527, 2528, 2529, 2530, 2531, 2532, 2533, 2534, 2535, + 2514, 2515, 2516, 2517, 2518, 2519, 2520, 2521, 2522, 2523, + 2524, 2525, 2526, 2527, 2530, 2531, 2532, 2533, 2534, 2535, 2536, 2537, 2538, 2539, 2540, 2541, 2542, 2543, 2544, 2545, - 2546, 2547, -2, 2549, 2550, 2551, 2552, 2553, 2554, 2555, - 2556, 2557, 2558, 2559, 2560, 2561, 2562, 2563, 2564, 2565, + 2546, 2547, 2548, 2549, 2550, 2551, 2552, 2553, 2554, 2555, + 2556, 2557, 2558, 2559, 2560, -2, 2562, 2563, 2564, 2565, 2566, 2567, 2568, 2569, 2570, 2571, 2572, 2573, 2574, 2575, 2576, 2577, 2578, 2579, 2580, 2581, 2582, 2583, 2584, 2585, - 2586, 2587, 2588, 2589, 2590, 2592, 2593, 2594, 2595, 2596, - 2597, 2598, 2599, 2600, 2601, 2602, 2603, 2604, 2605, 2606, - -2, -2, -2, 2610, 2611, 2612, 2613, 2614, 2615, 2616, - 2617, 2618, 2619, 2620, 2622, 2623, 2624, 2625, 2626, 2627, - 2628, 2629, 2630, 2631, 2632, 2633, 2634, 2635, 2636, 2637, + 2586, 2587, 2588, 2589, 2590, 2591, 2592, 2593, 2594, 2595, + 2596, 2597, 2598, 2599, 2600, 2601, 2602, 2603, 2605, 2606, + 2607, 2608, 2609, 2610, 2611, 2612, 2613, 2614, 2615, 2616, + 2617, 2618, 2619, -2, -2, -2, 2623, 2624, 2625, 2626, + 2627, 2628, 2629, 2630, 2631, 2632, 2633, 2635, 2636, 2637, 2638, 2639, 2640, 2641, 2642, 2643, 2644, 2645, 2646, 2647, 2648, 2649, 2650, 2651, 2652, 2653, 2654, 2655, 2656, 2657, - 2658, 2659, 2660, 0, 343, 341, 2248, 2273, 2280, 2281, - 2282, 2322, 2405, 2421, 2422, 2461, 2515, 2516, 2541, 2548, - 2591, 2607, 2608, 2609, 2621, 0, 0, 1191, 0, 0, - 380, 827, 828, 861, 868, 949, 979, 0, 848, 849, - 0, 783, 0, 1640, 422, 0, 2299, 426, 2598, 0, - 0, 0, 0, 780, 416, 417, 418, 419, 420, 421, - 0, 0, 0, 1148, 0, 0, 2629, 412, 0, 0, - 83, 470, 673, 0, 0, 0, 0, 0, 760, 760, - 1062, 1062, 0, 703, 0, 0, 0, 760, 0, 717, - 709, 0, 0, 0, 760, 0, 0, 972, 972, 0, - 763, 770, 760, 760, -2, 760, 760, 0, 755, 760, - 0, 0, 0, 723, 724, 725, 709, 709, 728, 729, - 730, 740, 741, 771, 2222, 0, 0, 374, 2408, 2620, - 1699, 0, 0, 0, 0, 0, 230, 1326, 232, 1328, - 236, 244, 0, 0, 0, 249, 250, 253, 254, 255, - 256, 257, 0, 261, 0, 263, 266, 0, 268, 269, - 0, 272, 273, 274, 0, 284, 285, 286, 1329, 1330, - 1331, 1332, 1333, 1334, 1335, 1336, -2, 0, 159, 1189, - 2177, 2056, 0, 2062, 2075, 2086, 1789, 1790, 1791, 1792, - 0, 0, 0, 0, 0, 0, 1801, 1802, 0, 1846, - 2674, 2718, 2719, 0, 1811, 1812, 1813, 1814, 1815, 1816, - 0, 170, 182, 183, 2108, 2109, 2110, 2111, 2112, 2113, - 2114, 0, 2116, 2117, 2118, 0, 1774, 1695, 0, 2683, - 2693, 0, 2707, 2714, 2715, 2716, 2717, 2706, 0, 0, - 2012, 0, 2002, 0, 0, -2, -2, 0, 0, 2488, - -2, 2720, 2721, 2722, 2680, 2703, 2711, 2712, 2713, 2684, - 2687, 2710, 2676, 2677, 2678, 2671, 2672, 2673, 2675, 2689, - 2691, 2702, 0, 2698, 2708, 2709, 2596, 0, 0, 2647, - 2685, 2686, 0, 0, 0, 0, 0, 0, 2656, 2657, - 2658, 2659, 2660, 2642, 184, 185, -2, -2, -2, -2, + 2658, 2659, 2660, 2661, 2662, 2663, 2664, 2665, 2666, 2667, + 2668, 2669, 2670, 2671, 2672, 2673, 0, 343, 341, 2258, + 2283, 2290, 2291, 2292, 2333, 2418, 2434, 2435, 2474, 2528, + 2529, 2554, 2561, 2604, 2620, 2621, 2622, 2634, 0, 0, + 1192, 0, 0, 380, 828, 829, 862, 869, 950, 980, + 0, 849, 850, 0, 784, 0, 1647, 422, 0, 2309, + 426, 2611, 0, 0, 0, 0, 781, 416, 417, 418, + 419, 420, 421, 0, 0, 0, 1149, 0, 0, 2642, + 412, 0, 0, 83, 470, 674, 0, 0, 0, 0, + 0, 761, 761, 1063, 1063, 0, 704, 0, 0, 0, + 761, 0, 718, 710, 0, 0, 0, 761, 0, 0, + 973, 973, 0, 764, 771, 761, 761, -2, 761, 761, + 0, 756, 761, 0, 0, 0, 724, 725, 726, 710, + 710, 729, 730, 731, 741, 742, 772, 2232, 0, 0, + 374, 2421, 2633, 1708, 0, 0, 0, 0, 0, 230, + 1327, 232, 1329, 236, 244, 0, 0, 0, 249, 250, + 253, 254, 255, 256, 257, 0, 261, 0, 263, 266, + 0, 268, 269, 0, 272, 273, 274, 0, 284, 285, + 286, 1330, 1331, 1332, 1333, 1334, 1335, 1336, 1337, -2, + 0, 159, 1190, 2187, 2066, 0, 2072, 2085, 2096, 1799, + 1800, 1801, 1802, 0, 0, 0, 0, 0, 0, 1811, + 1812, 0, 1856, 2687, 2731, 2732, 0, 1821, 1822, 1823, + 1824, 1825, 1826, 0, 170, 182, 183, 2118, 2119, 2120, + 2121, 2122, 2123, 2124, 0, 2126, 2127, 2128, 0, 1784, + 1704, 0, 2696, 2706, 0, 2720, 2727, 2728, 2729, 2730, + 2719, 0, 0, 2022, 0, 2012, 0, 0, -2, -2, + 0, 0, 2501, -2, 2733, 2734, 2735, 2693, 2716, 2724, + 2725, 2726, 2697, 2700, 2723, 2689, 2690, 2691, 2684, 2685, + 2686, 2688, 2702, 2704, 2715, 0, 2711, 2721, 2722, 2609, + 0, 0, 2660, 2698, 2699, 0, 0, 0, 0, 0, + 0, 2669, 2670, 2671, 2672, 2673, 2655, 184, 185, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, 2033, -2, 2035, -2, 2037, + -2, 2039, -2, -2, -2, -2, 2044, 2045, -2, 2047, + -2, -2, -2, -2, -2, -2, -2, 2024, 2025, 2026, + 2027, 2016, 2017, 2018, 2019, 2020, 2021, -2, -2, -2, + 2188, 950, 0, 0, 980, 1099, 0, 980, 0, 951, + 1025, 1028, 1031, 1034, 954, 0, 0, 129, 130, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 369, 370, 358, 360, 0, 364, 0, 0, + 360, 357, 351, 0, 1399, 1399, 1399, 0, 0, 1399, + 0, 0, 0, 1399, 1399, 1399, 1399, 1399, 0, 1399, + 0, 0, 0, 1399, 0, 1228, 1339, 1340, 1341, 1342, + 1397, 1398, 1504, 0, 0, 0, 913, 2405, 0, 0, + 921, 966, 0, 968, 971, 844, 840, 841, 842, 843, + 0, 0, 0, 607, 607, 0, 607, 0, 0, 607, + 0, 607, 607, 607, 0, 846, 2368, 2469, 2326, 2440, + 2268, 2421, 2633, 0, 316, 2501, 321, 0, 2332, 2372, + 0, 0, 2392, 0, -2, 0, 397, 980, 0, 0, + 950, 980, 980, 0, 1010, 1018, 0, 0, 0, 607, + 607, 0, 0, 607, 607, 607, 607, 607, 0, 607, + 607, 607, 0, 607, 0, 607, 0, 607, 607, 607, + 0, 1039, 1040, 1042, 1043, 1044, 1045, 1046, 1047, 1048, + 1049, 1050, 1051, 4, 5, 18, 0, 0, 0, 0, + 0, 0, 135, 134, 0, 2191, 2227, 2131, 2132, 2133, + 0, 2214, 2136, 2218, 2218, 2218, 2218, 0, 2168, 2169, + 2170, 2171, 2172, 2173, 2174, 2175, 2176, 2177, 2218, 2218, + 2218, 2218, 2218, 2218, 0, 0, 2186, 2157, 2216, 2216, + 2216, 2214, 2193, 2137, 2138, 2139, 2140, 2141, 2142, 2143, + 2144, 2145, 2146, 2147, 2148, 2149, 2150, 2221, 2221, 2224, + 2224, 2221, 2194, 2195, 2196, 2197, 2198, 2199, 2200, 2201, + 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, 2210, 2211, + 0, 475, 473, 474, 2062, 0, 0, 0, 980, -2, + 0, 0, 911, 0, 785, 1638, 0, 0, 423, 1709, + 0, 0, 427, 0, 428, 0, 0, 430, 0, 0, + 0, 452, 0, 455, 438, 439, 440, 441, 442, 434, + 0, 210, 0, 414, 415, 411, 0, 460, 461, 463, + 466, 1634, 80, 84, 87, 0, 973, 0, 676, 761, + 761, 761, 761, 692, 693, 694, 0, 1063, 0, 0, + 697, 698, 0, 699, 0, 0, 710, 761, 761, 716, + 717, 712, 711, 767, 768, 764, 0, 764, 764, 1063, + 0, 735, 736, 737, 761, 761, 743, 974, 0, 744, + 745, 764, 0, 769, 770, 1063, 0, 0, 1063, 1063, + 0, 753, 754, 0, 757, 761, 0, 760, 0, 0, + 1399, 0, 777, 712, 712, 2233, 2234, 376, 0, 0, + 0, 608, 0, 0, 0, 0, 0, 0, 241, 237, + 245, 248, 258, 265, 0, 277, 279, 282, 238, 246, + 251, 252, 259, 280, 239, 242, 243, 247, 281, 283, + 240, 260, 264, 278, 262, 267, 270, 271, 276, 0, + 211, 0, 0, 0, 0, 0, 2071, 0, 0, 2104, + 2105, 2106, 2107, 2108, 2109, 2110, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, + 2066, 0, 0, 1806, 1807, 1808, 1809, 0, 1813, 0, + 1857, 0, 0, 0, 0, 0, 0, 2125, 2129, 0, + 0, 2062, 2062, 0, 0, 2062, 2058, 0, 0, 0, + 0, 0, 0, 2062, 1995, 0, 0, 1997, 2013, 0, + 0, 1999, 2000, 0, 2003, 2004, 2062, 0, 2062, 2008, + 2062, 2062, 2062, 1989, 1990, 0, 0, 0, 2058, 2058, + 2058, 2058, 0, 0, 2058, 2058, 2058, 2058, 2058, 2058, + 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, + 2058, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 980, 950, 973, 0, 981, 0, -2, + 0, 1002, 1004, 1006, 1008, 1026, 1027, 1029, 1030, 1032, + 1033, 1035, 1036, 956, 0, 0, 131, 0, 0, 0, + 112, 0, 0, 110, 0, 0, 0, 0, 81, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 362, 0, 367, 353, 2462, 0, 352, 0, 0, + 0, 0, 1399, 1399, 0, 0, 1189, 0, 0, 1399, + 1399, 1229, 0, 0, 0, 0, 0, 0, 0, 0, + 1399, 1399, 1399, 0, 1416, 0, 0, 0, 0, 0, + 913, 913, 920, 0, 967, 0, 0, 846, 845, 0, + 1078, 1079, 1080, 1115, 1139, 1139, 0, 1139, 1119, 1647, + 0, 0, 0, 0, 1410, 0, 0, 0, 0, 0, + 0, 0, 780, 0, 0, 0, 501, 502, 0, 0, + 847, 0, 295, 299, 0, 302, 0, 2469, 0, 2469, + 0, 0, 309, 0, 0, 0, 0, 0, 0, 339, + 340, 0, 0, 0, 0, 330, 333, 1630, 1631, 1324, + 1325, 334, 335, 389, 390, 0, 973, 1001, 1003, 1005, + 1007, 996, 997, 973, 0, 973, 0, 1012, 1020, 0, + 0, 0, 607, 607, 0, 0, 0, 0, 0, 607, + 0, 0, 0, 822, 0, 824, 0, 0, 607, 0, + 0, 0, 1071, 1065, 1067, 1144, 170, 1041, 7, 152, + 149, 0, 18, 0, 0, 18, 18, 0, 18, 344, + 0, 2230, 2228, 2229, 0, 2135, 2215, 0, 2162, 0, + 2163, 2164, 2165, 2166, 2167, 2178, 2179, 2180, 2181, 2182, + 2183, 0, 0, 2158, 0, 2159, 2160, 2161, 2151, 2221, + 0, 2153, 2154, 0, 2155, 2156, 342, 472, 0, 0, + 2063, 1193, 0, 0, 973, 948, 0, 978, 0, 0, + 0, 607, 1636, 0, 1647, 0, 0, 0, 0, 0, + 424, 0, 435, 429, 0, 436, 431, 432, 0, 0, + 454, 456, 457, 458, 459, 443, 444, 782, 408, 409, + 410, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, -2, 0, 0, 413, 180, 0, 0, 465, 0, + 467, 0, 88, 469, 0, 678, 0, 1063, 0, 0, + 1063, 1063, 0, 0, 1063, 0, 696, 762, 763, 1064, + 700, 0, 0, 707, 2421, 1316, 712, 1063, 1063, 719, + 713, 720, 766, 721, 722, 723, 764, 1063, 1063, 975, + 761, 764, 746, 765, 764, 1647, 750, 0, 755, 758, + 759, 1647, 778, 1647, 0, 776, 727, 728, 377, 378, + 0, 0, 0, 224, 225, 226, 227, 228, 229, 231, + 215, 813, 815, 1328, 0, 1319, 0, 234, 275, 207, + 0, 0, 0, 0, 2067, 2068, 2069, 2070, 2075, 0, + 2077, 2079, 2081, 2083, 0, 2101, -2, -2, 1785, 1786, + 1787, 1788, 1789, 1790, 1791, 1792, 1793, 1794, 1795, 1796, + 1797, 1798, 2086, 2099, 2100, 0, 0, 0, 0, 0, + 0, 2097, 2097, 2092, 0, 1803, 1818, 1861, 1874, 1874, + 1827, 1632, 1633, 1804, 0, 0, 1854, 1858, 0, 0, + 0, 0, 0, 0, 1378, 2214, 0, 171, 2096, 2057, + 1956, 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, + 1966, 1967, 1968, 1969, 1970, 1971, 1972, 1973, 1974, 1975, + 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 0, + 0, 2066, 0, 0, 0, 0, 2059, 2060, 0, 0, + 0, 1944, 0, 0, 1950, 1951, 1952, 0, 898, 0, + 2023, 1996, 2014, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1985, 1986, 1987, 1988, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 973, 980, 1098, + 1100, 0, 907, 909, 910, 945, 978, 958, 0, 0, + 0, 127, 132, 0, 1471, 118, 0, 0, 0, 118, + 0, 0, 0, 118, 0, 0, 0, 1300, 1412, 89, + 1299, 1414, 0, 0, 0, 0, 0, 0, 0, 371, + 372, 0, 0, 366, 354, 2462, 356, 0, 0, 0, + 0, 0, 0, 1176, 0, 0, 0, 1207, 0, 0, + 0, 1244, 1245, 0, 605, 1310, 0, 0, 0, 1326, + 1382, 1395, 0, 0, 0, 1477, 1230, 1235, 1236, 1237, + 1231, 1232, 1238, 1239, 889, 903, 884, 0, 892, 0, + 0, 0, 969, 0, 0, 1113, 0, 0, 0, 0, + 0, 0, 0, 0, 1105, 0, 0, 0, 1104, 0, + 0, 0, 0, 0, 0, 971, 1140, 0, 1142, 1143, + 1117, -2, 0, 1074, 1076, 0, 1122, 2062, 0, 0, + 1479, 971, 499, 500, 505, 507, 0, 566, 566, 566, + 549, 566, 0, 0, 537, 2235, 0, 0, 0, 0, + 546, 2235, 0, 0, 2235, 2235, 2235, 2235, 2235, 2235, + 2235, 0, 0, 2235, 2235, 2235, 2235, 2235, 2235, 2235, + 2235, 2235, 2235, 2235, 0, 2235, 2235, 2235, 2235, 2235, + 1616, 2235, 0, 1411, 556, 557, 558, 559, 564, 565, + 0, 0, 510, 511, 0, 0, 0, 0, 0, 600, + 0, 0, 1243, 0, 605, 0, 0, 1288, 0, 0, + 1076, 0, 0, 0, 0, 307, 308, 296, 0, 297, + 0, 0, 310, 311, 0, 313, 314, 315, 322, 2326, + 2440, 317, 319, 0, 0, 323, 336, 337, 338, 0, + 0, 328, 329, 0, 0, 392, 393, 395, 0, 978, + 0, 1011, 1013, 1014, 1015, 0, 0, 1019, 1021, 1022, + 1023, 1024, 1413, 1401, 85, 86, 2642, 807, 0, 0, + 810, 811, 812, 816, 0, 0, 819, 820, 821, 0, + 0, 1297, 0, 1301, 1303, 1401, 1063, 0, 1072, 0, + 1068, 1145, 0, 1147, 0, 0, 150, 18, 0, 143, + 140, 0, 0, 0, 0, 0, 2192, 2130, 2231, 0, + 0, 0, 0, 2212, 0, 0, 2152, 0, 0, 0, + 133, 156, 928, 978, 0, 922, 0, 982, 983, 986, + 866, 903, 853, 0, 855, 892, 0, 0, 0, 0, + 0, 0, 1638, 0, 0, 0, 0, 1710, 0, 437, + 433, 453, 0, 0, 462, 0, 1635, 468, 471, 675, + 0, 677, 680, 761, 0, 683, 684, 761, 0, 0, + 690, 695, 703, 701, 0, 705, 0, 706, 0, 1318, + 761, 714, 715, 1063, 738, 739, 0, 0, 1063, 761, + 761, 749, 764, 773, 0, 774, 1647, 0, 218, 1313, + 0, 219, 223, 213, 0, 0, 0, 1315, 1320, 0, + 233, 0, 0, 208, 209, 1462, 1471, 0, 0, 0, + 158, 2076, 2078, 2080, 2082, 2084, 0, 2087, 2097, 2097, + 2093, 0, 2088, 0, 2090, 0, 1862, 1875, 1876, 1863, + 2067, 1810, 0, 1859, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 986, 0, 0, 0, 1932, 1934, 0, + 0, 0, 1939, 0, 1941, 1942, 1943, 1945, 0, 0, + 0, 1949, 0, 1994, 2015, 1998, 2001, 0, 2005, 0, + 2007, 2009, 2010, 2011, 0, 0, 0, 980, 980, 0, + 0, 1901, 1901, 1901, 0, 0, 0, 0, 1901, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1831, 0, 1832, 1833, 1834, 0, 1836, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 978, 973, + 0, 1101, 922, 952, 0, 0, 0, 0, 0, 0, + 1469, 0, 108, 0, 113, 0, 0, 109, 114, 0, + 0, 111, 0, 0, 120, 90, 0, 0, 1419, 1420, + 0, 0, 0, 373, 361, 363, 0, 355, 0, 1400, + 0, 0, 0, 1550, 1560, 1180, 0, 0, -2, 1209, + 0, 971, 971, 1255, 2235, 0, 609, 0, 0, 1312, + 0, 1277, 0, 0, 0, -2, 0, 0, 1395, 0, + 0, 1481, 0, 866, 0, 883, 900, 0, 904, 0, + 0, 896, 888, 893, 0, 0, 0, 918, 866, 866, + 23, 972, 0, 0, 0, 833, 837, 0, 1109, 1109, + 1109, 1084, 0, 1086, 1106, 1087, 1107, 0, 0, 1091, + 0, 1093, 0, 1095, 0, 1089, 1090, 1097, 1088, 1116, + 1141, 0, 1118, 0, 1121, 1123, 1124, 1130, 0, 0, + 0, 0, 0, 0, 495, 496, 1481, 0, 0, 1410, + 1547, 1515, 527, 0, 1660, 1661, 567, 0, 1668, 1677, + 1677, 1399, 1748, 0, 1677, 0, 0, 1679, 1680, 0, + 0, 0, 0, 550, 551, 0, 536, 0, 0, 0, + 0, 0, 0, 535, 0, 0, 577, 0, 0, 0, + 0, 0, 2236, 2235, 2235, 0, 544, 545, 0, 548, + 0, 0, 0, 0, 0, 0, 0, 0, 2235, 2235, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1607, 0, 0, 0, 0, 0, 0, 0, 1622, + 1623, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1255, 2235, 0, 0, 0, 0, 609, 1307, 1307, 1275, + 1293, 0, 503, 504, 574, 0, 0, 0, 300, 301, + 0, 0, 306, 324, 326, 298, 0, 0, 0, 325, + 327, 331, 332, 391, 394, 396, 922, 1009, 1016, 1017, + 82, 1402, 808, 809, 0, 1505, 1209, 825, 1298, 0, + 1305, 1054, 1066, 1073, 1146, 1148, 171, 1069, 0, 153, + 18, 152, 144, 145, 0, 18, 0, 0, 0, 0, + 2134, 2220, 2219, 2184, 0, 2185, 2217, 2222, 0, 2225, + 0, 476, 0, 932, 0, 922, 924, 949, 0, 0, + 989, 987, 988, 851, 0, 852, 0, 0, 859, 980, + 0, 918, 1302, 1639, 1640, 0, 1643, 1644, 1645, 1646, + 1637, 0, 0, 0, 0, 0, 0, 0, 783, 181, + 464, 679, 1063, 761, 1063, 0, 761, 761, 691, 1557, + 1558, 702, 708, 1317, 1063, 732, 976, 977, 1647, 1063, + 1063, 761, 779, 775, 0, 0, 0, 0, 0, 814, + 0, 215, 0, 0, 235, 0, 0, 0, 1471, 1466, + 2061, 2089, 2091, 0, 2098, 2094, 1805, 1814, 1855, 0, + 0, 0, 0, 0, 1864, 1865, 2218, 2218, 1868, 2214, + 2216, 2214, 1874, 1874, 0, 1379, 0, 1380, 986, 172, + 0, 0, 0, 0, 1901, 0, 0, 0, 899, 0, + 0, 0, 0, 0, 1897, 1899, 1901, 1901, 1908, 1902, + 1909, 1910, 1901, 1901, 1901, 1901, 1915, 1901, 1901, 1901, + 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 0, + 1895, 1835, 1837, 0, 1840, 0, 1843, 1844, 0, 0, + 0, 2119, 2120, 922, 978, 908, 946, 0, 0, 0, + 0, 71, 71, 1471, 0, 0, 0, 0, 0, 126, + 0, 0, 0, 0, 0, 0, 0, 1428, 1436, 0, + 365, 0, 91, 92, 94, 0, 0, 0, 0, 0, + 0, 0, 107, 1173, 0, 1174, 0, 1184, 0, 1178, + 0, 0, 1195, 1196, 1198, 0, 1201, 1202, 1203, 0, + 1210, 1211, 1213, 1214, 0, 2241, -2, -2, 2369, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, 2023, -2, 2025, -2, 2027, -2, 2029, -2, - -2, -2, -2, 2034, 2035, -2, 2037, -2, -2, -2, - -2, -2, -2, -2, 2014, 2015, 2016, 2017, 2006, 2007, - 2008, 2009, 2010, 2011, -2, -2, -2, 2178, 949, 0, - 0, 979, 1098, 0, 979, 0, 950, 1024, 1027, 1030, - 1033, 953, 0, 0, 129, 130, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, - 370, 358, 360, 0, 364, 0, 0, 360, 357, 351, - 0, 1392, 1392, 1392, 0, 0, 1392, 0, 0, 0, - 1392, 1392, 1392, 1392, 1392, 0, 1392, 0, 0, 0, - 1392, 0, 1227, 1338, 1339, 1340, 1390, 1391, 1497, 0, - 0, 0, 912, 2392, 0, 0, 920, 965, 0, 967, - 970, 843, 839, 840, 841, 842, 0, 0, 0, 606, - 606, 0, 606, 0, 0, 606, 0, 606, 606, 606, - 0, 845, 2355, 2456, 2316, 2427, 2258, 2408, 2620, 0, - 316, 2488, 321, 0, 2321, 2359, 0, 0, 2379, 0, - -2, 0, 397, 979, 0, 0, 949, 979, 979, 0, - 1009, 1017, 0, 0, 0, 606, 606, 0, 0, 606, - 606, 606, 606, 606, 0, 606, 606, 606, 0, 606, - 0, 606, 0, 606, 606, 606, 0, 1038, 1039, 1041, - 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 4, - 5, 18, 0, 0, 0, 0, 0, 0, 135, 134, - 0, 2181, 2217, 2121, 2122, 2123, 0, 2204, 2126, 2208, - 2208, 2208, 2208, 0, 2158, 2159, 2160, 2161, 2162, 2163, - 2164, 2165, 2166, 2167, 2208, 2208, 2208, 2208, 2208, 2208, - 0, 0, 2176, 2147, 2206, 2206, 2206, 2204, 2183, 2127, - 2128, 2129, 2130, 2131, 2132, 2133, 2134, 2135, 2136, 2137, - 2138, 2139, 2140, 2211, 2211, 2214, 2214, 2211, 2184, 2185, - 2186, 2187, 2188, 2189, 2190, 2191, 2192, 2193, 2194, 2195, - 2196, 2197, 2198, 2199, 2200, 2201, 0, 475, 473, 474, - 2052, 0, 0, 0, 979, -2, 0, 0, 910, 0, - 784, 1631, 0, 0, 423, 1700, 0, 0, 427, 0, - 428, 0, 0, 430, 0, 0, 0, 452, 0, 455, - 438, 439, 440, 441, 442, 434, 0, 210, 0, 414, - 415, 411, 0, 460, 461, 463, 466, 1627, 80, 84, - 87, 0, 972, 0, 675, 760, 760, 760, 760, 691, - 692, 693, 0, 1062, 0, 0, 696, 697, 0, 698, - 0, 0, 709, 760, 760, 715, 716, 711, 710, 766, - 767, 763, 0, 763, 763, 1062, 0, 734, 735, 736, - 760, 760, 742, 973, 0, 743, 744, 763, 0, 768, - 769, 1062, 0, 0, 1062, 1062, 0, 752, 753, 0, - 756, 760, 0, 759, 0, 0, 1392, 0, 776, 711, - 711, 2223, 2224, 376, 0, 0, 0, 607, 0, 0, - 0, 0, 0, 0, 241, 237, 245, 248, 258, 265, - 0, 277, 279, 282, 238, 246, 251, 252, 259, 280, - 239, 242, 243, 247, 281, 283, 240, 260, 264, 278, - 262, 267, 270, 271, 276, 0, 211, 0, 0, 0, - 0, 0, 2061, 0, 0, 2094, 2095, 2096, 2097, 2098, - 2099, 2100, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, -2, 2056, 0, 0, 1796, - 1797, 1798, 1799, 0, 1803, 0, 1847, 0, 0, 0, - 0, 0, 0, 2115, 2119, 0, 0, 2052, 2052, 0, - 0, 2052, 2048, 0, 0, 0, 0, 0, 0, 2052, - 1985, 0, 0, 1987, 2003, 0, 0, 1989, 1990, 0, - 1993, 1994, 2052, 0, 2052, 1998, 2052, 2052, 2052, 1979, - 1980, 0, 0, 0, 2048, 2048, 2048, 2048, 0, 0, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, - 2048, 2048, 2048, 2048, 2048, 2048, 2048, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 979, - 949, 972, 0, 980, 0, -2, 0, 1001, 1003, 1005, - 1007, 1025, 1026, 1028, 1029, 1031, 1032, 1034, 1035, 955, - 0, 0, 131, 0, 0, 0, 112, 0, 0, 110, - 0, 0, 0, 0, 81, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 362, 0, 367, - 353, 2449, 0, 352, 0, 0, 0, 0, 1392, 1392, - 0, 0, 1188, 0, 0, 1392, 1392, 1228, 0, 0, - 0, 0, 0, 0, 0, 0, 1392, 1392, 1392, 0, - 1409, 0, 0, 0, 0, 0, 912, 912, 919, 0, - 966, 0, 0, 845, 844, 0, 1077, 1078, 1079, 1114, - 1138, 1138, 0, 1138, 1118, 1640, 0, 0, 0, 0, - 1403, 0, 0, 0, 0, 0, 0, 0, 779, 0, - 0, 0, 501, 502, 0, 0, 846, 0, 295, 299, - 0, 302, 0, 2456, 0, 2456, 0, 0, 309, 0, - 0, 0, 0, 0, 0, 339, 340, 0, 0, 0, - 0, 330, 333, 1623, 1624, 1323, 1324, 334, 335, 389, - 390, 0, 972, 1000, 1002, 1004, 1006, 995, 996, 972, - 0, 972, 0, 1011, 1019, 0, 0, 0, 606, 606, - 0, 0, 0, 0, 0, 606, 0, 0, 0, 821, - 0, 823, 0, 0, 606, 0, 0, 0, 1070, 1064, - 1066, 1143, 170, 1040, 7, 152, 149, 0, 18, 0, - 0, 18, 18, 0, 18, 344, 0, 2220, 2218, 2219, - 0, 2125, 2205, 0, 2152, 0, 2153, 2154, 2155, 2156, - 2157, 2168, 2169, 2170, 2171, 2172, 2173, 0, 0, 2148, - 0, 2149, 2150, 2151, 2141, 2211, 0, 2143, 2144, 0, - 2145, 2146, 342, 472, 0, 0, 2053, 1192, 0, 0, - 972, 947, 0, 977, 0, 0, 0, 606, 1629, 0, - 1640, 0, 0, 0, 0, 0, 424, 0, 435, 429, - 0, 436, 431, 432, 0, 0, 454, 456, 457, 458, - 459, 443, 444, 781, 408, 409, 410, 398, 399, 400, - 401, 402, 403, 404, 405, 406, 407, -2, 0, 0, - 413, 180, 0, 0, 465, 0, 467, 0, 88, 469, - 0, 677, 0, 1062, 0, 0, 1062, 1062, 0, 0, - 1062, 0, 695, 761, 762, 1063, 699, 0, 0, 706, - 2408, 1315, 711, 1062, 1062, 718, 712, 719, 765, 720, - 721, 722, 763, 1062, 1062, 974, 760, 763, 745, 764, - 763, 1640, 749, 0, 754, 757, 758, 1640, 777, 1640, - 0, 775, 726, 727, 377, 378, 0, 0, 0, 224, - 225, 226, 227, 228, 229, 231, 215, 812, 814, 1327, - 0, 1318, 0, 234, 275, 207, 0, 0, 0, 0, - 2057, 2058, 2059, 2060, 2065, 0, 2067, 2069, 2071, 2073, - 0, 2091, -2, -2, 1775, 1776, 1777, 1778, 1779, 1780, - 1781, 1782, 1783, 1784, 1785, 1786, 1787, 1788, 2076, 2089, - 2090, 0, 0, 0, 0, 0, 0, 2087, 2087, 2082, - 0, 1793, 1808, 1851, 1864, 1864, 1817, 1625, 1626, 1794, - 0, 0, 1844, 1848, 0, 0, 0, 0, 0, 0, - 1371, 2204, 0, 171, 2086, 2047, 1946, 1947, 1948, 1949, - 1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, - 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, - 1970, 1971, 1972, 1973, 1974, 0, 0, 2056, 0, 0, - 0, 0, 2049, 2050, 0, 0, 0, 1934, 0, 0, - 1940, 1941, 1942, 0, 897, 0, 2013, 1986, 2004, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1975, 1976, 1977, 1978, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 972, 979, 1097, 1099, 0, 906, 908, - 909, 944, 977, 957, 0, 0, 0, 127, 132, 0, - 1464, 118, 0, 0, 0, 118, 0, 0, 0, 118, - 0, 0, 0, 1299, 1405, 89, 1298, 1407, 0, 0, - 0, 0, 0, 0, 0, 371, 372, 0, 0, 366, - 354, 2449, 356, 0, 0, 0, 0, 0, 0, 1175, - 0, 0, 0, 1206, 0, 0, 0, 1243, 1244, 0, - 604, 1309, 0, 0, 0, 1325, 1375, 1388, 0, 0, - 0, 1470, 1229, 1234, 1235, 1236, 1230, 1231, 1237, 1238, - 888, 902, 883, 0, 891, 0, 0, 0, 968, 0, - 0, 1112, 0, 0, 0, 0, 0, 0, 0, 0, - 1104, 0, 0, 0, 1103, 0, 0, 0, 0, 0, - 0, 970, 1139, 0, 1141, 1142, 1116, -2, 0, 1073, - 1075, 0, 1121, 2052, 0, 0, 1472, 970, 499, 500, - 505, 507, 0, 566, 566, 566, 549, 566, 0, 0, - 537, 2225, 0, 0, 0, 0, 546, 2225, 0, 0, - 2225, 2225, 2225, 2225, 2225, 2225, 2225, 0, 0, 2225, - 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, - 0, 2225, 2225, 2225, 2225, 2225, 1609, 2225, 0, 1404, - 556, 557, 558, 559, 564, 565, 0, 0, 510, 511, - 0, 0, 0, 0, 0, 599, 0, 0, 1242, 0, - 604, 0, 0, 1287, 0, 0, 1075, 0, 0, 0, - 0, 307, 308, 296, 0, 297, 0, 0, 310, 311, - 0, 313, 314, 315, 322, 2316, 2427, 317, 319, 0, - 0, 323, 336, 337, 338, 0, 0, 328, 329, 0, - 0, 392, 393, 395, 0, 977, 0, 1010, 1012, 1013, - 1014, 0, 0, 1018, 1020, 1021, 1022, 1023, 1406, 1394, - 85, 86, 2629, 806, 0, 0, 809, 810, 811, 815, - 0, 0, 818, 819, 820, 0, 0, 1296, 0, 1300, - 1302, 1394, 1062, 0, 1071, 0, 1067, 1144, 0, 1146, - 0, 0, 150, 18, 0, 143, 140, 0, 0, 0, - 0, 0, 2182, 2120, 2221, 0, 0, 0, 0, 2202, - 0, 0, 2142, 0, 0, 0, 133, 156, 927, 977, - 0, 921, 0, 981, 982, 985, 865, 902, 852, 0, - 854, 891, 0, 0, 0, 0, 0, 0, 1631, 0, - 0, 0, 0, 1701, 0, 437, 433, 453, 0, 0, - 462, 0, 1628, 468, 471, 674, 0, 676, 679, 760, - 0, 682, 683, 760, 0, 0, 689, 694, 702, 700, - 0, 704, 0, 705, 0, 1317, 760, 713, 714, 1062, - 737, 738, 0, 0, 1062, 760, 760, 748, 763, 772, - 0, 773, 1640, 0, 218, 1312, 0, 219, 223, 213, - 0, 0, 0, 1314, 1319, 0, 233, 0, 0, 208, - 209, 1455, 1464, 0, 0, 0, 158, 2066, 2068, 2070, - 2072, 2074, 0, 2077, 2087, 2087, 2083, 0, 2078, 0, - 2080, 0, 1852, 1865, 1866, 1853, 2057, 1800, 0, 1849, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 985, - 0, 0, 0, 1922, 1924, 0, 0, 0, 1929, 0, - 1931, 1932, 1933, 1935, 0, 0, 0, 1939, 0, 1984, - 2005, 1988, 1991, 0, 1995, 0, 1997, 1999, 2000, 2001, - 0, 0, 0, 979, 979, 0, 0, 1891, 1891, 1891, - 0, 0, 0, 0, 1891, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1821, 0, - 1822, 1823, 1824, 0, 1826, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 977, 972, 0, 1100, 921, 951, - 0, 0, 0, 0, 0, 0, 1462, 0, 108, 0, - 113, 0, 0, 109, 114, 0, 0, 111, 0, 0, - 120, 90, 0, 0, 1412, 1413, 0, 0, 0, 373, - 361, 363, 0, 355, 0, 1393, 0, 0, 0, 1543, - 1553, 1179, 0, 0, -2, 1208, 0, 970, 970, 1254, - 2225, 0, 608, 0, 0, 1311, 0, 1276, 0, 0, - 0, -2, 0, 0, 1388, 0, 0, 1474, 0, 865, - 0, 882, 899, 0, 903, 0, 0, 895, 887, 892, - 0, 0, 0, 917, 865, 865, 23, 971, 0, 0, - 0, 832, 836, 0, 1108, 1108, 1108, 1083, 0, 1085, - 1105, 1086, 1106, 0, 0, 1090, 0, 1092, 0, 1094, - 0, 1088, 1089, 1096, 1087, 1115, 1140, 0, 1117, 0, - 1120, 1122, 1123, 1129, 0, 0, 0, 0, 0, 0, - 495, 496, 1474, 0, 0, 1403, 1540, 1508, 527, 0, - 1653, 1654, 567, 0, 1660, 1669, 1392, 1739, 0, 1669, - 0, 0, 1671, 1672, 0, 0, 0, 0, 550, 551, - 0, 536, 0, 0, 0, 0, 0, 0, 535, 0, - 0, 577, 0, 0, 0, 0, 0, 2226, 2225, 2225, - 0, 544, 545, 0, 548, 0, 0, 0, 0, 0, - 0, 0, 0, 2225, 2225, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1600, 0, 0, 0, - 0, 0, 0, 0, 1615, 1616, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1254, 2225, 0, 0, 0, - 0, 608, 1306, 1306, 1274, 1292, 0, 503, 504, 574, - 0, 0, 0, 300, 301, 0, 0, 306, 324, 326, - 298, 0, 0, 0, 325, 327, 331, 332, 391, 394, - 396, 921, 1008, 1015, 1016, 82, 1395, 807, 808, 0, - 1498, 1208, 824, 1297, 0, 1304, 1053, 1065, 1072, 1145, - 1147, 171, 1068, 0, 153, 18, 152, 144, 145, 0, - 18, 0, 0, 0, 0, 2124, 2210, 2209, 2174, 0, - 2175, 2207, 2212, 0, 2215, 0, 476, 0, 931, 0, - 921, 923, 948, 0, 0, 988, 986, 987, 850, 0, - 851, 0, 0, 858, 979, 0, 917, 1301, 1632, 1633, - 0, 1636, 1637, 1638, 1639, 1630, 0, 0, 0, 0, - 0, 0, 0, 782, 181, 464, 678, 1062, 760, 1062, - 0, 760, 760, 690, 1550, 1551, 701, 707, 1316, 1062, - 731, 975, 976, 1640, 1062, 1062, 760, 778, 774, 0, - 0, 0, 0, 0, 813, 0, 215, 0, 0, 235, - 0, 0, 0, 1464, 1459, 2051, 2079, 2081, 0, 2088, - 2084, 1795, 1804, 1845, 0, 0, 0, 0, 0, 1854, - 1855, 2208, 2208, 1858, 2204, 2206, 2204, 1864, 1864, 0, - 1372, 0, 1373, 985, 172, 0, 0, 0, 0, 1891, - 0, 0, 0, 898, 0, 0, 0, 0, 0, 1887, - 1889, 1891, 1891, 1898, 1892, 1899, 1900, 1891, 1891, 1891, - 1891, 1905, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, - 1891, 1891, 1891, 1891, 0, 1885, 1825, 1827, 0, 1830, - 0, 1833, 1834, 0, 0, 0, 2109, 2110, 921, 977, - 907, 945, 0, 0, 0, 0, 71, 71, 1464, 0, - 0, 0, 0, 0, 126, 0, 0, 0, 0, 0, - 0, 0, 1421, 1429, 0, 365, 0, 91, 92, 94, - 0, 0, 0, 0, 0, 0, 0, 107, 1172, 0, - 1173, 0, 1183, 0, 1177, 0, 0, 1194, 1195, 1197, - 0, 1200, 1201, 1202, 0, 1209, 1210, 1212, 1213, 0, - 2231, -2, -2, 2356, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, -2, 2290, + -2, -2, -2, -2, 2300, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, -2, -2, 1207, 0, - 0, 1258, 1255, 1256, 1257, 0, 0, 1306, 609, 610, - 611, 612, 0, 0, 0, 1310, 0, 0, 0, 1267, - 0, 0, 0, 1376, 1377, 1378, 1379, 1380, 1381, 1382, - 1383, 1384, 1385, -2, 1398, 0, 1640, 0, 0, 1646, - 1446, 0, 0, 1451, 0, 0, 1646, 1444, 2225, 1646, - 1482, 0, 1471, 862, 0, 902, 904, 0, 0, 902, - 0, 0, 913, 0, 914, 0, 0, 863, 864, 0, - 1113, 910, 0, -2, 0, 0, 834, 0, 0, 1080, - 1109, 0, 0, 1081, 1082, 1084, 1107, 0, 1101, 1091, - 1093, 1095, 1074, 0, 1124, 0, 0, 1126, 1127, 1128, - 1119, 491, 0, 1545, 0, 1548, 1549, 493, 0, 1555, - 0, 1558, 1559, 1482, 1473, 0, 506, 566, 0, 1528, - 0, 0, 1534, 0, 1541, 520, 0, 568, 0, 1659, - 1689, 1670, 1689, 1740, 1689, 1689, 1392, 0, 568, 0, - 0, 538, 0, 0, 0, 0, 0, 534, 571, 985, - 521, 523, 524, 525, 575, 576, 578, 0, 580, 581, - 540, 552, 553, 554, 555, 0, 0, 0, 547, 560, - 561, 562, 563, 522, 1577, 1578, 1579, 1582, 1583, 1584, - 1585, 0, 0, 1588, 1589, 1590, 1591, 1592, 1686, 1687, - 1688, 1593, 1594, 1595, 1596, 1597, 1598, 1599, 1617, 1618, - 1619, 1620, 1621, 1622, 1601, 1602, 1603, 1604, 1605, 1606, - 1607, 1608, 0, 0, 1612, 0, 0, 1177, 0, 514, - 515, 0, 517, 0, 0, 1258, 0, 0, 0, 0, - 0, 1306, 602, 0, 0, 603, 1276, 0, 1294, 0, - 1288, 1289, 0, 0, 880, 1062, 384, 0, 1062, 294, - 303, 304, 305, 312, 0, 628, 318, 991, 816, 817, - 1499, 1500, 0, 0, 1051, 0, 0, 148, 151, 0, - 146, 0, 0, 0, 0, 138, 136, 2203, 0, 0, - 157, 933, 195, 0, 0, 991, 925, 0, 0, 983, - 984, 0, 866, 0, 902, 972, 911, 0, 1641, 1642, - 1643, 1644, 0, 1702, 425, 680, 1062, 684, 760, 1062, - 1062, 0, 708, 746, 750, 747, 1062, 0, 1313, 215, - 220, 221, 222, 216, 214, 1320, 0, 1322, 0, 1457, - 0, 0, 2085, 1850, 1805, 0, 1807, 1809, 1856, 1857, - 1859, 1860, 1861, 1862, 1863, 1810, 0, 1374, 1923, 1925, - 0, 1927, 1928, 1930, 1936, 1937, 0, 1992, 1996, 0, - 0, 1983, 0, 0, 0, 0, 1896, 1897, 1901, 1902, - 1903, 1904, 1906, 1907, 1908, 1909, 1910, 1911, 1912, 1913, - 1914, 1915, 1916, 1917, 0, 979, 1886, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 991, - 921, 0, 0, 960, 961, 962, 963, 964, 956, 0, - 0, 73, 0, 73, 1463, 1465, 119, 121, 0, 115, - 116, 117, 0, 0, 1143, 1435, 1640, 1423, 0, 1415, - 0, 1429, 0, 0, 0, 93, 0, 95, 0, 2411, - 0, 0, 0, 0, 1394, 0, 0, 1185, 0, 0, - 1176, 0, 1187, 1203, 1199, 0, 0, 0, 1214, 0, - 0, 1225, 0, 0, 1246, 1247, 1248, 1272, 1260, 0, - 614, 615, 0, 0, 0, 627, 623, 624, 625, 605, - 1305, 1283, 0, 0, 1283, 1270, 0, 0, 1282, 0, - 1399, 2225, 2225, 2225, 1435, 0, 0, 0, 1647, 1648, - 1650, 1651, 1652, 1448, 1450, 1437, 0, 0, 0, 0, - 1485, 0, 0, 1476, 0, 0, 900, 0, 905, 902, - 886, 896, 885, 893, 894, 918, 0, 915, 969, 1138, - 0, 0, 1062, 833, 836, 837, 0, 1110, 0, 1102, - 1076, 1133, 1130, 0, 0, 0, 0, 0, 0, 0, - 0, 1485, 498, 1506, 0, 0, 0, 0, 0, 1538, - 0, 0, 1510, 0, 539, 569, 0, -2, 0, 1690, - 0, 1673, 1690, 0, 0, 1689, 0, 528, 568, 0, - 0, 0, 582, 0, 590, 591, 1342, 1342, 1342, 1342, - 588, 1735, 0, 589, 0, 573, 0, 579, 1580, 1581, - 0, 1586, 1587, 0, 1611, 0, 0, 509, 512, 0, - 1181, 1182, -2, 0, 0, 0, 594, 0, 0, 0, - 595, 596, 601, 1307, 1308, 1267, 0, 1283, 0, 1293, - 0, 1290, 1291, 979, 0, 0, 0, 865, 320, 943, - 0, 0, 822, 0, 1036, 0, 0, 1069, 0, 18, - 0, 0, 141, 2213, 2216, 935, 0, 932, 196, 0, - 0, 0, 946, 927, 0, 924, 0, 989, 990, 853, - 902, 857, 856, 859, 0, 1635, 1645, 681, 1062, 686, - 687, 0, 751, 217, 212, 1321, 1467, 0, 1458, 0, - 1759, 1819, 1820, 0, 1938, 0, 0, 1891, 1888, 1891, - 1890, 1891, 1882, 0, 1828, 0, 1831, 0, 1835, 1836, - 0, 1838, 1839, 1840, 0, 1842, 1843, 2179, 991, 958, - 0, 954, 0, 69, 0, 72, 70, 0, 0, 0, - 125, 1410, 0, 1435, 1414, 0, 0, 0, 1416, 0, - 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, - 0, 105, 0, 0, 0, 0, 1184, 0, 1178, 0, - 0, 1196, 1198, 0, 1211, 0, 1232, 1232, 1259, 1245, - 0, 1226, 0, 0, 616, 617, 0, 620, 626, 1261, - 0, 0, 1264, 1265, 1263, 1266, 0, 0, 1280, 0, - 0, 0, 0, 1386, 0, 1389, -2, 0, 1435, 0, - 0, 0, 1543, -2, 1491, 0, 1483, 0, 1475, 0, - 1478, 0, 890, 901, 884, 0, 1062, 1062, -2, 865, - 835, 0, 869, 871, 0, 0, 0, 1134, 1136, 1131, - 1132, 1546, 492, 1547, 1556, 494, 1557, 1560, 1561, 1491, - 1508, 0, 1529, 0, 0, 0, 0, 0, 0, 0, - 1509, 0, 1522, 570, 1691, -2, 1705, 1707, 0, 1404, - 1710, 1711, 0, 0, 0, 0, 0, 0, 0, 0, - 1766, 1721, 0, 0, 0, 1726, 1727, 1728, 0, 0, - 0, 0, 2103, 2104, 0, 1738, 0, 0, 0, 0, - 0, 0, 0, 1667, 529, 530, 0, 532, 533, 1342, - 0, 584, 585, 586, 587, 1736, 572, 526, 2225, 542, - 1610, 1613, 1614, 513, 516, 0, 0, 600, 597, 598, - 1270, 1275, 1286, 1295, 881, 972, 1062, 385, 386, 382, - 992, 993, 0, 1303, 1052, 1060, 2647, 2649, 2646, 142, - 147, 0, 0, 937, 0, 934, 0, 928, 930, 206, - 931, 926, 978, 855, 1634, 685, 760, 166, 198, 0, - 0, 1806, 0, 0, 0, 1926, 1981, 1982, 1894, 1895, - 1918, 0, 1883, 0, 1877, 1878, 1879, 1884, 0, 0, - 0, 0, 2180, 0, 952, 74, 123, 122, 0, 0, - 1411, 0, 0, 0, 1427, 1428, 0, 1430, 1431, 1432, - 0, 0, 0, 0, 78, 0, 0, 0, 1394, 0, - 1394, 0, 0, 0, 1544, 1554, 0, 1186, 1180, 1190, - 1204, 0, 1217, 1215, 1224, 1239, 1223, 0, 0, 0, - 613, 618, 0, 621, 622, 1284, 1283, 0, 1268, 1269, - 0, 1278, 0, 0, 1400, 1401, 1402, 1272, 1501, 1447, - 0, -2, 1574, 0, 1649, 1452, 1438, 0, 1440, 1441, - 1442, 1443, 1467, 2225, 1542, 1501, 1479, 0, 1486, 0, - 1484, 1477, 889, 916, 979, 865, 830, 0, 872, 878, - 0, 877, 1111, 1125, 0, 1488, 508, 1540, 1530, 0, - 1532, 0, 0, 0, 0, 1511, -2, 0, 1706, 1708, - 1709, 1712, 1713, 1714, 1715, 1716, 1771, 1772, 1773, 0, - 0, 1719, 1768, 1769, 1770, 1720, 0, 0, 0, 1725, - 0, 0, 0, 2101, 2102, 1764, 0, 0, 1674, 1676, - 1677, 1678, 1679, 1680, 1681, 1682, 1683, 1684, 1685, 1675, - 0, 0, 0, 1666, 1668, 531, 583, 0, 1343, 2225, - 2225, 0, 0, 0, 1349, 1350, 2225, 2225, 2225, 2225, - 2225, 2225, 0, 0, 0, 2225, 2225, 2225, 2225, 2225, - 1365, 1366, 0, 2225, 2225, 0, 2225, 0, 0, 1285, - 865, 865, 0, 1054, 0, 0, 0, 0, 137, 139, - 154, 0, 936, 197, 0, 933, 1062, 168, 0, 189, - 0, 1468, 0, 1818, 0, 0, 0, 1893, 1880, 0, - 0, 0, 0, 0, 0, 2105, 2106, 2107, 0, 1829, - 1832, 1837, 1841, 959, 0, 1436, 1424, 1425, 1426, 1422, - 0, 0, 1433, 1434, 0, 76, 0, 99, 0, 0, - 100, 1394, 101, 1394, 0, 0, 1174, 0, 0, 1240, - 1241, 1249, 1250, 0, 1252, 1253, 1273, 619, 1262, 1271, - 1277, 1280, 0, 1342, 1387, 1503, 0, 1449, 1403, 1576, - 2225, 1272, 1454, 0, 1503, 1469, 0, 1481, 0, 1493, - 0, 1487, 972, 831, 870, 0, 0, 878, 1135, 1137, - 497, 0, 1490, 1526, 1531, 1533, 1535, 0, 1539, 1537, - 1512, -2, 0, 1520, 0, 0, 1717, 1718, 0, 0, - 2002, 2225, 0, 0, 1754, 0, 1342, 1342, 1342, 1342, - 0, 592, 593, 0, 0, 1346, 1347, 0, 0, 0, - 0, 0, 0, 0, 0, 1358, 1359, 0, 0, 0, - 0, 0, 0, 0, 0, 541, 0, 0, 519, 381, - 383, 994, 1061, 0, 0, 0, 0, 0, 935, 688, - 155, 0, 167, 186, 0, 199, 200, 0, 0, 0, - 0, 1460, 0, 1762, 1763, 0, 1867, 0, 0, 0, - 0, 1872, 1873, 1874, 1875, 1876, 124, 1429, 1429, 1394, - 78, 0, 98, 0, 102, 103, 0, 1394, 0, 1216, - 0, 1251, 1279, 1281, 1341, 1439, 0, 1540, 1575, 0, - 1453, 1553, 1563, 1445, 1480, 1492, 0, 1495, 865, 0, - 879, 0, 1489, 1507, 0, 1536, 1513, 1521, 0, 1516, - 0, 0, 0, 1767, 0, 1724, 0, 0, 1732, 1742, - 1755, 0, 0, 1655, 0, 1657, 0, 1661, 0, 1663, - 0, 0, 1344, 1345, 1348, 1351, 1352, 1353, 1354, 1355, - 1356, 0, 1360, 1361, 1362, 1363, 1364, 1367, 1368, 1369, - 1370, 543, 518, 0, 2052, 1056, 1057, 0, 939, 929, - 937, 169, 173, 0, 195, 192, 0, 201, 0, 0, - 0, 0, 1456, 0, 1760, 0, 1868, 1869, 1870, 1871, - 1417, 1429, 1418, 1429, 75, 77, 79, 97, 1394, 104, - 0, 1218, 1219, 1233, 0, 1528, 1580, 1552, 1562, 0, - 1494, 829, 0, 874, 0, 1527, 1515, 0, -2, 1523, - 0, 0, 2054, 2063, 2064, 1722, 1729, 0, 1731, 1733, - 1734, 1741, 1743, 1744, 0, 1756, 1757, 1758, 1765, 1342, - 1342, 1342, 1342, 1665, 1357, 1055, 0, 0, 938, 0, - 922, 160, 0, 0, 190, 191, 193, 0, 202, 0, - 204, 205, 0, 0, 1881, 1419, 1420, 106, 1220, 1504, - 0, 1506, 0, 0, 0, 1517, -2, 0, 1525, 0, - 1723, 1732, 1745, 0, 1746, 0, 0, 0, 1656, 1658, - 1662, 1664, 2052, 1058, 940, 1466, 0, 174, 0, 176, - 178, 179, 1692, 187, 188, 194, 203, 0, 0, 1205, - 1221, 0, 0, 1508, 0, 1565, 0, 1568, 1569, 873, - 0, 0, 1524, 2055, 1730, 1747, 1749, 1750, 0, 0, - 1748, 0, 161, 162, 0, 175, 0, 0, 1461, 1761, - 1222, 1505, 1502, 1564, 0, 0, 0, 0, 1751, 1753, - 1752, 1059, 0, 0, 177, 1693, 1566, 1567, 1570, 1571, - 0, 0, 163, 164, 165, 0, 0, 876, 1694, 0, - 0, 875, + -2, -2, -2, 1208, 0, 0, 1259, 1256, 1257, 1258, + 0, 0, 1307, 610, 611, 612, 613, 0, 0, 0, + 1311, 0, 0, 0, 1268, 0, 0, 0, 1383, 1384, + 1385, 1386, 1387, 1388, 1389, 1390, 1391, 1392, -2, 1405, + 0, 1647, 0, 0, 1653, 1453, 0, 0, 1458, 0, + 0, 1653, 1451, 2235, 1653, 1489, 0, 1478, 863, 0, + 903, 905, 0, 0, 903, 0, 0, 914, 0, 915, + 0, 0, 864, 865, 0, 1114, 911, 0, -2, 0, + 0, 835, 0, 0, 1081, 1110, 0, 0, 1082, 1083, + 1085, 1108, 0, 1102, 1092, 1094, 1096, 1075, 0, 1125, + 0, 0, 1127, 1128, 1129, 1120, 491, 0, 1552, 0, + 1555, 1556, 493, 0, 1562, 0, 1565, 1566, 1489, 1480, + 0, 506, 566, 0, 1535, 0, 0, 1541, 0, 1548, + 520, 0, 568, 0, 1667, 1698, 1678, 1698, 1698, 1749, + 1698, 1698, 1399, 0, 568, 0, 0, 538, 0, 0, + 0, 0, 0, 534, 571, 986, 521, 523, 524, 525, + 575, 576, 578, 0, 580, 581, 540, 552, 553, 554, + 555, 0, 0, 0, 547, 560, 561, 562, 563, 522, + 1584, 1585, 1586, 1589, 1590, 1591, 1592, 0, 0, 1595, + 1596, 1597, 1598, 1599, 1695, 1696, 1697, 1600, 1601, 1602, + 1603, 1604, 1605, 1606, 1624, 1625, 1626, 1627, 1628, 1629, + 1608, 1609, 1610, 1611, 1612, 1613, 1614, 1615, 0, 0, + 1619, 0, 0, 1178, 0, 514, 515, 0, 517, 0, + 0, 1259, 0, 0, 0, 0, 0, 1307, 603, 0, + 0, 604, 1277, 0, 1295, 0, 1289, 1290, 0, 0, + 881, 1063, 384, 0, 1063, 294, 303, 304, 305, 312, + 0, 629, 318, 992, 817, 818, 1506, 1507, 0, 0, + 1052, 0, 0, 148, 151, 0, 146, 0, 0, 0, + 0, 138, 136, 2213, 0, 0, 157, 934, 195, 0, + 0, 992, 926, 0, 0, 984, 985, 0, 867, 0, + 903, 973, 912, 0, 1648, 1649, 1650, 1651, 0, 1711, + 425, 681, 1063, 685, 761, 1063, 1063, 0, 709, 747, + 751, 748, 1063, 0, 1314, 215, 220, 221, 222, 216, + 214, 1321, 0, 1323, 0, 1464, 0, 0, 2095, 1860, + 1815, 0, 1817, 1819, 1866, 1867, 1869, 1870, 1871, 1872, + 1873, 1820, 0, 1381, 1933, 1935, 0, 1937, 1938, 1940, + 1946, 1947, 0, 2002, 2006, 0, 0, 1993, 0, 0, + 0, 0, 1906, 1907, 1911, 1912, 1913, 1914, 1916, 1917, + 1918, 1919, 1920, 1921, 1922, 1923, 1924, 1925, 1926, 1927, + 0, 980, 1896, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 992, 922, 0, 0, 961, + 962, 963, 964, 965, 957, 0, 0, 73, 0, 73, + 1470, 1472, 119, 121, 0, 115, 116, 117, 0, 0, + 1144, 1442, 1647, 1430, 0, 1422, 0, 1436, 0, 0, + 0, 93, 0, 95, 0, 2424, 0, 0, 0, 0, + 1401, 0, 0, 1186, 0, 0, 1177, 0, 1188, 1204, + 1200, 0, 0, 0, 1215, 0, 0, 1226, 0, 0, + 1247, 1248, 1249, 1273, 1261, 0, 615, 616, 0, 0, + 0, 628, 624, 625, 626, 606, 1306, 1284, 0, 0, + 1284, 1271, 0, 0, 1283, 0, 1406, 2235, 2235, 2235, + 1442, 0, 0, 0, 1654, 1655, 1657, 1658, 1659, 1455, + 1457, 1444, 0, 0, 0, 0, 1492, 0, 0, 1483, + 0, 0, 901, 0, 906, 903, 887, 897, 886, 894, + 895, 919, 0, 916, 970, 1139, 0, 0, 1063, 834, + 837, 838, 0, 1111, 0, 1103, 1077, 1134, 1131, 0, + 0, 0, 0, 0, 0, 0, 0, 1492, 498, 1513, + 0, 0, 0, 0, 0, 1545, 0, 0, 1517, 0, + 539, 569, 0, -2, 0, 1699, 0, 0, 1681, 1699, + 0, 0, 1698, 0, 528, 568, 0, 0, 0, 582, + 0, 591, 592, 1344, 1344, 1344, 1344, 1344, 589, 1744, + 0, 590, 0, 573, 0, 579, 1587, 1588, 0, 1593, + 1594, 0, 1618, 0, 0, 509, 512, 0, 1182, 1183, + -2, 0, 0, 0, 595, 0, 0, 0, 596, 597, + 602, 1308, 1309, 1268, 0, 1284, 0, 1294, 0, 1291, + 1292, 980, 0, 0, 0, 866, 320, 944, 0, 0, + 823, 0, 1037, 0, 0, 1070, 0, 18, 0, 0, + 141, 2223, 2226, 936, 0, 933, 196, 0, 0, 0, + 947, 928, 0, 925, 0, 990, 991, 854, 903, 858, + 857, 860, 0, 1642, 1652, 682, 1063, 687, 688, 0, + 752, 217, 212, 1322, 1474, 0, 1465, 0, 1768, 1829, + 1830, 0, 1948, 0, 0, 1901, 1898, 1901, 1900, 1901, + 1892, 0, 1838, 0, 1841, 0, 1845, 1846, 0, 1848, + 1849, 1850, 0, 1852, 1853, 2189, 992, 959, 0, 955, + 0, 69, 0, 72, 70, 0, 0, 0, 125, 1417, + 0, 1442, 1421, 0, 0, 0, 1423, 0, 0, 0, + 0, 0, 96, 0, 0, 0, 0, 0, 0, 105, + 0, 0, 0, 0, 1185, 0, 1179, 0, 0, 1197, + 1199, 0, 1212, 0, 1233, 1233, 1260, 1246, 0, 1227, + 0, 0, 617, 618, 0, 621, 627, 1262, 0, 0, + 1265, 1266, 1264, 1267, 0, 0, 1281, 0, 0, 0, + 0, 1393, 0, 1396, -2, 0, 1442, 0, 0, 0, + 1550, -2, 1498, 0, 1490, 0, 1482, 0, 1485, 0, + 891, 902, 885, 0, 1063, 1063, -2, 866, 836, 0, + 870, 872, 0, 0, 0, 1135, 1137, 1132, 1133, 1553, + 492, 1554, 1563, 494, 1564, 1567, 1568, 1498, 1515, 0, + 1536, 0, 0, 0, 0, 0, 0, 0, 1516, 0, + 1529, 570, 1700, -2, 1714, 1716, 0, 1411, 1719, 1720, + 0, 0, 0, 0, 0, 0, 0, 0, 1776, 1730, + 0, 0, 0, 1735, 1736, 1737, 0, 0, 0, 0, + 2113, 2114, 0, 1747, 0, 0, 0, 0, 0, 0, + 0, 0, 1675, 529, 530, 0, 532, 533, 1344, 0, + 584, 585, 586, 587, 588, 1745, 572, 526, 2235, 542, + 1617, 1620, 1621, 513, 516, 0, 0, 601, 598, 599, + 1271, 1276, 1287, 1296, 882, 973, 1063, 385, 386, 382, + 993, 994, 0, 1304, 1053, 1061, 2660, 2662, 2659, 142, + 147, 0, 0, 938, 0, 935, 0, 929, 931, 206, + 932, 927, 979, 856, 1641, 686, 761, 166, 198, 0, + 0, 1816, 0, 0, 0, 1936, 1991, 1992, 1904, 1905, + 1928, 0, 1893, 0, 1887, 1888, 1889, 1894, 0, 0, + 0, 0, 2190, 0, 953, 74, 123, 122, 0, 0, + 1418, 0, 0, 0, 1434, 1435, 0, 1437, 1438, 1439, + 0, 0, 0, 0, 78, 0, 0, 0, 1401, 0, + 1401, 0, 0, 0, 1551, 1561, 0, 1187, 1181, 1191, + 1205, 0, 1218, 1216, 1225, 1240, 1224, 0, 0, 0, + 614, 619, 0, 622, 623, 1285, 1284, 0, 1269, 1270, + 0, 1279, 0, 0, 1407, 1408, 1409, 1273, 1508, 1454, + 0, -2, 1581, 0, 1656, 1459, 1445, 0, 1447, 1448, + 1449, 1450, 1474, 2235, 1549, 1508, 1486, 0, 1493, 0, + 1491, 1484, 890, 917, 980, 866, 831, 0, 873, 879, + 0, 878, 1112, 1126, 0, 1495, 508, 1547, 1537, 0, + 1539, 0, 0, 0, 0, 1518, -2, 0, 1715, 1717, + 1718, 1721, 1722, 1723, 1724, 1725, 1781, 1782, 1783, 0, + 0, 1728, 1778, 1779, 1780, 1729, 0, 0, 0, 1734, + 0, 0, 0, 2111, 2112, 1774, 0, 0, 0, 1682, + 1684, 1685, 1686, 1687, 1688, 1689, 1690, 1691, 1692, 1693, + 1694, 1683, 0, 0, 0, 1674, 1676, 531, 583, 0, + 1345, 2235, 2235, 0, 0, 0, 1351, 1352, 2235, 2235, + 2235, 2235, 2235, 2235, 0, 0, 0, 2235, 2235, 2235, + 2235, 2235, 2235, 0, 1370, 1371, 1372, 0, 2235, 2235, + 2235, 0, 2235, 0, 0, 1286, 866, 866, 0, 1055, + 0, 0, 0, 0, 137, 139, 154, 0, 937, 197, + 0, 934, 1063, 168, 0, 189, 0, 1475, 0, 1828, + 0, 0, 0, 0, 1903, 1890, 0, 0, 0, 0, + 0, 0, 2115, 2116, 2117, 0, 1839, 1842, 1847, 1851, + 960, 0, 1443, 1431, 1432, 1433, 1429, 0, 0, 1440, + 1441, 0, 76, 0, 99, 0, 0, 100, 1401, 101, + 1401, 0, 0, 1175, 0, 0, 1241, 1242, 1250, 1251, + 0, 1253, 1254, 1274, 620, 1263, 1272, 1278, 1281, 0, + 1344, 1394, 1510, 0, 1456, 1410, 1583, 2235, 1273, 1461, + 0, 1510, 1476, 0, 1488, 0, 1500, 0, 1494, 973, + 832, 871, 0, 0, 879, 1136, 1138, 497, 0, 1497, + 1533, 1538, 1540, 1542, 0, 1546, 1544, 1519, -2, 0, + 1527, 0, 0, 1726, 1727, 0, 0, 2012, 2235, 0, + 0, 1763, 0, 1344, 1344, 1344, 1344, 1344, 0, 593, + 594, 0, 0, 1348, 1349, 0, 0, 0, 0, 0, + 0, 0, 0, 1360, 1361, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 541, 0, 0, 519, + 381, 383, 995, 1062, 0, 0, 0, 0, 0, 936, + 689, 155, 0, 167, 186, 0, 199, 200, 0, 0, + 0, 0, 1467, 0, 1771, 1773, 1772, 0, 1877, 0, + 0, 0, 0, 1882, 1883, 1884, 1885, 1886, 124, 1436, + 1436, 1401, 78, 0, 98, 0, 102, 103, 0, 1401, + 0, 1217, 0, 1252, 1280, 1282, 1343, 1446, 0, 1547, + 1582, 0, 1460, 1560, 1570, 1452, 1487, 1499, 0, 1502, + 866, 0, 880, 0, 1496, 1514, 0, 1543, 1520, 1528, + 0, 1523, 0, 0, 0, 1777, 0, 1733, 0, 0, + 1741, 1751, 1764, 0, 0, 1662, 0, 1664, 1665, 0, + 1669, 0, 1671, 0, 0, 1346, 1347, 1350, 1353, 1354, + 1355, 1356, 1357, 1358, 0, 1362, 1363, 1364, 1365, 1366, + 1367, 1368, 1369, 1373, 1374, 1375, 1376, 1377, 543, 518, + 0, 2062, 1057, 1058, 0, 940, 930, 938, 169, 173, + 0, 195, 192, 0, 201, 0, 0, 0, 0, 1463, + 0, 1769, 0, 1878, 1879, 1880, 1881, 1424, 1436, 1425, + 1436, 75, 77, 79, 97, 1401, 104, 0, 1219, 1220, + 1234, 0, 1535, 1587, 1559, 1569, 0, 1501, 830, 0, + 875, 0, 1534, 1522, 0, -2, 1530, 0, 0, 2064, + 2073, 2074, 1731, 1738, 0, 1740, 1742, 1743, 1750, 1752, + 1753, 0, 1765, 1766, 1767, 1775, 1344, 1344, 1344, 1344, + 1673, 1359, 1056, 0, 0, 939, 0, 923, 160, 0, + 0, 190, 191, 193, 0, 202, 0, 204, 205, 0, + 0, 1891, 1426, 1427, 106, 1221, 1511, 0, 1513, 0, + 0, 0, 1524, -2, 0, 1532, 0, 1732, 1741, 1754, + 0, 1755, 0, 0, 0, 1663, 1666, 1670, 1672, 2062, + 1059, 941, 1473, 0, 174, 0, 176, 178, 179, 1701, + 187, 188, 194, 203, 0, 0, 1206, 1222, 0, 0, + 1515, 0, 1572, 0, 1575, 1576, 874, 0, 0, 1531, + 2065, 1739, 1756, 1758, 1759, 0, 0, 1757, 0, 161, + 162, 0, 175, 0, 0, 1468, 1770, 1223, 1512, 1509, + 1571, 0, 0, 0, 0, 1760, 1762, 1761, 1060, 0, + 0, 177, 1702, 1573, 1574, 1577, 1578, 0, 0, 163, + 164, 165, 0, 0, 877, 1703, 0, 0, 876, } var yyTok1 = [...]int{ @@ -13156,14 +13228,14 @@ var yyTok1 = [...]int{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 122, 3, 3, 3, 155, 145, 3, 89, 90, 152, 150, 175, 151, 174, 153, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 764, 761, - 132, 131, 133, 3, 765, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 768, 765, + 132, 131, 133, 3, 769, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 157, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 762, 144, 763, 158, + 3, 3, 3, 766, 144, 767, 158, } var yyTok2 = [...]int{ @@ -13293,7 +13365,8 @@ var yyTok3 = [...]int{ 58070, 745, 58071, 746, 58072, 747, 58073, 748, 58074, 749, 58075, 750, 58076, 751, 58077, 752, 58078, 753, 58079, 754, 58080, 755, 58081, 756, 58082, 757, 58083, 758, 58084, 759, - 58085, 760, 0, + 58085, 760, 58086, 761, 58087, 762, 58088, 763, 58089, 764, + 0, } var yyErrorMessages = [...]struct { @@ -13643,7 +13716,7 @@ yydefault: case 3: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:994 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:994 { if yyDollar[1].statementUnion() != nil { yylex.(*Lexer).AppendStmt(yyDollar[1].statementUnion()) @@ -13651,7 +13724,7 @@ yydefault: } case 4: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:1000 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1000 { yylex.(*Lexer).AppendTopLevelSemicolon(yyDollar[2].pos) if yyDollar[3].statementUnion() != nil { @@ -13661,7 +13734,7 @@ yydefault: case 5: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1009 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1009 { yyLOCAL = tree.NewCompoundStmt(yyDollar[2].statementsUnion()) } @@ -13669,7 +13742,7 @@ yydefault: case 6: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.Statement -//line mysql_sql.y:1015 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1015 { yyLOCAL = []tree.Statement{yyDollar[1].statementUnion()} } @@ -13677,7 +13750,7 @@ yydefault: case 7: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.Statement -//line mysql_sql.y:1019 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1019 { yyLOCAL = append(yyDollar[1].statementsUnion(), yyDollar[3].statementUnion()) } @@ -13685,7 +13758,7 @@ yydefault: case 17: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1034 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1034 { yyLOCAL = yyDollar[1].statementUnion() } @@ -13693,7 +13766,7 @@ yydefault: case 18: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1038 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1038 { yyLOCAL = tree.Statement(nil) } @@ -13701,7 +13774,7 @@ yydefault: case 19: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1044 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1044 { yyLOCAL = yyDollar[1].statementUnion() } @@ -13709,7 +13782,7 @@ yydefault: case 20: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1048 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1048 { yyLOCAL = yyDollar[1].statementUnion() } @@ -13717,7 +13790,7 @@ yydefault: case 22: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1053 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1053 { yyLOCAL = yyDollar[1].statementUnion() } @@ -13725,7 +13798,7 @@ yydefault: case 23: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1057 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1057 { yyLOCAL = tree.Statement(nil) } @@ -13733,7 +13806,7 @@ yydefault: case 58: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1097 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1097 { yyLOCAL = yyDollar[1].selectUnion() } @@ -13741,7 +13814,7 @@ yydefault: case 69: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1113 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1113 { var timestamp = yyDollar[2].str var isS3 = false @@ -13756,7 +13829,7 @@ yydefault: case 70: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1124 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1124 { var timestamp = yyDollar[2].str var isS3 = true @@ -13770,32 +13843,32 @@ yydefault: yyVAL.union = yyLOCAL case 71: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:1136 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1136 { yyVAL.str = "" } case 72: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:1140 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1140 { yyVAL.str = yyDollar[2].str } case 73: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:1145 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1145 { yyVAL.str = "" } case 74: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:1149 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1149 { yyVAL.str = yyDollar[2].str } case 75: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1155 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1155 { yyLOCAL = &tree.CreateCDC{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -13812,7 +13885,7 @@ yydefault: case 76: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:1170 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1170 { yyLOCAL = yyDollar[1].strsUnion() } @@ -13820,7 +13893,7 @@ yydefault: case 77: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:1174 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1174 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].strsUnion()...) } @@ -13828,7 +13901,7 @@ yydefault: case 78: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:1178 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1178 { yyLOCAL = []string{} } @@ -13836,7 +13909,7 @@ yydefault: case 79: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:1182 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1182 { yyLOCAL = append(yyLOCAL, yyDollar[1].str) yyLOCAL = append(yyLOCAL, yyDollar[3].str) @@ -13845,7 +13918,7 @@ yydefault: case 80: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1189 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1189 { yyLOCAL = &tree.ShowCDC{ Option: yyDollar[3].allCDCOptionUnion(), @@ -13855,7 +13928,7 @@ yydefault: case 81: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1197 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1197 { yyLOCAL = &tree.PauseCDC{ Option: yyDollar[3].allCDCOptionUnion(), @@ -13865,7 +13938,7 @@ yydefault: case 82: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1205 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1205 { yyLOCAL = tree.NewDropCDC(yyDollar[4].allCDCOptionUnion(), yyDollar[3].boolValUnion(), yyDollar[5].boolValUnion()) } @@ -13873,7 +13946,7 @@ yydefault: case 83: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.AllOrNotCDC -//line mysql_sql.y:1210 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1210 { yyLOCAL = &tree.AllOrNotCDC{ All: true, @@ -13884,7 +13957,7 @@ yydefault: case 84: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.AllOrNotCDC -//line mysql_sql.y:1217 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1217 { yyLOCAL = yyDollar[1].allCDCOptionUnion() } @@ -13892,7 +13965,7 @@ yydefault: case 85: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.AllOrNotCDC -//line mysql_sql.y:1223 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1223 { yyLOCAL = yyDollar[1].allCDCOptionUnion() } @@ -13900,7 +13973,7 @@ yydefault: case 86: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.AllOrNotCDC -//line mysql_sql.y:1228 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1228 { yyLOCAL = &tree.AllOrNotCDC{ All: false, @@ -13911,7 +13984,7 @@ yydefault: case 87: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.AllOrNotCDC -//line mysql_sql.y:1237 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1237 { yyLOCAL = &tree.AllOrNotCDC{ All: true, @@ -13922,7 +13995,7 @@ yydefault: case 88: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.AllOrNotCDC -//line mysql_sql.y:1244 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1244 { yyLOCAL = &tree.AllOrNotCDC{ All: false, @@ -13933,7 +14006,7 @@ yydefault: case 89: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1253 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1253 { yyLOCAL = &tree.ResumeCDC{ TaskName: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -13943,7 +14016,7 @@ yydefault: case 90: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1261 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1261 { yyLOCAL = &tree.RestartCDC{ TaskName: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -13953,7 +14026,7 @@ yydefault: case 91: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1269 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1269 { yyLOCAL = &tree.CreateSnapShot{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -13965,7 +14038,7 @@ yydefault: case 92: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1279 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1279 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELCLUSTER, @@ -13979,7 +14052,7 @@ yydefault: case 93: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1289 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1289 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELACCOUNT, @@ -13993,7 +14066,7 @@ yydefault: case 94: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1299 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1299 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELACCOUNT, @@ -14007,7 +14080,7 @@ yydefault: case 95: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1309 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1309 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELDATABASE, @@ -14021,7 +14094,7 @@ yydefault: case 96: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1319 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1319 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELTABLE, @@ -14035,7 +14108,7 @@ yydefault: case 97: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1329 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1329 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELTABLE, @@ -14051,7 +14124,7 @@ yydefault: case 98: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1341 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1341 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELDATABASE, @@ -14067,7 +14140,7 @@ yydefault: case 99: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.ObjectInfo -//line mysql_sql.y:1353 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1353 { spLevel := tree.SnapshotLevelType{ Level: tree.SNAPSHOTLEVELACCOUNT, @@ -14082,7 +14155,7 @@ yydefault: case 100: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1366 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1366 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14097,7 +14170,7 @@ yydefault: case 101: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1377 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1377 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14112,7 +14185,7 @@ yydefault: case 102: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1388 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1388 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14128,7 +14201,7 @@ yydefault: case 103: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1400 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1400 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14144,7 +14217,7 @@ yydefault: case 104: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1412 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1412 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14161,7 +14234,7 @@ yydefault: case 105: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1425 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1425 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14176,7 +14249,7 @@ yydefault: case 106: yyDollar = yyS[yypt-13 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1436 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1436 { yyLOCAL = &tree.CreatePitr{ IfNotExists: yyDollar[3].ifNotExistsUnion(), @@ -14193,7 +14266,7 @@ yydefault: case 107: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:1451 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1451 { yyLOCAL = yyDollar[1].item.(int64) } @@ -14201,7 +14274,7 @@ yydefault: case 108: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1458 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1458 { var account tree.Identifier var database tree.Identifier @@ -14237,7 +14310,7 @@ yydefault: case 109: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1490 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1490 { var account tree.Identifier var database tree.Identifier @@ -14278,7 +14351,7 @@ yydefault: case 110: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1527 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1527 { yyLOCAL = &tree.RestoreSnapShot{ Level: tree.RESTORELEVELCLUSTER, @@ -14289,7 +14362,7 @@ yydefault: case 111: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1534 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1534 { result := &tree.RestoreSnapShot{ Level: tree.RESTORELEVELACCOUNT, @@ -14307,7 +14380,7 @@ yydefault: case 112: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:1550 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1550 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[1].cstrUnion().Compare())} } @@ -14315,7 +14388,7 @@ yydefault: case 113: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:1554 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1554 { yyLOCAL = tree.IdentifierList{ tree.Identifier(yyDollar[1].cstrUnion().Compare()), @@ -14326,7 +14399,7 @@ yydefault: case 114: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:1563 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1563 { yyLOCAL = tree.IdentifierList{ tree.Identifier(yyDollar[1].cstrUnion().Compare()), @@ -14337,7 +14410,7 @@ yydefault: case 115: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:1570 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1570 { yyLOCAL = tree.IdentifierList{ tree.Identifier(yyDollar[1].cstrUnion().Compare()), @@ -14348,32 +14421,32 @@ yydefault: yyVAL.union = yyLOCAL case 116: yyDollar = yyS[yypt-5 : yypt+1] -//line mysql_sql.y:1580 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1580 { yyVAL.str = yyDollar[4].cstrUnion().Compare() } case 117: yyDollar = yyS[yypt-5 : yypt+1] -//line mysql_sql.y:1584 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1584 { yyVAL.str = strings.ToLower(yyDollar[4].str) } case 118: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:1589 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1589 { yyVAL.str = "" } case 119: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:1593 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1593 { yyVAL.str = yyDollar[3].cstrUnion().Compare() } case 120: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1599 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1599 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELACCOUNT, @@ -14385,7 +14458,7 @@ yydefault: case 121: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1607 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1607 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELDATABASE, @@ -14398,7 +14471,7 @@ yydefault: case 122: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1616 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1616 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELDATABASE, @@ -14412,7 +14485,7 @@ yydefault: case 123: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1626 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1626 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELTABLE, @@ -14426,7 +14499,7 @@ yydefault: case 124: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1636 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1636 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELTABLE, @@ -14441,7 +14514,7 @@ yydefault: case 125: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1647 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1647 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELACCOUNT, @@ -14455,7 +14528,7 @@ yydefault: case 126: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1657 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1657 { yyLOCAL = &tree.RestorePitr{ Level: tree.RESTORELEVELCLUSTER, @@ -14467,7 +14540,7 @@ yydefault: case 127: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1667 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1667 { var connectionId uint64 switch v := yyDollar[3].item.(type) { @@ -14490,7 +14563,7 @@ yydefault: case 128: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.KillOption -//line mysql_sql.y:1687 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1687 { yyLOCAL = tree.KillOption{ Exist: false, @@ -14500,7 +14573,7 @@ yydefault: case 129: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.KillOption -//line mysql_sql.y:1693 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1693 { yyLOCAL = tree.KillOption{ Exist: true, @@ -14511,7 +14584,7 @@ yydefault: case 130: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.KillOption -//line mysql_sql.y:1700 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1700 { yyLOCAL = tree.KillOption{ Exist: true, @@ -14522,7 +14595,7 @@ yydefault: case 131: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.StatementOption -//line mysql_sql.y:1708 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1708 { yyLOCAL = tree.StatementOption{ Exist: false, @@ -14532,7 +14605,7 @@ yydefault: case 132: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.StatementOption -//line mysql_sql.y:1714 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1714 { yyLOCAL = tree.StatementOption{ Exist: true, @@ -14543,7 +14616,7 @@ yydefault: case 133: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1723 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1723 { yyLOCAL = &tree.CallStmt{ Name: yyDollar[2].procNameUnion(), @@ -14554,7 +14627,7 @@ yydefault: case 134: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1732 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1732 { yyLOCAL = &tree.LeaveStmt{ Name: tree.Identifier(yyDollar[2].cstrUnion().Compare()), @@ -14564,7 +14637,7 @@ yydefault: case 135: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1740 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1740 { yyLOCAL = &tree.IterateStmt{ Name: tree.Identifier(yyDollar[2].cstrUnion().Compare()), @@ -14574,7 +14647,7 @@ yydefault: case 136: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1748 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1748 { yyLOCAL = &tree.WhileStmt{ Name: "", @@ -14586,7 +14659,7 @@ yydefault: case 137: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1756 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1756 { yyLOCAL = &tree.WhileStmt{ Name: tree.Identifier(yyDollar[1].cstrUnion().Compare()), @@ -14598,7 +14671,7 @@ yydefault: case 138: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1766 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1766 { yyLOCAL = &tree.RepeatStmt{ Name: "", @@ -14610,7 +14683,7 @@ yydefault: case 139: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1774 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1774 { yyLOCAL = &tree.RepeatStmt{ Name: tree.Identifier(yyDollar[1].cstrUnion().Compare()), @@ -14622,7 +14695,7 @@ yydefault: case 140: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1784 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1784 { yyLOCAL = &tree.LoopStmt{ Name: "", @@ -14633,7 +14706,7 @@ yydefault: case 141: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1791 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1791 { yyLOCAL = &tree.LoopStmt{ Name: tree.Identifier(yyDollar[1].cstrUnion().Compare()), @@ -14644,7 +14717,7 @@ yydefault: case 142: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1800 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1800 { yyLOCAL = &tree.IfStmt{ Cond: yyDollar[2].exprUnion(), @@ -14657,7 +14730,7 @@ yydefault: case 143: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.ElseIfStmt -//line mysql_sql.y:1810 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1810 { yyLOCAL = nil } @@ -14665,7 +14738,7 @@ yydefault: case 144: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.ElseIfStmt -//line mysql_sql.y:1814 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1814 { yyLOCAL = yyDollar[1].elseIfClauseListUnion() } @@ -14673,7 +14746,7 @@ yydefault: case 145: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.ElseIfStmt -//line mysql_sql.y:1820 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1820 { yyLOCAL = []*tree.ElseIfStmt{yyDollar[1].elseIfClauseUnion()} } @@ -14681,7 +14754,7 @@ yydefault: case 146: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.ElseIfStmt -//line mysql_sql.y:1824 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1824 { yyLOCAL = append(yyDollar[1].elseIfClauseListUnion(), yyDollar[2].elseIfClauseUnion()) } @@ -14689,7 +14762,7 @@ yydefault: case 147: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.ElseIfStmt -//line mysql_sql.y:1830 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1830 { yyLOCAL = &tree.ElseIfStmt{ Cond: yyDollar[2].exprUnion(), @@ -14700,7 +14773,7 @@ yydefault: case 148: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1839 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1839 { yyLOCAL = &tree.CaseStmt{ Expr: yyDollar[2].exprUnion(), @@ -14712,7 +14785,7 @@ yydefault: case 149: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.WhenStmt -//line mysql_sql.y:1849 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1849 { yyLOCAL = []*tree.WhenStmt{yyDollar[1].whenClause2Union()} } @@ -14720,7 +14793,7 @@ yydefault: case 150: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.WhenStmt -//line mysql_sql.y:1853 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1853 { yyLOCAL = append(yyDollar[1].whenClauseList2Union(), yyDollar[2].whenClause2Union()) } @@ -14728,7 +14801,7 @@ yydefault: case 151: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.WhenStmt -//line mysql_sql.y:1859 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1859 { yyLOCAL = &tree.WhenStmt{ Cond: yyDollar[2].exprUnion(), @@ -14739,7 +14812,7 @@ yydefault: case 152: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []tree.Statement -//line mysql_sql.y:1868 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1868 { yyLOCAL = nil } @@ -14747,7 +14820,7 @@ yydefault: case 153: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []tree.Statement -//line mysql_sql.y:1872 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1872 { yyLOCAL = yyDollar[2].statementsUnion() } @@ -14755,7 +14828,7 @@ yydefault: case 154: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1878 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1878 { ep := &tree.ExportParam{ Outfile: true, @@ -14775,7 +14848,7 @@ yydefault: case 155: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1896 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1896 { yyLOCAL = &tree.Load{ Local: yyDollar[3].boolValUnion(), @@ -14791,7 +14864,7 @@ yydefault: case 156: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1910 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1910 { yyLOCAL = &tree.DumpTable{Table: yyDollar[3].tableNameUnion(), Path: yyDollar[5].str} } @@ -14799,7 +14872,7 @@ yydefault: case 157: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1914 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1914 { yyLOCAL = &tree.DumpTable{Table: yyDollar[3].tableNameUnion(), Path: yyDollar[5].str, MetadataOnly: true} } @@ -14807,7 +14880,7 @@ yydefault: case 158: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1920 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1920 { yyLOCAL = &tree.LoadTable{Table: yyDollar[3].tableNameUnion(), Path: yyDollar[5].str} } @@ -14815,7 +14888,7 @@ yydefault: case 159: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:1926 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1926 { yyLOCAL = &tree.LoadExtension{ Name: tree.Identifier(yyDollar[2].str), @@ -14825,7 +14898,7 @@ yydefault: case 160: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:1933 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1933 { yyLOCAL = nil } @@ -14833,7 +14906,7 @@ yydefault: case 161: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:1937 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1937 { yyLOCAL = yyDollar[2].updateExprsUnion() } @@ -14841,7 +14914,7 @@ yydefault: case 162: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:1943 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1943 { yyLOCAL = tree.UpdateExprs{yyDollar[1].updateExprUnion()} } @@ -14849,7 +14922,7 @@ yydefault: case 163: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:1947 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1947 { yyLOCAL = append(yyDollar[1].updateExprsUnion(), yyDollar[3].updateExprUnion()) } @@ -14857,7 +14930,7 @@ yydefault: case 164: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UpdateExpr -//line mysql_sql.y:1953 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1953 { yyLOCAL = &tree.UpdateExpr{ Names: []*tree.UnresolvedName{yyDollar[1].unresolvedNameUnion()}, @@ -14868,7 +14941,7 @@ yydefault: case 165: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UpdateExpr -//line mysql_sql.y:1960 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1960 { yyLOCAL = &tree.UpdateExpr{ Names: []*tree.UnresolvedName{yyDollar[1].unresolvedNameUnion()}, @@ -14879,7 +14952,7 @@ yydefault: case 166: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:1968 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1968 { yyLOCAL = false } @@ -14887,7 +14960,7 @@ yydefault: case 167: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:1972 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1972 { str := strings.ToLower(yyDollar[2].str) if str == "true" { @@ -14903,7 +14976,7 @@ yydefault: case 168: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:1984 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1984 { yyLOCAL = true } @@ -14911,7 +14984,7 @@ yydefault: case 169: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:1988 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:1988 { str := strings.ToLower(yyDollar[2].str) if str == "true" { @@ -14927,7 +15000,7 @@ yydefault: case 170: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:2002 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2002 { yyLOCAL = tree.NewUnresolvedName(yyDollar[1].cstrUnion()) } @@ -14935,7 +15008,7 @@ yydefault: case 171: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:2006 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2006 { tblNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedName(tblNameCStr, yyDollar[3].cstrUnion()) @@ -14944,7 +15017,7 @@ yydefault: case 172: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:2011 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2011 { dbNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) tblNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[3].cstrUnion().Origin()) @@ -14954,7 +15027,7 @@ yydefault: case 173: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []tree.LoadColumn -//line mysql_sql.y:2018 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2018 { yyLOCAL = nil } @@ -14962,7 +15035,7 @@ yydefault: case 174: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []tree.LoadColumn -//line mysql_sql.y:2022 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2022 { yyLOCAL = nil } @@ -14970,7 +15043,7 @@ yydefault: case 175: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.LoadColumn -//line mysql_sql.y:2026 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2026 { yyLOCAL = yyDollar[2].loadColumnsUnion() } @@ -14978,7 +15051,7 @@ yydefault: case 176: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.LoadColumn -//line mysql_sql.y:2032 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2032 { switch yyDollar[1].loadColumnUnion().(type) { case *tree.UnresolvedName: @@ -14991,7 +15064,7 @@ yydefault: case 177: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.LoadColumn -//line mysql_sql.y:2041 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2041 { switch yyDollar[3].loadColumnUnion().(type) { case *tree.UnresolvedName: @@ -15004,7 +15077,7 @@ yydefault: case 178: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.LoadColumn -//line mysql_sql.y:2052 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2052 { yyLOCAL = yyDollar[1].unresolvedNameUnion() } @@ -15012,7 +15085,7 @@ yydefault: case 179: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.LoadColumn -//line mysql_sql.y:2056 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2056 { yyLOCAL = yyDollar[1].varExprUnion() } @@ -15020,7 +15093,7 @@ yydefault: case 180: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.VarExpr -//line mysql_sql.y:2062 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2062 { yyLOCAL = []*tree.VarExpr{yyDollar[1].varExprUnion()} } @@ -15028,7 +15101,7 @@ yydefault: case 181: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.VarExpr -//line mysql_sql.y:2066 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2066 { yyLOCAL = append(yyDollar[1].varExprsUnion(), yyDollar[3].varExprUnion()) } @@ -15036,7 +15109,7 @@ yydefault: case 182: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.VarExpr -//line mysql_sql.y:2072 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2072 { yyLOCAL = yyDollar[1].varExprUnion() } @@ -15044,7 +15117,7 @@ yydefault: case 183: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.VarExpr -//line mysql_sql.y:2076 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2076 { yyLOCAL = yyDollar[1].varExprUnion() } @@ -15052,7 +15125,7 @@ yydefault: case 184: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.VarExpr -//line mysql_sql.y:2082 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2082 { v := strings.ToLower(yyDollar[1].str) var isGlobal bool @@ -15074,7 +15147,7 @@ yydefault: case 185: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.VarExpr -//line mysql_sql.y:2102 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2102 { // vs := strings.Split($1, ".") // var r string @@ -15096,7 +15169,7 @@ yydefault: case 186: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:2121 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2121 { yyLOCAL = 0 } @@ -15104,7 +15177,7 @@ yydefault: case 187: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:2125 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2125 { yyLOCAL = yyDollar[2].item.(int64) } @@ -15112,7 +15185,7 @@ yydefault: case 188: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:2129 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2129 { yyLOCAL = yyDollar[2].item.(int64) } @@ -15120,7 +15193,7 @@ yydefault: case 189: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Lines -//line mysql_sql.y:2134 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2134 { yyLOCAL = nil } @@ -15128,7 +15201,7 @@ yydefault: case 190: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Lines -//line mysql_sql.y:2138 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2138 { yyLOCAL = &tree.Lines{ StartingBy: yyDollar[2].str, @@ -15141,7 +15214,7 @@ yydefault: case 191: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Lines -//line mysql_sql.y:2147 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2147 { yyLOCAL = &tree.Lines{ StartingBy: yyDollar[3].str, @@ -15153,32 +15226,32 @@ yydefault: yyVAL.union = yyLOCAL case 192: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:2157 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2157 { yyVAL.str = "" } case 194: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:2164 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2164 { yyVAL.str = yyDollar[3].str } case 195: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:2169 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2169 { yyVAL.str = "\n" } case 197: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:2176 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2176 { yyVAL.str = yyDollar[3].str } case 198: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:2181 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2181 { yyLOCAL = nil } @@ -15186,7 +15259,7 @@ yydefault: case 199: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:2185 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2185 { res := &tree.Fields{ Terminated: &tree.Terminated{ @@ -15216,7 +15289,7 @@ yydefault: case 200: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.Fields -//line mysql_sql.y:2213 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2213 { yyLOCAL = []*tree.Fields{yyDollar[1].fieldsUnion()} } @@ -15224,7 +15297,7 @@ yydefault: case 201: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.Fields -//line mysql_sql.y:2217 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2217 { yyLOCAL = append(yyDollar[1].fieldsListUnion(), yyDollar[2].fieldsUnion()) } @@ -15232,7 +15305,7 @@ yydefault: case 202: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:2223 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2223 { yyLOCAL = &tree.Fields{ Terminated: &tree.Terminated{ @@ -15244,7 +15317,7 @@ yydefault: case 203: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:2231 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2231 { str := yyDollar[4].str if str != "\\" && len(str) > 1 { @@ -15268,7 +15341,7 @@ yydefault: case 204: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:2251 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2251 { str := yyDollar[3].str if str != "\\" && len(str) > 1 { @@ -15291,7 +15364,7 @@ yydefault: case 205: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:2270 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2270 { str := yyDollar[3].str if str != "\\" && len(str) > 1 { @@ -15314,7 +15387,7 @@ yydefault: case 207: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.DuplicateKey -//line mysql_sql.y:2295 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2295 { yyLOCAL = &tree.DuplicateKeyError{} } @@ -15322,7 +15395,7 @@ yydefault: case 208: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.DuplicateKey -//line mysql_sql.y:2299 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2299 { yyLOCAL = &tree.DuplicateKeyIgnore{} } @@ -15330,7 +15403,7 @@ yydefault: case 209: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.DuplicateKey -//line mysql_sql.y:2303 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2303 { yyLOCAL = &tree.DuplicateKeyReplace{} } @@ -15338,7 +15411,7 @@ yydefault: case 210: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:2308 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2308 { yyLOCAL = false } @@ -15346,7 +15419,7 @@ yydefault: case 211: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:2312 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2312 { yyLOCAL = true } @@ -15354,7 +15427,7 @@ yydefault: case 212: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2318 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2318 { yyLOCAL = &tree.Grant{ Typ: tree.GrantTypePrivilege, @@ -15371,7 +15444,7 @@ yydefault: case 213: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2331 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2331 { yyLOCAL = &tree.Grant{ Typ: tree.GrantTypeRole, @@ -15386,7 +15459,7 @@ yydefault: case 214: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2342 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2342 { yyLOCAL = &tree.Grant{ Typ: tree.GrantTypeProxy, @@ -15402,7 +15475,7 @@ yydefault: case 215: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:2355 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2355 { yyLOCAL = false } @@ -15410,7 +15483,7 @@ yydefault: case 216: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:2359 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2359 { yyLOCAL = true } @@ -15418,7 +15491,7 @@ yydefault: case 217: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2369 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2369 { yyLOCAL = &tree.Revoke{ Typ: tree.RevokeTypePrivilege, @@ -15435,7 +15508,7 @@ yydefault: case 218: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2382 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2382 { yyLOCAL = &tree.Revoke{ Typ: tree.RevokeTypeRole, @@ -15450,7 +15523,7 @@ yydefault: case 219: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.PrivilegeLevel -//line mysql_sql.y:2395 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2395 { yyLOCAL = &tree.PrivilegeLevel{ Level: tree.PRIVILEGE_LEVEL_TYPE_STAR, @@ -15460,7 +15533,7 @@ yydefault: case 220: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.PrivilegeLevel -//line mysql_sql.y:2401 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2401 { yyLOCAL = &tree.PrivilegeLevel{ Level: tree.PRIVILEGE_LEVEL_TYPE_STAR_STAR, @@ -15470,7 +15543,7 @@ yydefault: case 221: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.PrivilegeLevel -//line mysql_sql.y:2407 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2407 { tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) yyLOCAL = &tree.PrivilegeLevel{ @@ -15482,7 +15555,7 @@ yydefault: case 222: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.PrivilegeLevel -//line mysql_sql.y:2415 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2415 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[3].cstrUnion().Origin()) @@ -15496,7 +15569,7 @@ yydefault: case 223: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.PrivilegeLevel -//line mysql_sql.y:2425 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2425 { tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) yyLOCAL = &tree.PrivilegeLevel{ @@ -15508,7 +15581,7 @@ yydefault: case 224: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectType -//line mysql_sql.y:2435 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2435 { yyLOCAL = tree.OBJECT_TYPE_TABLE } @@ -15516,7 +15589,7 @@ yydefault: case 225: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectType -//line mysql_sql.y:2439 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2439 { yyLOCAL = tree.OBJECT_TYPE_DATABASE } @@ -15524,7 +15597,7 @@ yydefault: case 226: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectType -//line mysql_sql.y:2443 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2443 { yyLOCAL = tree.OBJECT_TYPE_FUNCTION } @@ -15532,7 +15605,7 @@ yydefault: case 227: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectType -//line mysql_sql.y:2447 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2447 { yyLOCAL = tree.OBJECT_TYPE_PROCEDURE } @@ -15540,7 +15613,7 @@ yydefault: case 228: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectType -//line mysql_sql.y:2451 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2451 { yyLOCAL = tree.OBJECT_TYPE_VIEW } @@ -15548,7 +15621,7 @@ yydefault: case 229: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ObjectType -//line mysql_sql.y:2455 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2455 { yyLOCAL = tree.OBJECT_TYPE_ACCOUNT } @@ -15556,7 +15629,7 @@ yydefault: case 230: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.Privilege -//line mysql_sql.y:2461 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2461 { yyLOCAL = []*tree.Privilege{yyDollar[1].privilegeUnion()} } @@ -15564,7 +15637,7 @@ yydefault: case 231: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.Privilege -//line mysql_sql.y:2465 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2465 { yyLOCAL = append(yyDollar[1].privilegesUnion(), yyDollar[3].privilegeUnion()) } @@ -15572,7 +15645,7 @@ yydefault: case 232: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Privilege -//line mysql_sql.y:2471 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2471 { yyLOCAL = &tree.Privilege{ Type: yyDollar[1].privilegeTypeUnion(), @@ -15583,7 +15656,7 @@ yydefault: case 233: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Privilege -//line mysql_sql.y:2478 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2478 { yyLOCAL = &tree.Privilege{ Type: yyDollar[1].privilegeTypeUnion(), @@ -15594,7 +15667,7 @@ yydefault: case 234: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.UnresolvedName -//line mysql_sql.y:2487 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2487 { yyLOCAL = []*tree.UnresolvedName{yyDollar[1].unresolvedNameUnion()} } @@ -15602,7 +15675,7 @@ yydefault: case 235: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.UnresolvedName -//line mysql_sql.y:2491 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2491 { yyLOCAL = append(yyDollar[1].unresolveNamesUnion(), yyDollar[3].unresolvedNameUnion()) } @@ -15610,7 +15683,7 @@ yydefault: case 236: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2497 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2497 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALL } @@ -15618,7 +15691,7 @@ yydefault: case 237: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2501 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2501 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_ACCOUNT } @@ -15626,7 +15699,7 @@ yydefault: case 238: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2505 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2505 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DROP_ACCOUNT } @@ -15634,7 +15707,7 @@ yydefault: case 239: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2509 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2509 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALTER_ACCOUNT } @@ -15642,7 +15715,7 @@ yydefault: case 240: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2513 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2513 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_UPGRADE_ACCOUNT } @@ -15650,7 +15723,7 @@ yydefault: case 241: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2517 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2517 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALL } @@ -15658,7 +15731,7 @@ yydefault: case 242: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2521 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2521 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALTER_TABLE } @@ -15666,7 +15739,7 @@ yydefault: case 243: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2525 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2525 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALTER_VIEW } @@ -15674,7 +15747,7 @@ yydefault: case 244: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2529 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2529 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE } @@ -15682,7 +15755,7 @@ yydefault: case 245: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2533 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2533 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_USER } @@ -15690,7 +15763,7 @@ yydefault: case 246: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2537 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2537 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DROP_USER } @@ -15698,7 +15771,7 @@ yydefault: case 247: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2541 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2541 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALTER_USER } @@ -15706,7 +15779,7 @@ yydefault: case 248: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2545 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2545 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_TABLESPACE } @@ -15714,7 +15787,7 @@ yydefault: case 249: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2549 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2549 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_TRIGGER } @@ -15722,7 +15795,7 @@ yydefault: case 250: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2553 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2553 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DELETE } @@ -15730,7 +15803,7 @@ yydefault: case 251: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2557 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2557 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DROP_TABLE } @@ -15738,7 +15811,7 @@ yydefault: case 252: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2561 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2561 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DROP_VIEW } @@ -15746,7 +15819,7 @@ yydefault: case 253: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2565 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2565 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_EXECUTE } @@ -15754,7 +15827,7 @@ yydefault: case 254: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2569 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2569 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_INDEX } @@ -15762,7 +15835,7 @@ yydefault: case 255: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2573 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2573 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_INSERT } @@ -15770,7 +15843,7 @@ yydefault: case 256: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2577 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2577 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_SELECT } @@ -15778,7 +15851,7 @@ yydefault: case 257: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2581 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2581 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_SUPER } @@ -15786,7 +15859,7 @@ yydefault: case 258: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2585 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2585 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_DATABASE } @@ -15794,7 +15867,7 @@ yydefault: case 259: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2589 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2589 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DROP_DATABASE } @@ -15802,7 +15875,7 @@ yydefault: case 260: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2593 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2593 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_SHOW_DATABASES } @@ -15810,7 +15883,7 @@ yydefault: case 261: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2597 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2597 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CONNECT } @@ -15818,7 +15891,7 @@ yydefault: case 262: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2601 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2601 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_MANAGE_GRANTS } @@ -15826,7 +15899,7 @@ yydefault: case 263: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2605 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2605 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_OWNERSHIP } @@ -15834,7 +15907,7 @@ yydefault: case 264: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2609 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2609 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_SHOW_TABLES } @@ -15842,7 +15915,7 @@ yydefault: case 265: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2613 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2613 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_TABLE } @@ -15850,7 +15923,7 @@ yydefault: case 266: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2617 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2617 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_UPDATE } @@ -15858,7 +15931,7 @@ yydefault: case 267: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2621 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2621 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_GRANT_OPTION } @@ -15866,7 +15939,7 @@ yydefault: case 268: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2625 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2625 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_REFERENCES } @@ -15874,7 +15947,7 @@ yydefault: case 269: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2629 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2629 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_REFERENCE } @@ -15882,7 +15955,7 @@ yydefault: case 270: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2633 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2633 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_REPLICATION_SLAVE } @@ -15890,7 +15963,7 @@ yydefault: case 271: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2637 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2637 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_REPLICATION_CLIENT } @@ -15898,7 +15971,7 @@ yydefault: case 272: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2641 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2641 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_USAGE } @@ -15906,7 +15979,7 @@ yydefault: case 273: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2645 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2645 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_RELOAD } @@ -15914,7 +15987,7 @@ yydefault: case 274: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2649 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2649 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_FILE } @@ -15922,7 +15995,7 @@ yydefault: case 275: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2653 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2653 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_TEMPORARY_TABLES } @@ -15930,7 +16003,7 @@ yydefault: case 276: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2657 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2657 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_LOCK_TABLES } @@ -15938,7 +16011,7 @@ yydefault: case 277: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2661 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2661 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_VIEW } @@ -15946,7 +16019,7 @@ yydefault: case 278: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2665 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2665 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_SHOW_VIEW } @@ -15954,7 +16027,7 @@ yydefault: case 279: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2669 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2669 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_ROLE } @@ -15962,7 +16035,7 @@ yydefault: case 280: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2673 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2673 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_DROP_ROLE } @@ -15970,7 +16043,7 @@ yydefault: case 281: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2677 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2677 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALTER_ROLE } @@ -15978,7 +16051,7 @@ yydefault: case 282: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2681 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2681 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_CREATE_ROUTINE } @@ -15986,7 +16059,7 @@ yydefault: case 283: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2685 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2685 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_ALTER_ROUTINE } @@ -15994,7 +16067,7 @@ yydefault: case 284: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2689 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2689 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_EVENT } @@ -16002,7 +16075,7 @@ yydefault: case 285: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2693 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2693 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_SHUTDOWN } @@ -16010,7 +16083,7 @@ yydefault: case 286: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.PrivilegeType -//line mysql_sql.y:2697 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2697 { yyLOCAL = tree.PRIVILEGE_TYPE_STATIC_TRUNCATE } @@ -16018,7 +16091,7 @@ yydefault: case 294: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2712 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2712 { yyLOCAL = &tree.SetLogserviceSettings{ Name: yyDollar[4].str, @@ -16029,7 +16102,7 @@ yydefault: case 295: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2721 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2721 { yyLOCAL = &tree.SetTransaction{ Global: false, @@ -16040,7 +16113,7 @@ yydefault: case 296: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2728 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2728 { yyLOCAL = &tree.SetTransaction{ Global: true, @@ -16051,7 +16124,7 @@ yydefault: case 297: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2735 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2735 { yyLOCAL = &tree.SetTransaction{ Global: false, @@ -16062,7 +16135,7 @@ yydefault: case 298: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2744 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2744 { var connID uint32 switch v := yyDollar[5].item.(type) { @@ -16082,7 +16155,7 @@ yydefault: case 299: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.TransactionCharacteristic -//line mysql_sql.y:2762 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2762 { yyLOCAL = []*tree.TransactionCharacteristic{yyDollar[1].transactionCharacteristicUnion()} } @@ -16090,7 +16163,7 @@ yydefault: case 300: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.TransactionCharacteristic -//line mysql_sql.y:2766 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2766 { yyLOCAL = append(yyDollar[1].transactionCharacteristicListUnion(), yyDollar[3].transactionCharacteristicUnion()) } @@ -16098,7 +16171,7 @@ yydefault: case 301: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.TransactionCharacteristic -//line mysql_sql.y:2772 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2772 { yyLOCAL = &tree.TransactionCharacteristic{ IsLevel: true, @@ -16109,7 +16182,7 @@ yydefault: case 302: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.TransactionCharacteristic -//line mysql_sql.y:2779 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2779 { yyLOCAL = &tree.TransactionCharacteristic{ Access: yyDollar[1].accessModeUnion(), @@ -16119,7 +16192,7 @@ yydefault: case 303: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IsolationLevelType -//line mysql_sql.y:2787 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2787 { yyLOCAL = tree.ISOLATION_LEVEL_REPEATABLE_READ } @@ -16127,7 +16200,7 @@ yydefault: case 304: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IsolationLevelType -//line mysql_sql.y:2791 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2791 { yyLOCAL = tree.ISOLATION_LEVEL_READ_COMMITTED } @@ -16135,7 +16208,7 @@ yydefault: case 305: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IsolationLevelType -//line mysql_sql.y:2795 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2795 { yyLOCAL = tree.ISOLATION_LEVEL_READ_UNCOMMITTED } @@ -16143,7 +16216,7 @@ yydefault: case 306: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IsolationLevelType -//line mysql_sql.y:2799 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2799 { yyLOCAL = tree.ISOLATION_LEVEL_SERIALIZABLE } @@ -16151,7 +16224,7 @@ yydefault: case 307: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AccessModeType -//line mysql_sql.y:2805 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2805 { yyLOCAL = tree.ACCESS_MODE_READ_WRITE } @@ -16159,7 +16232,7 @@ yydefault: case 308: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AccessModeType -//line mysql_sql.y:2809 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2809 { yyLOCAL = tree.ACCESS_MODE_READ_ONLY } @@ -16167,7 +16240,7 @@ yydefault: case 309: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2815 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2815 { yyLOCAL = &tree.SetRole{ SecondaryRole: false, @@ -16178,7 +16251,7 @@ yydefault: case 310: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2822 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2822 { yyLOCAL = &tree.SetRole{ SecondaryRole: true, @@ -16189,7 +16262,7 @@ yydefault: case 311: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2829 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2829 { yyLOCAL = &tree.SetRole{ SecondaryRole: true, @@ -16200,7 +16273,7 @@ yydefault: case 312: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2838 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2838 { dr := yyDollar[4].setDefaultRoleUnion() dr.Users = yyDollar[6].usersUnion() @@ -16210,7 +16283,7 @@ yydefault: case 313: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.SetDefaultRole -//line mysql_sql.y:2868 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2868 { yyLOCAL = &tree.SetDefaultRole{Type: tree.SET_DEFAULT_ROLE_TYPE_NONE, Roles: nil} } @@ -16218,7 +16291,7 @@ yydefault: case 314: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.SetDefaultRole -//line mysql_sql.y:2872 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2872 { yyLOCAL = &tree.SetDefaultRole{Type: tree.SET_DEFAULT_ROLE_TYPE_ALL, Roles: nil} } @@ -16226,7 +16299,7 @@ yydefault: case 315: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.SetDefaultRole -//line mysql_sql.y:2876 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2876 { yyLOCAL = &tree.SetDefaultRole{Type: tree.SET_DEFAULT_ROLE_TYPE_NORMAL, Roles: yyDollar[1].rolesUnion()} } @@ -16234,7 +16307,7 @@ yydefault: case 316: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2882 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2882 { yyLOCAL = &tree.SetVar{Assignments: yyDollar[2].varAssignmentExprsUnion()} } @@ -16242,7 +16315,7 @@ yydefault: case 317: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2888 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2888 { yyLOCAL = &tree.SetPassword{Password: yyDollar[4].str} } @@ -16250,21 +16323,21 @@ yydefault: case 318: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:2892 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2892 { yyLOCAL = &tree.SetPassword{User: yyDollar[4].userUnion(), Password: yyDollar[6].str} } yyVAL.union = yyLOCAL case 320: yyDollar = yyS[yypt-4 : yypt+1] -//line mysql_sql.y:2899 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2899 { yyVAL.str = yyDollar[3].str } case 321: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.VarAssignmentExpr -//line mysql_sql.y:2905 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2905 { yyLOCAL = []*tree.VarAssignmentExpr{yyDollar[1].varAssignmentExprUnion()} } @@ -16272,7 +16345,7 @@ yydefault: case 322: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.VarAssignmentExpr -//line mysql_sql.y:2909 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2909 { yyLOCAL = append(yyDollar[1].varAssignmentExprsUnion(), yyDollar[3].varAssignmentExprUnion()) } @@ -16280,7 +16353,7 @@ yydefault: case 323: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:2915 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2915 { yyLOCAL = &tree.VarAssignmentExpr{ System: true, @@ -16292,7 +16365,7 @@ yydefault: case 324: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:2923 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2923 { yyLOCAL = &tree.VarAssignmentExpr{ System: true, @@ -16305,7 +16378,7 @@ yydefault: case 325: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:2932 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2932 { yyLOCAL = &tree.VarAssignmentExpr{ System: true, @@ -16318,7 +16391,7 @@ yydefault: case 326: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:2941 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2941 { yyLOCAL = &tree.VarAssignmentExpr{ System: true, @@ -16330,7 +16403,7 @@ yydefault: case 327: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:2949 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2949 { yyLOCAL = &tree.VarAssignmentExpr{ System: true, @@ -16342,7 +16415,7 @@ yydefault: case 328: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:2957 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2957 { vs := strings.Split(yyDollar[1].str, ".") var isGlobal bool @@ -16369,7 +16442,7 @@ yydefault: case 329: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:2980 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2980 { v := strings.ToLower(yyDollar[1].str) var isGlobal bool @@ -16392,7 +16465,7 @@ yydefault: case 330: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:2999 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:2999 { yyLOCAL = &tree.VarAssignmentExpr{ Name: strings.ToLower(yyDollar[1].str), @@ -16403,7 +16476,7 @@ yydefault: case 331: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3006 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3006 { yyLOCAL = &tree.VarAssignmentExpr{ Name: strings.ToLower(yyDollar[1].str), @@ -16414,7 +16487,7 @@ yydefault: case 332: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3013 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3013 { yyLOCAL = &tree.VarAssignmentExpr{ Name: strings.ToLower(yyDollar[1].str), @@ -16426,7 +16499,7 @@ yydefault: case 333: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3021 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3021 { yyLOCAL = &tree.VarAssignmentExpr{ Name: strings.ToLower(yyDollar[1].str), @@ -16437,7 +16510,7 @@ yydefault: case 334: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3028 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3028 { yyLOCAL = &tree.VarAssignmentExpr{ Name: strings.ToLower(yyDollar[1].str), @@ -16448,7 +16521,7 @@ yydefault: case 335: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.VarAssignmentExpr -//line mysql_sql.y:3035 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3035 { yyLOCAL = &tree.VarAssignmentExpr{ Name: strings.ToLower(yyDollar[1].str), @@ -16459,7 +16532,7 @@ yydefault: case 336: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:3044 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3044 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_char) } @@ -16467,7 +16540,7 @@ yydefault: case 337: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:3048 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3048 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_char) } @@ -16475,39 +16548,39 @@ yydefault: case 338: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:3052 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3052 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL case 339: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3058 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3058 { yyVAL.str = string(yyDollar[1].str) } case 340: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3062 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3062 { yyVAL.str = yyDollar[1].str } case 341: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3068 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3068 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } case 342: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:3072 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3072 { yyVAL.str = yyDollar[1].cstrUnion().Compare() + "." + yyDollar[3].cstrUnion().Compare() } case 343: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:3078 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3078 { yyLOCAL = []string{yyDollar[1].str} } @@ -16515,7 +16588,7 @@ yydefault: case 344: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:3082 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3082 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].str) } @@ -16523,7 +16596,7 @@ yydefault: case 351: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3096 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3096 { yyLOCAL = &tree.SavePoint{Name: tree.Identifier(yyDollar[2].cstrUnion().Compare())} } @@ -16531,7 +16604,7 @@ yydefault: case 352: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3102 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3102 { yyLOCAL = &tree.ReleaseSavePoint{Name: tree.Identifier(yyDollar[3].cstrUnion().Compare())} } @@ -16539,7 +16612,7 @@ yydefault: case 353: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3108 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3108 { yyLOCAL = &tree.RollbackToSavePoint{Name: tree.Identifier(yyDollar[3].cstrUnion().Compare())} } @@ -16547,7 +16620,7 @@ yydefault: case 354: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3113 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3113 { yyLOCAL = &tree.RollbackToSavePoint{Name: tree.Identifier(yyDollar[4].cstrUnion().Compare())} } @@ -16555,7 +16628,7 @@ yydefault: case 355: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3118 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3118 { yyLOCAL = &tree.RollbackToSavePoint{Name: tree.Identifier(yyDollar[5].cstrUnion().Compare())} } @@ -16563,7 +16636,7 @@ yydefault: case 356: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3123 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3123 { yyLOCAL = &tree.RollbackToSavePoint{Name: tree.Identifier(yyDollar[4].cstrUnion().Compare())} } @@ -16571,7 +16644,7 @@ yydefault: case 357: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3129 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3129 { yyLOCAL = &tree.RollbackTransaction{Type: yyDollar[2].completionTypeUnion()} } @@ -16579,7 +16652,7 @@ yydefault: case 358: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3135 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3135 { yyLOCAL = &tree.CommitTransaction{Type: yyDollar[2].completionTypeUnion()} } @@ -16587,7 +16660,7 @@ yydefault: case 359: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3140 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3140 { yyLOCAL = tree.COMPLETION_TYPE_NO_CHAIN } @@ -16595,7 +16668,7 @@ yydefault: case 360: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3144 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3144 { yyLOCAL = tree.COMPLETION_TYPE_NO_CHAIN } @@ -16603,7 +16676,7 @@ yydefault: case 361: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3148 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3148 { yyLOCAL = tree.COMPLETION_TYPE_CHAIN } @@ -16611,7 +16684,7 @@ yydefault: case 362: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3152 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3152 { yyLOCAL = tree.COMPLETION_TYPE_CHAIN } @@ -16619,7 +16692,7 @@ yydefault: case 363: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3156 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3156 { yyLOCAL = tree.COMPLETION_TYPE_RELEASE } @@ -16627,7 +16700,7 @@ yydefault: case 364: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3160 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3160 { yyLOCAL = tree.COMPLETION_TYPE_RELEASE } @@ -16635,7 +16708,7 @@ yydefault: case 365: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3164 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3164 { yyLOCAL = tree.COMPLETION_TYPE_NO_CHAIN } @@ -16643,7 +16716,7 @@ yydefault: case 366: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3168 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3168 { yyLOCAL = tree.COMPLETION_TYPE_NO_CHAIN } @@ -16651,7 +16724,7 @@ yydefault: case 367: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.CompletionType -//line mysql_sql.y:3172 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3172 { yyLOCAL = tree.COMPLETION_TYPE_NO_CHAIN } @@ -16659,7 +16732,7 @@ yydefault: case 368: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3178 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3178 { yyLOCAL = &tree.BeginTransaction{} } @@ -16667,7 +16740,7 @@ yydefault: case 369: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3182 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3182 { yyLOCAL = &tree.BeginTransaction{} } @@ -16675,7 +16748,7 @@ yydefault: case 370: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3186 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3186 { yyLOCAL = &tree.BeginTransaction{} } @@ -16683,7 +16756,7 @@ yydefault: case 371: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3190 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3190 { m := tree.MakeTransactionModes(tree.READ_WRITE_MODE_READ_WRITE) yyLOCAL = &tree.BeginTransaction{Modes: m} @@ -16692,7 +16765,7 @@ yydefault: case 372: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3195 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3195 { m := tree.MakeTransactionModes(tree.READ_WRITE_MODE_READ_ONLY) yyLOCAL = &tree.BeginTransaction{Modes: m} @@ -16701,7 +16774,7 @@ yydefault: case 373: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3200 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3200 { yyLOCAL = &tree.BeginTransaction{} } @@ -16709,7 +16782,7 @@ yydefault: case 374: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3206 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3206 { name := yyDollar[2].cstrUnion() secondaryRole := false @@ -16726,7 +16799,7 @@ yydefault: case 375: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3219 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3219 { name := yylex.(*Lexer).GetDbOrTblNameCStr("") secondaryRole := false @@ -16743,7 +16816,7 @@ yydefault: case 376: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3232 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3232 { name := yylex.(*Lexer).GetDbOrTblNameCStr("") secondaryRole := false @@ -16760,7 +16833,7 @@ yydefault: case 377: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3245 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3245 { name := yylex.(*Lexer).GetDbOrTblNameCStr("") secondaryRole := true @@ -16777,7 +16850,7 @@ yydefault: case 378: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3258 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3258 { name := yylex.(*Lexer).GetDbOrTblNameCStr("") secondaryRole := true @@ -16794,7 +16867,7 @@ yydefault: case 380: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3274 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3274 { yyDollar[2].statementUnion().(*tree.Update).With = yyDollar[1].withClauseUnion() yyLOCAL = yyDollar[2].statementUnion() @@ -16803,7 +16876,7 @@ yydefault: case 381: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3281 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3281 { // Single-table syntax yyLOCAL = &tree.Update{ @@ -16821,7 +16894,7 @@ yydefault: case 382: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3295 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3295 { // Multiple-table syntax yyLOCAL = &tree.Update{ @@ -16838,7 +16911,7 @@ yydefault: case 383: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3308 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3308 { // PostgreSQL-style UPDATE target SET ... FROM source_tables WHERE ... // The target table is kept in Tables; FROM-clause sources are stored @@ -16858,7 +16931,7 @@ yydefault: case 384: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:3326 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3326 { yyLOCAL = tree.UpdateExprs{yyDollar[1].updateExprUnion()} } @@ -16866,7 +16939,7 @@ yydefault: case 385: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:3330 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3330 { yyLOCAL = append(yyDollar[1].updateExprsUnion(), yyDollar[3].updateExprUnion()) } @@ -16874,7 +16947,7 @@ yydefault: case 386: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UpdateExpr -//line mysql_sql.y:3336 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3336 { yyLOCAL = &tree.UpdateExpr{Names: []*tree.UnresolvedName{yyDollar[1].unresolvedNameUnion()}, Expr: yyDollar[3].exprUnion()} } @@ -16882,7 +16955,7 @@ yydefault: case 389: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3346 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3346 { yyLOCAL = &tree.LockTableStmt{TableLocks: yyDollar[3].tableLocksUnion()} } @@ -16890,7 +16963,7 @@ yydefault: case 390: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.TableLock -//line mysql_sql.y:3352 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3352 { yyLOCAL = []tree.TableLock{yyDollar[1].tableLockUnion()} } @@ -16898,7 +16971,7 @@ yydefault: case 391: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.TableLock -//line mysql_sql.y:3356 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3356 { yyLOCAL = append(yyDollar[1].tableLocksUnion(), yyDollar[3].tableLockUnion()) } @@ -16906,7 +16979,7 @@ yydefault: case 392: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableLock -//line mysql_sql.y:3362 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3362 { yyLOCAL = tree.TableLock{Table: *yyDollar[1].tableNameUnion(), LockType: yyDollar[2].tableLockTypeUnion()} } @@ -16914,7 +16987,7 @@ yydefault: case 393: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableLockType -//line mysql_sql.y:3368 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3368 { yyLOCAL = tree.TableLockRead } @@ -16922,7 +16995,7 @@ yydefault: case 394: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableLockType -//line mysql_sql.y:3372 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3372 { yyLOCAL = tree.TableLockReadLocal } @@ -16930,7 +17003,7 @@ yydefault: case 395: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableLockType -//line mysql_sql.y:3376 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3376 { yyLOCAL = tree.TableLockWrite } @@ -16938,7 +17011,7 @@ yydefault: case 396: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableLockType -//line mysql_sql.y:3380 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3380 { yyLOCAL = tree.TableLockLowPriorityWrite } @@ -16946,7 +17019,7 @@ yydefault: case 397: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3386 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3386 { yyLOCAL = &tree.UnLockTableStmt{} } @@ -16954,7 +17027,7 @@ yydefault: case 407: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3401 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3401 { yyLOCAL = yyDollar[1].selectUnion() } @@ -16962,7 +17035,7 @@ yydefault: case 408: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3407 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3407 { yyLOCAL = tree.NewPrepareStmt(tree.Identifier(yyDollar[2].str), yyDollar[4].statementUnion()) } @@ -16970,7 +17043,7 @@ yydefault: case 409: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3411 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3411 { yyLOCAL = tree.NewPrepareString(tree.Identifier(yyDollar[2].str), yyDollar[4].str) } @@ -16978,7 +17051,7 @@ yydefault: case 410: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3415 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3415 { yyLOCAL = tree.NewPrepareVar(tree.Identifier(yyDollar[2].str), yyDollar[4].varExprUnion()) } @@ -16986,7 +17059,7 @@ yydefault: case 411: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3421 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3421 { yyLOCAL = &tree.ExecuteSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -16996,7 +17069,7 @@ yydefault: case 412: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3427 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3427 { yyLOCAL = tree.NewExecute(tree.Identifier(yyDollar[2].str)) } @@ -17004,7 +17077,7 @@ yydefault: case 413: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3431 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3431 { yyLOCAL = tree.NewExecuteWithVariables(tree.Identifier(yyDollar[2].str), yyDollar[4].varExprsUnion()) } @@ -17012,7 +17085,7 @@ yydefault: case 414: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3437 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3437 { yyLOCAL = tree.NewDeallocate(tree.Identifier(yyDollar[3].str), false) } @@ -17020,7 +17093,7 @@ yydefault: case 415: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3443 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3443 { yyLOCAL = tree.NewReset(tree.Identifier(yyDollar[3].str)) } @@ -17028,7 +17101,7 @@ yydefault: case 421: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3454 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3454 { yyLOCAL = yyDollar[1].selectUnion() } @@ -17036,7 +17109,7 @@ yydefault: case 422: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3460 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3460 { yyLOCAL = &tree.ShowColumns{Table: yyDollar[2].unresolvedObjectNameUnion()} } @@ -17044,7 +17117,7 @@ yydefault: case 423: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3464 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3464 { yyLOCAL = &tree.ShowColumns{Table: yyDollar[2].unresolvedObjectNameUnion(), ColName: yyDollar[3].unresolvedNameUnion()} } @@ -17052,7 +17125,7 @@ yydefault: case 424: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3468 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3468 { yyLOCAL = tree.NewExplainFor("", uint64(yyDollar[4].item.(int64))) } @@ -17060,7 +17133,7 @@ yydefault: case 425: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3472 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3472 { yyLOCAL = tree.NewExplainFor(yyDollar[4].str, uint64(yyDollar[7].item.(int64))) } @@ -17068,7 +17141,7 @@ yydefault: case 426: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3476 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3476 { yyLOCAL = tree.NewExplainStmt(yyDollar[2].statementUnion(), "text") } @@ -17076,7 +17149,7 @@ yydefault: case 427: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3480 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3480 { options := []tree.OptionElem{ tree.MakeOptionElem(tree.VerboseOption, "NULL"), @@ -17087,7 +17160,7 @@ yydefault: case 428: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3487 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3487 { options := []tree.OptionElem{ tree.MakeOptionElem(tree.AnalyzeOption, "NULL"), @@ -17098,7 +17171,7 @@ yydefault: case 429: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3494 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3494 { options := []tree.OptionElem{ tree.MakeOptionElem(tree.AnalyzeOption, "NULL"), @@ -17110,7 +17183,7 @@ yydefault: case 430: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3502 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3502 { options := []tree.OptionElem{ tree.MakeOptionElem(tree.PhyPlanOption, "NULL"), @@ -17121,7 +17194,7 @@ yydefault: case 431: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3509 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3509 { options := []tree.OptionElem{ tree.MakeOptionElem(tree.PhyPlanOption, "NULL"), @@ -17133,7 +17206,7 @@ yydefault: case 432: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3517 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3517 { options := []tree.OptionElem{ tree.MakeOptionElem(tree.PhyPlanOption, "NULL"), @@ -17145,7 +17218,7 @@ yydefault: case 433: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3525 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3525 { yyLOCAL = tree.MakeExplainStmt(yyDollar[5].statementUnion(), yyDollar[3].explainOptionsUnion()) } @@ -17153,7 +17226,7 @@ yydefault: case 434: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3529 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3529 { yyLOCAL = tree.MakeExplainStmt(yyDollar[3].statementUnion(), nil) } @@ -17161,7 +17234,7 @@ yydefault: case 435: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3533 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3533 { options := []tree.OptionElem{ tree.MakeOptionElem(tree.VerboseOption, "NULL"), @@ -17172,7 +17245,7 @@ yydefault: case 436: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3540 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3540 { options := []tree.OptionElem{ tree.MakeOptionElem(tree.AnalyzeOption, "NULL"), @@ -17183,7 +17256,7 @@ yydefault: case 437: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3547 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3547 { options := []tree.OptionElem{ tree.MakeOptionElem(tree.AnalyzeOption, "NULL"), @@ -17195,7 +17268,7 @@ yydefault: case 452: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.OptionElem -//line mysql_sql.y:3585 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3585 { yyLOCAL = []tree.OptionElem{yyDollar[1].explainOptionUnion()} } @@ -17203,7 +17276,7 @@ yydefault: case 453: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.OptionElem -//line mysql_sql.y:3589 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3589 { yyLOCAL = append(yyDollar[1].explainOptionsUnion(), yyDollar[3].explainOptionUnion()) } @@ -17211,45 +17284,45 @@ yydefault: case 454: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.OptionElem -//line mysql_sql.y:3595 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3595 { yyLOCAL = tree.MakeOptionElem(yyDollar[1].str, yyDollar[2].str) } yyVAL.union = yyLOCAL case 455: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3601 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3601 { yyVAL.str = yyDollar[1].str } case 456: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3606 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3606 { yyVAL.str = "true" } case 457: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3607 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3607 { yyVAL.str = "false" } case 458: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3608 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3608 { yyVAL.str = yyDollar[1].str } case 459: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:3609 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3609 { yyVAL.str = yyDollar[1].str } case 460: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3613 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3613 { yyLOCAL = tree.NewAnalyzeStmt(yyDollar[3].analyzeTableEntriesUnion()) } @@ -17257,7 +17330,7 @@ yydefault: case 461: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.AnalyzeTableEntry -//line mysql_sql.y:3619 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3619 { yyLOCAL = []*tree.AnalyzeTableEntry{yyDollar[1].analyzeTableEntryUnion()} } @@ -17265,7 +17338,7 @@ yydefault: case 462: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.AnalyzeTableEntry -//line mysql_sql.y:3623 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3623 { yyLOCAL = append(yyDollar[1].analyzeTableEntriesUnion(), yyDollar[3].analyzeTableEntryUnion()) } @@ -17273,7 +17346,7 @@ yydefault: case 463: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.AnalyzeTableEntry -//line mysql_sql.y:3629 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3629 { yyLOCAL = &tree.AnalyzeTableEntry{Table: yyDollar[1].tableNameUnion()} } @@ -17281,7 +17354,7 @@ yydefault: case 464: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.AnalyzeTableEntry -//line mysql_sql.y:3633 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3633 { yyLOCAL = &tree.AnalyzeTableEntry{Table: yyDollar[1].tableNameUnion(), Cols: yyDollar[3].identifierListUnion()} } @@ -17289,7 +17362,7 @@ yydefault: case 465: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3639 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3639 { yyLOCAL = tree.NewCheckTableStmt(yyDollar[3].tableNamesUnion(), yyDollar[4].checkTableOptionUnion()) } @@ -17297,7 +17370,7 @@ yydefault: case 466: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.CheckTableOption -//line mysql_sql.y:3645 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3645 { yyLOCAL = tree.CheckTableOptionNone } @@ -17305,7 +17378,7 @@ yydefault: case 467: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.CheckTableOption -//line mysql_sql.y:3649 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3649 { yyLOCAL = tree.CheckTableOptionExtended } @@ -17313,7 +17386,7 @@ yydefault: case 468: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.CheckTableOption -//line mysql_sql.y:3653 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3653 { yyLOCAL = tree.CheckTableOptionForUpgrade } @@ -17321,7 +17394,7 @@ yydefault: case 469: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3659 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3659 { yyLOCAL = tree.NewShowProfileStmt(yyDollar[3].int64ValUnion(), yyDollar[4].limitUnion()) } @@ -17329,7 +17402,7 @@ yydefault: case 470: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:3665 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3665 { yyLOCAL = 0 } @@ -17337,7 +17410,7 @@ yydefault: case 471: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:3669 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3669 { yyLOCAL = yyDollar[3].item.(int64) } @@ -17345,7 +17418,7 @@ yydefault: case 472: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3675 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3675 { yyLOCAL = &tree.UpgradeStatement{ Target: yyDollar[3].upgrade_targetUnion(), @@ -17356,7 +17429,7 @@ yydefault: case 473: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Target -//line mysql_sql.y:3684 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3684 { yyLOCAL = &tree.Target{ AccountName: yyDollar[1].str, @@ -17367,7 +17440,7 @@ yydefault: case 474: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Target -//line mysql_sql.y:3691 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3691 { yyLOCAL = &tree.Target{ AccountName: "", @@ -17378,7 +17451,7 @@ yydefault: case 475: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:3699 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3699 { yyLOCAL = -1 } @@ -17386,7 +17459,7 @@ yydefault: case 476: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:3703 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3703 { res := yyDollar[3].item.(int64) if res <= 0 { @@ -17399,7 +17472,7 @@ yydefault: case 491: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3731 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3731 { yyLOCAL = &tree.AlterIcebergCatalog{ Name: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -17410,7 +17483,7 @@ yydefault: case 492: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3738 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3738 { yyLOCAL = &tree.AlterIcebergCatalog{ Name: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -17421,7 +17494,7 @@ yydefault: case 493: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3747 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3747 { yyLOCAL = &tree.AlterMongoDBConnection{ Name: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -17433,7 +17506,7 @@ yydefault: case 494: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3755 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3755 { yyLOCAL = &tree.AlterMongoDBConnection{ Name: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -17445,7 +17518,7 @@ yydefault: case 495: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3763 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3763 { yyLOCAL = &tree.AlterMongoDBConnection{ Name: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -17456,7 +17529,7 @@ yydefault: case 496: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3770 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3770 { yyLOCAL = &tree.AlterMongoDBConnection{ Name: tree.Identifier(yyDollar[4].cstrUnion().Compare()), @@ -17467,7 +17540,7 @@ yydefault: case 497: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3779 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3779 { var ifExists = yyDollar[3].boolValUnion() var name = yyDollar[4].tableNameUnion() @@ -17492,7 +17565,7 @@ yydefault: case 498: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3802 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3802 { var ifExists = yyDollar[3].boolValUnion() var name = yyDollar[4].tableNameUnion() @@ -17504,7 +17577,7 @@ yydefault: case 499: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3812 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3812 { var table = yyDollar[3].tableNameUnion() alterTable := tree.NewAlterTable(table) @@ -17515,7 +17588,7 @@ yydefault: case 500: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3819 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3819 { var table = yyDollar[3].tableNameUnion() alterTable := tree.NewAlterTable(table) @@ -17526,7 +17599,7 @@ yydefault: case 501: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3828 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3828 { alterTables := yyDollar[3].renameTableOptionsUnion() renameTables := tree.NewRenameTable(alterTables) @@ -17536,7 +17609,7 @@ yydefault: case 502: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.AlterTable -//line mysql_sql.y:3836 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3836 { yyLOCAL = []*tree.AlterTable{yyDollar[1].renameTableOptionUnion()} } @@ -17544,7 +17617,7 @@ yydefault: case 503: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.AlterTable -//line mysql_sql.y:3840 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3840 { yyLOCAL = append(yyDollar[1].renameTableOptionsUnion(), yyDollar[3].renameTableOptionUnion()) } @@ -17552,7 +17625,7 @@ yydefault: case 504: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.AlterTable -//line mysql_sql.y:3846 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3846 { var table = yyDollar[1].tableNameUnion() alterTable := tree.NewAlterTable(table) @@ -17564,7 +17637,7 @@ yydefault: case 505: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AlterTableOptions -//line mysql_sql.y:3856 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3856 { yyLOCAL = []tree.AlterTableOption{yyDollar[1].alterTableOptionUnion()} } @@ -17572,7 +17645,7 @@ yydefault: case 506: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOptions -//line mysql_sql.y:3860 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3860 { yyLOCAL = append(yyDollar[1].alterTableOptionsUnion(), yyDollar[3].alterTableOptionUnion()) } @@ -17580,7 +17653,7 @@ yydefault: case 507: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AlterPartitionOption -//line mysql_sql.y:3866 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3866 { yyLOCAL = yyDollar[1].alterPartitionOptionUnion() } @@ -17588,7 +17661,7 @@ yydefault: case 508: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.AlterPartitionOption -//line mysql_sql.y:3870 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3870 { yyDollar[3].partitionByUnion().Num = uint64(yyDollar[4].int64ValUnion()) var PartBy = yyDollar[3].partitionByUnion() @@ -17614,7 +17687,7 @@ yydefault: case 509: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3894 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3894 { var ifExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -17626,7 +17699,7 @@ yydefault: case 510: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3904 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3904 { yyLOCAL = &tree.AlterSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -17637,7 +17710,7 @@ yydefault: case 511: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3911 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3911 { yyLOCAL = &tree.AlterSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -17648,7 +17721,7 @@ yydefault: case 512: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3918 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3918 { yyLOCAL = &tree.AlterSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -17661,7 +17734,7 @@ yydefault: case 513: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3927 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3927 { yyLOCAL = &tree.AlterSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -17673,7 +17746,7 @@ yydefault: case 514: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3935 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3935 { yyLOCAL = &tree.AlterSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -17685,7 +17758,7 @@ yydefault: case 515: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3943 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3943 { yyLOCAL = &tree.AlterSQLTask{ Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), @@ -17697,7 +17770,7 @@ yydefault: case 516: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3953 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3953 { var oldName = yyDollar[5].cstrUnion().Compare() var newName = yyDollar[8].cstrUnion().Compare() @@ -17707,7 +17780,7 @@ yydefault: case 517: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3959 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3959 { var oldName = yyDollar[3].cstrUnion().Compare() var newName = yyDollar[6].cstrUnion().Compare() @@ -17717,7 +17790,7 @@ yydefault: case 518: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3965 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3965 { var roleName = yyDollar[3].cstrUnion().Compare() var ruleSQL = yyDollar[6].str @@ -17730,7 +17803,7 @@ yydefault: case 519: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:3974 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3974 { var roleName = yyDollar[3].cstrUnion().Compare() var dbName = yyDollar[8].cstrUnion().Compare() @@ -17741,7 +17814,7 @@ yydefault: case 520: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterPartitionOption -//line mysql_sql.y:3983 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3983 { var typ = tree.AlterPartitionAddPartition var partitions = yyDollar[3].partitionsUnion() @@ -17755,7 +17828,7 @@ yydefault: case 521: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterPartitionOption -//line mysql_sql.y:3993 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:3993 { var typ = tree.AlterPartitionDropPartition var partitionNames = yyDollar[3].PartitionNamesUnion() @@ -17775,7 +17848,7 @@ yydefault: case 522: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterPartitionOption -//line mysql_sql.y:4009 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4009 { var typ = tree.AlterPartitionTruncatePartition var partitionNames = yyDollar[3].PartitionNamesUnion() @@ -17795,7 +17868,7 @@ yydefault: case 523: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:4027 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4027 { yyLOCAL = nil } @@ -17803,7 +17876,7 @@ yydefault: case 524: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:4031 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4031 { yyLOCAL = yyDollar[1].PartitionNamesUnion() } @@ -17811,7 +17884,7 @@ yydefault: case 525: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:4037 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4037 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[1].cstrUnion().Compare())} } @@ -17819,7 +17892,7 @@ yydefault: case 526: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:4041 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4041 { yyLOCAL = append(yyDollar[1].PartitionNamesUnion(), tree.Identifier(yyDollar[3].cstrUnion().Compare())) } @@ -17827,7 +17900,7 @@ yydefault: case 527: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4047 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4047 { var def = yyDollar[2].tableDefUnion() opt := tree.NewAlterOptionAdd(def) @@ -17837,7 +17910,7 @@ yydefault: case 528: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4053 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4053 { var typ = tree.AlterTableModifyColumn var newColumn = yyDollar[3].columnTableDefUnion() @@ -17849,7 +17922,7 @@ yydefault: case 529: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4061 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4061 { // Type OldColumnName NewColumn Position var typ = tree.AlterTableChangeColumn @@ -17863,7 +17936,7 @@ yydefault: case 530: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4071 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4071 { var typ = tree.AlterTableRenameColumn var oldColumnName = yyDollar[3].unresolvedNameUnion() @@ -17875,7 +17948,7 @@ yydefault: case 531: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4079 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4079 { var typ = tree.AlterTableAlterColumn var columnName = yyDollar[3].unresolvedNameUnion() @@ -17889,7 +17962,7 @@ yydefault: case 532: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4089 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4089 { var typ = tree.AlterTableAlterColumn var columnName = yyDollar[3].unresolvedNameUnion() @@ -17903,7 +17976,7 @@ yydefault: case 533: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4099 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4099 { var typ = tree.AlterTableAlterColumn var columnName = yyDollar[3].unresolvedNameUnion() @@ -17917,7 +17990,7 @@ yydefault: case 534: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4109 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4109 { var orderByClauseType = tree.AlterTableOrderByColumn var orderByColumnList = yyDollar[3].alterColumnOrderByUnion() @@ -17928,7 +18001,7 @@ yydefault: case 535: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4116 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4116 { yyLOCAL = tree.AlterTableOption(yyDollar[2].alterTableOptionUnion()) } @@ -17936,7 +18009,7 @@ yydefault: case 536: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4120 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4120 { yyLOCAL = tree.AlterTableOption(yyDollar[2].alterTableOptionUnion()) } @@ -17944,7 +18017,7 @@ yydefault: case 537: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4124 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4124 { yyLOCAL = tree.AlterTableOption(yyDollar[1].tableOptionUnion()) } @@ -17952,7 +18025,7 @@ yydefault: case 538: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4128 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4128 { yyLOCAL = tree.AlterTableOption(yyDollar[3].alterTableOptionUnion()) } @@ -17960,7 +18033,7 @@ yydefault: case 539: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4132 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4132 { var column = yyDollar[3].columnTableDefUnion() var position = yyDollar[4].alterColPositionUnion() @@ -17971,7 +18044,7 @@ yydefault: case 540: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4139 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4139 { yyLOCAL = tree.NewAlterOptionAlgorithm(yyDollar[3].str) } @@ -17979,7 +18052,7 @@ yydefault: case 541: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4143 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4143 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[4].str) } @@ -17987,7 +18060,7 @@ yydefault: case 542: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4147 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4147 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[5].str) } @@ -17995,7 +18068,7 @@ yydefault: case 543: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4151 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4151 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[5].str) } @@ -18003,7 +18076,7 @@ yydefault: case 544: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4155 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4155 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[1].str) } @@ -18011,7 +18084,7 @@ yydefault: case 545: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4159 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4159 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[1].str) } @@ -18019,7 +18092,7 @@ yydefault: case 546: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4163 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4163 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[1].str) } @@ -18027,7 +18100,7 @@ yydefault: case 547: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4167 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4167 { yyLOCAL = tree.NewAlterOptionLock(yyDollar[3].str) } @@ -18035,33 +18108,33 @@ yydefault: case 548: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4171 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4171 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[1].str) } yyVAL.union = yyLOCAL case 549: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:4176 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4176 { yyVAL.str = "" } case 566: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:4207 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4207 { yyVAL.str = "" } case 567: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:4211 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4211 { yyVAL.str = string("COLUMN") } case 568: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ColumnPosition -//line mysql_sql.y:4216 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4216 { var typ = tree.ColumnPositionNone var relativeColumn *tree.UnresolvedName @@ -18071,7 +18144,7 @@ yydefault: case 569: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.ColumnPosition -//line mysql_sql.y:4222 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4222 { var typ = tree.ColumnPositionFirst var relativeColumn *tree.UnresolvedName @@ -18081,7 +18154,7 @@ yydefault: case 570: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ColumnPosition -//line mysql_sql.y:4228 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4228 { var typ = tree.ColumnPositionAfter var relativeColumn = yyDollar[2].unresolvedNameUnion() @@ -18091,7 +18164,7 @@ yydefault: case 571: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.AlterColumnOrder -//line mysql_sql.y:4236 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4236 { yyLOCAL = []*tree.AlterColumnOrder{yyDollar[1].alterColumnOrderUnion()} } @@ -18099,7 +18172,7 @@ yydefault: case 572: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.AlterColumnOrder -//line mysql_sql.y:4240 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4240 { yyLOCAL = append(yyDollar[1].alterColumnOrderByUnion(), yyDollar[3].alterColumnOrderUnion()) } @@ -18107,7 +18180,7 @@ yydefault: case 573: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.AlterColumnOrder -//line mysql_sql.y:4246 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4246 { var column = yyDollar[1].unresolvedNameUnion() var direction = yyDollar[2].directionUnion() @@ -18117,7 +18190,7 @@ yydefault: case 574: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4254 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4254 { var name = yyDollar[1].unresolvedObjectNameUnion() yyLOCAL = tree.NewAlterOptionTableName(name) @@ -18126,7 +18199,7 @@ yydefault: case 575: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4261 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4261 { var dropType = tree.AlterTableDropIndex var name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) @@ -18136,7 +18209,7 @@ yydefault: case 576: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4267 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4267 { var dropType = tree.AlterTableDropKey var name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) @@ -18146,7 +18219,7 @@ yydefault: case 577: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4273 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4273 { var dropType = tree.AlterTableDropColumn var name = tree.Identifier(yyDollar[1].cstrUnion().Compare()) @@ -18156,7 +18229,7 @@ yydefault: case 578: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4279 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4279 { var dropType = tree.AlterTableDropColumn var name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) @@ -18166,7 +18239,7 @@ yydefault: case 579: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4285 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4285 { var dropType = tree.AlterTableDropForeignKey var name = tree.Identifier(yyDollar[3].cstrUnion().Compare()) @@ -18177,7 +18250,7 @@ yydefault: case 580: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4292 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4292 { yyLOCAL = &tree.AlterOptionDrop{ Typ: tree.AlterTableDropForeignKey, @@ -18188,7 +18261,7 @@ yydefault: case 581: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4299 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4299 { var dropType = tree.AlterTableDropPrimaryKey var name = tree.Identifier("") @@ -18198,7 +18271,7 @@ yydefault: case 582: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4307 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4307 { var indexName = tree.Identifier(yyDollar[2].cstrUnion().Compare()) var visibility = yyDollar[3].indexVisibilityUnion() @@ -18208,7 +18281,7 @@ yydefault: case 583: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4313 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4313 { var io *tree.IndexOption = nil if yyDollar[5].indexOptionUnion() == nil { @@ -18227,7 +18300,7 @@ yydefault: case 584: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4328 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4328 { var io *tree.IndexOption = nil if yyDollar[4].indexOptionUnion() == nil { @@ -18244,7 +18317,7 @@ yydefault: case 585: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4341 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4341 { var io *tree.IndexOption = nil if yyDollar[4].indexOptionUnion() == nil { @@ -18261,7 +18334,7 @@ yydefault: case 586: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4354 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4354 { var io *tree.IndexOption = nil if yyDollar[4].indexOptionUnion() == nil { @@ -18278,7 +18351,7 @@ yydefault: case 587: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4367 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4367 { var io *tree.IndexOption = nil if yyDollar[4].indexOptionUnion() == nil { @@ -18293,61 +18366,78 @@ yydefault: } yyVAL.union = yyLOCAL case 588: + yyDollar = yyS[yypt-4 : yypt+1] + var yyLOCAL tree.AlterTableOption +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4380 + { + var io *tree.IndexOption = nil + if yyDollar[4].indexOptionUnion() == nil { + io = tree.NewIndexOption() + io.IType = tree.INDEX_TYPE_FULLTEXT2 + } else { + io = yyDollar[4].indexOptionUnion() + io.IType = tree.INDEX_TYPE_FULLTEXT2 + } + var name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) + yyLOCAL = tree.NewAlterOptionAlterReIndex(name, io) + } + yyVAL.union = yyLOCAL + case 589: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4380 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4393 { var checkType = yyDollar[1].str var enforce = yyDollar[3].boolValUnion() yyLOCAL = tree.NewAlterOptionAlterCheck(checkType, enforce) } yyVAL.union = yyLOCAL - case 589: + case 590: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AlterTableOption -//line mysql_sql.y:4386 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4399 { var checkType = yyDollar[1].str var enforce = yyDollar[3].boolValUnion() yyLOCAL = tree.NewAlterOptionAlterCheck(checkType, enforce) } yyVAL.union = yyLOCAL - case 590: + case 591: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.VisibleType -//line mysql_sql.y:4394 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4407 { yyLOCAL = tree.VISIBLE_TYPE_VISIBLE } yyVAL.union = yyLOCAL - case 591: + case 592: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.VisibleType -//line mysql_sql.y:4398 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4411 { yyLOCAL = tree.VISIBLE_TYPE_INVISIBLE } yyVAL.union = yyLOCAL - case 592: + case 593: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:4404 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4417 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 593: + case 594: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:4408 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4421 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 594: + case 595: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4414 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4427 { var ifExists = yyDollar[3].boolValUnion() var name = yyDollar[4].exprUnion() @@ -18364,10 +18454,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 595: + case 596: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4432 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4445 { var accountName = "" var dbName = yyDollar[3].str @@ -18383,10 +18473,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 596: + case 597: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4447 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4460 { var accountName = "" var dbName = yyDollar[3].str @@ -18402,10 +18492,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 597: + case 598: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4462 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4475 { var accountName = yyDollar[4].str var dbName = "" @@ -18421,10 +18511,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 598: + case 599: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4477 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4490 { assignments := []*tree.VarAssignmentExpr{ { @@ -18437,20 +18527,20 @@ yydefault: yyLOCAL = &tree.SetVar{Assignments: assignments} } yyVAL.union = yyLOCAL - case 599: + case 600: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.AlterAccountAuthOption -//line mysql_sql.y:4490 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4503 { yyLOCAL = tree.AlterAccountAuthOption{ Exist: false, } } yyVAL.union = yyLOCAL - case 600: + case 601: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AlterAccountAuthOption -//line mysql_sql.y:4496 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4509 { yyLOCAL = tree.AlterAccountAuthOption{ Exist: true, @@ -18460,10 +18550,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 601: + case 602: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4507 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4520 { // Create temporary variables with meaningful names ifExists := yyDollar[3].boolValUnion() @@ -18476,10 +18566,10 @@ yydefault: yyLOCAL = tree.NewAlterUser(ifExists, users, role, miscOpt, commentOrAttribute) } yyVAL.union = yyLOCAL - case 602: + case 603: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4519 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4532 { ifExists := yyDollar[3].boolValUnion() var Username = yyDollar[4].usernameRecordUnion().Username @@ -18491,10 +18581,10 @@ yydefault: yyLOCAL = tree.NewAlterUser(ifExists, users, nil, miscOpt, commentOrAttribute) } yyVAL.union = yyLOCAL - case 603: + case 604: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4530 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4543 { ifExists := yyDollar[3].boolValUnion() var Username = yyDollar[4].usernameRecordUnion().Username @@ -18506,18 +18596,18 @@ yydefault: yyLOCAL = tree.NewAlterUser(ifExists, users, nil, miscOpt, commentOrAttribute) } yyVAL.union = yyLOCAL - case 604: + case 605: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Role -//line mysql_sql.y:4542 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4555 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 605: + case 606: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Role -//line mysql_sql.y:4546 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4559 { var UserName = yyDollar[3].str yyLOCAL = tree.NewRole( @@ -18525,66 +18615,66 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 606: + case 607: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:4554 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4567 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 607: + case 608: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:4558 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4571 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 608: + case 609: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4563 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4576 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 609: + case 610: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4567 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4580 { yyLOCAL = yyDollar[1].userMiscOptionUnion() } yyVAL.union = yyLOCAL - case 610: + case 611: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4583 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4596 { yyLOCAL = tree.NewUserMiscOptionAccountUnlock() } yyVAL.union = yyLOCAL - case 611: + case 612: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4587 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4600 { yyLOCAL = tree.NewUserMiscOptionAccountLock() } yyVAL.union = yyLOCAL - case 612: + case 613: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4591 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4604 { yyLOCAL = tree.NewUserMiscOptionPasswordExpireNone() } yyVAL.union = yyLOCAL - case 613: + case 614: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4595 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4608 { var Value = yyDollar[3].item.(int64) yyLOCAL = tree.NewUserMiscOptionPasswordExpireInterval( @@ -18592,34 +18682,34 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 614: + case 615: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4602 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4615 { yyLOCAL = tree.NewUserMiscOptionPasswordExpireNever() } yyVAL.union = yyLOCAL - case 615: + case 616: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4606 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4619 { yyLOCAL = tree.NewUserMiscOptionPasswordExpireDefault() } yyVAL.union = yyLOCAL - case 616: + case 617: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4610 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4623 { yyLOCAL = tree.NewUserMiscOptionPasswordHistoryDefault() } yyVAL.union = yyLOCAL - case 617: + case 618: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4614 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4627 { var Value = yyDollar[3].item.(int64) yyLOCAL = tree.NewUserMiscOptionPasswordHistoryCount( @@ -18627,18 +18717,18 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 618: + case 619: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4621 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4634 { yyLOCAL = tree.NewUserMiscOptionPasswordReuseIntervalDefault() } yyVAL.union = yyLOCAL - case 619: + case 620: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4625 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4638 { var Value = yyDollar[4].item.(int64) yyLOCAL = tree.NewUserMiscOptionPasswordReuseIntervalCount( @@ -18646,34 +18736,34 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 620: + case 621: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4632 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4645 { yyLOCAL = tree.NewUserMiscOptionPasswordRequireCurrentNone() } yyVAL.union = yyLOCAL - case 621: + case 622: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4636 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4649 { yyLOCAL = tree.NewUserMiscOptionPasswordRequireCurrentDefault() } yyVAL.union = yyLOCAL - case 622: + case 623: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4640 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4653 { yyLOCAL = tree.NewUserMiscOptionPasswordRequireCurrentOptional() } yyVAL.union = yyLOCAL - case 623: + case 624: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4644 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4657 { var Value = yyDollar[2].item.(int64) yyLOCAL = tree.NewUserMiscOptionFailedLoginAttempts( @@ -18681,10 +18771,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 624: + case 625: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4651 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4664 { var Value = yyDollar[2].item.(int64) yyLOCAL = tree.NewUserMiscOptionPasswordLockTimeCount( @@ -18692,38 +18782,38 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 625: + case 626: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.UserMiscOption -//line mysql_sql.y:4658 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4671 { yyLOCAL = tree.NewUserMiscOptionPasswordLockTimeUnbounded() } yyVAL.union = yyLOCAL - case 626: + case 627: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:4664 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4677 { yyVAL.item = nil } - case 627: + case 628: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:4669 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4682 { yyVAL.item = nil } - case 673: + case 674: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4724 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4737 { yyLOCAL = &tree.ShowSQLTasks{} } yyVAL.union = yyLOCAL - case 674: + case 675: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4730 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4743 { stmt := &tree.ShowSQLTaskRuns{} if yyDollar[4].str != "" { @@ -18737,38 +18827,38 @@ yydefault: yyLOCAL = stmt } yyVAL.union = yyLOCAL - case 675: + case 676: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:4744 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4757 { yyVAL.str = "" } - case 676: + case 677: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:4748 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4761 { yyVAL.str = yyDollar[2].cstrUnion().Compare() } - case 677: + case 678: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:4753 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4766 { yyLOCAL = -1 } yyVAL.union = yyLOCAL - case 678: + case 679: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:4757 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4770 { yyLOCAL = sqlTaskInt64(yyDollar[2].item) } yyVAL.union = yyLOCAL - case 679: + case 680: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4763 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4776 { yyLOCAL = &tree.ShowIcebergCatalogs{ Like: yyDollar[4].comparisionExprUnion(), @@ -18776,10 +18866,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 680: + case 681: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4771 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4784 { yyLOCAL = &tree.ShowIcebergNamespaces{ Catalog: tree.Identifier(yyDollar[5].cstrUnion().Compare()), @@ -18788,10 +18878,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 681: + case 682: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4779 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4792 { yyLOCAL = &tree.ShowIcebergNamespaces{ Catalog: tree.Identifier(yyDollar[6].cstrUnion().Compare()), @@ -18800,10 +18890,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 682: + case 683: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4787 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4800 { yyLOCAL = &tree.ShowIcebergNamespaces{ Like: yyDollar[4].comparisionExprUnion(), @@ -18811,10 +18901,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 683: + case 684: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4794 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4807 { yyLOCAL = &tree.ShowIcebergTables{ Like: yyDollar[4].comparisionExprUnion(), @@ -18822,10 +18912,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 684: + case 685: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4801 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4814 { yyLOCAL = &tree.ShowIcebergTables{ Catalog: tree.Identifier(yyDollar[5].cstrUnion().Compare()), @@ -18834,10 +18924,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 685: + case 686: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4809 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4822 { yyLOCAL = &tree.ShowIcebergTables{ Catalog: tree.Identifier(yyDollar[5].cstrUnion().Compare()), @@ -18847,10 +18937,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 686: + case 687: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4818 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4831 { yyLOCAL = &tree.ShowIcebergTables{ Catalog: tree.Identifier(yyDollar[6].cstrUnion().Compare()), @@ -18859,10 +18949,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 687: + case 688: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4826 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4839 { yyLOCAL = &tree.ShowIcebergTables{ Namespace: yyDollar[6].str, @@ -18871,10 +18961,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 688: + case 689: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4834 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4847 { yyLOCAL = &tree.ShowIcebergTables{ Namespace: yyDollar[6].str, @@ -18884,10 +18974,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 689: + case 690: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4845 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4858 { yyLOCAL = &tree.ShowMongoDBConnections{ Like: yyDollar[4].comparisionExprUnion(), @@ -18895,50 +18985,50 @@ yydefault: } } yyVAL.union = yyLOCAL - case 690: + case 691: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:4854 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4867 { yyVAL.str = yyDollar[1].str } - case 691: + case 692: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4860 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4873 { yyLOCAL = &tree.ShowLogserviceReplicas{} } yyVAL.union = yyLOCAL - case 692: + case 693: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4866 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4879 { yyLOCAL = &tree.ShowLogserviceStores{} } yyVAL.union = yyLOCAL - case 693: + case 694: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4872 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4885 { yyLOCAL = &tree.ShowLogserviceSettings{} } yyVAL.union = yyLOCAL - case 694: + case 695: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4878 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4891 { yyLOCAL = &tree.ShowRules{ RoleName: yyDollar[5].cstrUnion().Compare(), } } yyVAL.union = yyLOCAL - case 695: + case 696: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4886 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4899 { yyLOCAL = &tree.ShowCollation{ Like: yyDollar[3].comparisionExprUnion(), @@ -18946,50 +19036,50 @@ yydefault: } } yyVAL.union = yyLOCAL - case 696: + case 697: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4895 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4908 { yyLOCAL = &tree.ShowStages{ Like: yyDollar[3].comparisionExprUnion(), } } yyVAL.union = yyLOCAL - case 697: + case 698: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4903 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4916 { yyLOCAL = &tree.ShowSnapShots{ Where: yyDollar[3].whereUnion(), } } yyVAL.union = yyLOCAL - case 698: + case 699: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4911 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4924 { yyLOCAL = &tree.ShowPitr{ Where: yyDollar[3].whereUnion(), } } yyVAL.union = yyLOCAL - case 699: + case 700: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4919 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4932 { yyLOCAL = &tree.ShowRecoveryWindow{ Level: tree.RECOVERYWINDOWLEVELACCOUNT, } } yyVAL.union = yyLOCAL - case 700: + case 701: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4925 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4938 { yyLOCAL = &tree.ShowRecoveryWindow{ Level: tree.RECOVERYWINDOWLEVELDATABASE, @@ -18997,10 +19087,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 701: + case 702: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4932 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4945 { yyLOCAL = &tree.ShowRecoveryWindow{ Level: tree.RECOVERYWINDOWLEVELTABLE, @@ -19009,10 +19099,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 702: + case 703: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4940 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4953 { yyLOCAL = &tree.ShowRecoveryWindow{ Level: tree.RECOVERYWINDOWLEVELACCOUNT, @@ -19020,26 +19110,26 @@ yydefault: } } yyVAL.union = yyLOCAL - case 703: + case 704: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4949 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4962 { yyLOCAL = &tree.ShowGrants{ShowGrantType: tree.GrantForUser} } yyVAL.union = yyLOCAL - case 704: + case 705: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4953 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4966 { yyLOCAL = &tree.ShowGrants{Username: yyDollar[4].usernameRecordUnion().Username, Hostname: yyDollar[4].usernameRecordUnion().Hostname, Roles: yyDollar[5].rolesUnion(), ShowGrantType: tree.GrantForUser} } yyVAL.union = yyLOCAL - case 705: + case 706: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4957 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4970 { s := &tree.ShowGrants{} roles := []*tree.Role{ @@ -19050,44 +19140,44 @@ yydefault: yyLOCAL = s } yyVAL.union = yyLOCAL - case 706: + case 707: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.Role -//line mysql_sql.y:4968 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4981 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 707: + case 708: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.Role -//line mysql_sql.y:4972 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4985 { yyLOCAL = yyDollar[2].rolesUnion() } yyVAL.union = yyLOCAL - case 708: + case 709: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4978 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4991 { yyLOCAL = &tree.ShowTableStatus{DbName: yyDollar[5].str, Like: yyDollar[6].comparisionExprUnion(), Where: yyDollar[7].whereUnion()} } yyVAL.union = yyLOCAL - case 709: + case 710: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:4983 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:4996 { } - case 711: + case 712: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:4987 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5000 { } - case 713: + case 714: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:4992 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5005 { yyLOCAL = &tree.ShowFunctionOrProcedureStatus{ Like: yyDollar[4].comparisionExprUnion(), @@ -19096,10 +19186,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 714: + case 715: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5002 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5015 { yyLOCAL = &tree.ShowFunctionOrProcedureStatus{ Like: yyDollar[4].comparisionExprUnion(), @@ -19108,68 +19198,68 @@ yydefault: } } yyVAL.union = yyLOCAL - case 715: + case 716: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5012 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5025 { yyLOCAL = &tree.ShowRolesStmt{ Like: yyDollar[3].comparisionExprUnion(), } } yyVAL.union = yyLOCAL - case 716: + case 717: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5020 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5033 { yyLOCAL = &tree.ShowNodeList{} } yyVAL.union = yyLOCAL - case 717: + case 718: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5026 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5039 { yyLOCAL = &tree.ShowLocks{} } yyVAL.union = yyLOCAL - case 718: + case 719: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5032 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5045 { yyLOCAL = &tree.ShowTableNumber{DbName: yyDollar[4].str} } yyVAL.union = yyLOCAL - case 719: + case 720: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5038 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5051 { yyLOCAL = &tree.ShowColumnNumber{Table: yyDollar[3].unresolvedObjectNameUnion(), DbName: yyDollar[4].str} } yyVAL.union = yyLOCAL - case 720: + case 721: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5044 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5057 { yyLOCAL = &tree.ShowTableValues{Table: yyDollar[3].unresolvedObjectNameUnion(), DbName: yyDollar[4].str} } yyVAL.union = yyLOCAL - case 721: + case 722: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5050 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5063 { yyLOCAL = &tree.ShowTableSize{Table: yyDollar[3].unresolvedObjectNameUnion(), DbName: yyDollar[4].str} } yyVAL.union = yyLOCAL - case 722: + case 723: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5056 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5069 { s := yyDollar[2].statementUnion().(*tree.ShowTarget) s.Like = yyDollar[3].comparisionExprUnion() @@ -19177,74 +19267,74 @@ yydefault: yyLOCAL = s } yyVAL.union = yyLOCAL - case 723: + case 724: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5065 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5078 { yyLOCAL = &tree.ShowTarget{Type: tree.ShowConfig} } yyVAL.union = yyLOCAL - case 724: + case 725: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5069 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5082 { yyLOCAL = &tree.ShowTarget{Type: tree.ShowCharset} } yyVAL.union = yyLOCAL - case 725: + case 726: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5073 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5086 { yyLOCAL = &tree.ShowTarget{Type: tree.ShowEngines} } yyVAL.union = yyLOCAL - case 726: + case 727: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5077 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5090 { yyLOCAL = &tree.ShowTarget{DbName: yyDollar[3].str, Type: tree.ShowTriggers} } yyVAL.union = yyLOCAL - case 727: + case 728: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5081 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5094 { yyLOCAL = &tree.ShowTarget{DbName: yyDollar[3].str, Type: tree.ShowEvents} } yyVAL.union = yyLOCAL - case 728: + case 729: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5085 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5098 { yyLOCAL = &tree.ShowTarget{Type: tree.ShowPlugins} } yyVAL.union = yyLOCAL - case 729: + case 730: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5089 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5102 { yyLOCAL = &tree.ShowTarget{Type: tree.ShowPrivileges} } yyVAL.union = yyLOCAL - case 730: + case 731: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5093 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5106 { yyLOCAL = &tree.ShowTarget{Type: tree.ShowProfiles} } yyVAL.union = yyLOCAL - case 731: + case 732: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5099 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5112 { yyLOCAL = &tree.ShowIndex{ TableName: yyDollar[4].unresolvedObjectNameUnion(), @@ -19253,20 +19343,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 732: + case 733: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5108 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5121 { } - case 733: + case 734: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:5110 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5123 { } - case 737: + case 738: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5119 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5132 { yyLOCAL = &tree.ShowVariables{ Global: yyDollar[2].boolValUnion(), @@ -19275,10 +19365,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 738: + case 739: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5129 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5142 { yyLOCAL = &tree.ShowStatus{ Global: yyDollar[2].boolValUnion(), @@ -19287,58 +19377,58 @@ yydefault: } } yyVAL.union = yyLOCAL - case 739: + case 740: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:5138 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5151 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 740: + case 741: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:5142 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5155 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 741: + case 742: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:5146 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5159 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 742: + case 743: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5152 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5165 { yyLOCAL = &tree.ShowWarnings{} } yyVAL.union = yyLOCAL - case 743: + case 744: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5158 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5171 { yyLOCAL = &tree.ShowErrors{} } yyVAL.union = yyLOCAL - case 744: + case 745: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5164 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5177 { yyLOCAL = &tree.ShowProcessList{Full: yyDollar[2].fullOptUnion()} } yyVAL.union = yyLOCAL - case 745: + case 746: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5170 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5183 { yyLOCAL = &tree.ShowSequences{ DBName: yyDollar[3].str, @@ -19346,10 +19436,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 746: + case 747: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5179 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5192 { yyLOCAL = &tree.ShowTables{ Open: false, @@ -19361,10 +19451,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 747: + case 748: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5190 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5203 { yyLOCAL = &tree.ShowTables{ Open: true, @@ -19375,10 +19465,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 748: + case 749: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5202 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5215 { yyLOCAL = &tree.ShowDatabases{ Like: yyDollar[3].comparisionExprUnion(), @@ -19387,18 +19477,18 @@ yydefault: } } yyVAL.union = yyLOCAL - case 749: + case 750: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5210 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5223 { yyLOCAL = &tree.ShowDatabases{Like: yyDollar[3].comparisionExprUnion(), Where: yyDollar[4].whereUnion()} } yyVAL.union = yyLOCAL - case 750: + case 751: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5216 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5229 { yyLOCAL = &tree.ShowColumns{ Ext: false, @@ -19411,10 +19501,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 751: + case 752: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5228 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5241 { yyLOCAL = &tree.ShowColumns{ Ext: true, @@ -19427,134 +19517,134 @@ yydefault: } } yyVAL.union = yyLOCAL - case 752: + case 753: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5242 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5255 { yyLOCAL = &tree.ShowAccounts{Like: yyDollar[3].comparisionExprUnion()} } yyVAL.union = yyLOCAL - case 753: + case 754: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5248 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5261 { yyLOCAL = &tree.ShowPublications{Like: yyDollar[3].comparisionExprUnion()} } yyVAL.union = yyLOCAL - case 754: + case 755: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5254 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5267 { yyLOCAL = &tree.ShowPublicationCoverage{Name: yyDollar[4].str} } yyVAL.union = yyLOCAL - case 755: + case 756: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5260 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5273 { yyLOCAL = &tree.ShowAccountUpgrade{} } yyVAL.union = yyLOCAL - case 756: + case 757: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5266 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5279 { yyLOCAL = &tree.ShowSubscriptions{Like: yyDollar[3].comparisionExprUnion()} } yyVAL.union = yyLOCAL - case 757: + case 758: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5270 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5283 { yyLOCAL = &tree.ShowSubscriptions{All: true, Like: yyDollar[4].comparisionExprUnion()} } yyVAL.union = yyLOCAL - case 758: + case 759: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5276 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5289 { yyLOCAL = &tree.ShowCcprSubscriptions{TaskId: yyDollar[4].str} } yyVAL.union = yyLOCAL - case 759: + case 760: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5280 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5293 { yyLOCAL = &tree.ShowCcprSubscriptions{} } yyVAL.union = yyLOCAL - case 760: + case 761: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ComparisonExpr -//line mysql_sql.y:5285 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5298 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 761: + case 762: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ComparisonExpr -//line mysql_sql.y:5289 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5302 { yyLOCAL = tree.NewComparisonExpr(tree.LIKE, nil, yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 762: + case 763: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ComparisonExpr -//line mysql_sql.y:5293 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5306 { yyLOCAL = tree.NewComparisonExpr(tree.ILIKE, nil, yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 763: + case 764: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5298 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5311 { yyVAL.str = "" } - case 764: + case 765: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:5302 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5315 { yyVAL.str = yyDollar[2].cstrUnion().Compare() } - case 765: + case 766: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnresolvedObjectName -//line mysql_sql.y:5308 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5321 { yyLOCAL = yyDollar[2].unresolvedObjectNameUnion() } yyVAL.union = yyLOCAL - case 770: + case 771: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:5321 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5334 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 771: + case 772: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:5325 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5338 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 772: + case 773: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5331 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5344 { yyLOCAL = &tree.ShowCreateTable{ Name: yyDollar[4].unresolvedObjectNameUnion(), @@ -19562,10 +19652,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 773: + case 774: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5339 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5352 { yyLOCAL = &tree.ShowCreateView{ Name: yyDollar[4].unresolvedObjectNameUnion(), @@ -19573,10 +19663,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 774: + case 775: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5346 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5359 { yyLOCAL = &tree.ShowCreateDatabase{ IfNotExists: yyDollar[4].ifNotExistsUnion(), @@ -19585,94 +19675,94 @@ yydefault: } } yyVAL.union = yyLOCAL - case 775: + case 776: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5354 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5367 { yyLOCAL = &tree.ShowCreatePublications{Name: yyDollar[4].str} } yyVAL.union = yyLOCAL - case 776: + case 777: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5360 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5373 { yyLOCAL = &tree.ShowBackendServers{} } yyVAL.union = yyLOCAL - case 777: + case 778: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnresolvedObjectName -//line mysql_sql.y:5366 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5379 { tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedObjectName(tblName) } yyVAL.union = yyLOCAL - case 778: + case 779: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UnresolvedObjectName -//line mysql_sql.y:5371 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5384 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[3].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedObjectName(dbName, tblName) } yyVAL.union = yyLOCAL - case 779: + case 780: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:5379 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5392 { yyVAL.str = yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) } - case 780: + case 781: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnresolvedObjectName -//line mysql_sql.y:5385 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5398 { tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedObjectName(tblName) } yyVAL.union = yyLOCAL - case 781: + case 782: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UnresolvedObjectName -//line mysql_sql.y:5390 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5403 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[3].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedObjectName(dbName, tblName) } yyVAL.union = yyLOCAL - case 782: + case 783: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.UnresolvedObjectName -//line mysql_sql.y:5396 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5409 { yyLOCAL = tree.NewUnresolvedObjectName(yyDollar[1].cstrUnion().Compare(), yyDollar[3].cstrUnion().Compare(), yyDollar[5].cstrUnion().Compare()) } yyVAL.union = yyLOCAL - case 783: + case 784: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5402 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5415 { yyLOCAL = tree.NewTruncateTable(yyDollar[2].tableNameUnion()) } yyVAL.union = yyLOCAL - case 784: + case 785: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5406 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5419 { yyLOCAL = tree.NewTruncateTable(yyDollar[3].tableNameUnion()) } yyVAL.union = yyLOCAL - case 806: + case 807: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5437 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5450 { yyLOCAL = &tree.DropSQLTask{ IfExists: yyDollar[3].boolValUnion(), @@ -19680,10 +19770,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 807: + case 808: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5446 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5459 { yyLOCAL = &tree.DropIcebergCatalog{ IfExists: yyDollar[4].boolValUnion(), @@ -19691,10 +19781,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 808: + case 809: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5455 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5468 { yyLOCAL = &tree.DropMongoDBConnection{ IfExists: yyDollar[4].boolValUnion(), @@ -19702,56 +19792,56 @@ yydefault: } } yyVAL.union = yyLOCAL - case 809: + case 810: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5464 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5477 { var ifExists = yyDollar[3].boolValUnion() var name = yyDollar[4].tableNamesUnion() yyLOCAL = tree.NewDropSequence(ifExists, name) } yyVAL.union = yyLOCAL - case 810: + case 811: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5472 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5485 { var ifExists = yyDollar[3].boolValUnion() var name = yyDollar[4].exprUnion() yyLOCAL = tree.NewDropAccount(ifExists, name) } yyVAL.union = yyLOCAL - case 811: + case 812: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5480 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5493 { var ifExists = yyDollar[3].boolValUnion() var users = yyDollar[4].usersUnion() yyLOCAL = tree.NewDropUser(ifExists, users) } yyVAL.union = yyLOCAL - case 812: + case 813: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.User -//line mysql_sql.y:5488 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5501 { yyLOCAL = []*tree.User{yyDollar[1].userUnion()} } yyVAL.union = yyLOCAL - case 813: + case 814: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.User -//line mysql_sql.y:5492 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5505 { yyLOCAL = append(yyDollar[1].usersUnion(), yyDollar[3].userUnion()) } yyVAL.union = yyLOCAL - case 814: + case 815: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.User -//line mysql_sql.y:5498 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5511 { var Username = yyDollar[1].usernameRecordUnion().Username var Hostname = yyDollar[1].usernameRecordUnion().Hostname @@ -19763,20 +19853,20 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 815: + case 816: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5511 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5524 { var ifExists = yyDollar[3].boolValUnion() var roles = yyDollar[4].rolesUnion() yyLOCAL = tree.NewDropRole(ifExists, roles) } yyVAL.union = yyLOCAL - case 816: + case 817: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5519 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5532 { var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) var tableName = yyDollar[6].tableNameUnion() @@ -19784,10 +19874,10 @@ yydefault: yyLOCAL = tree.NewDropIndex(name, tableName, ifExists) } yyVAL.union = yyLOCAL - case 817: + case 818: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5528 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5541 { var ifExists = yyDollar[4].boolValUnion() var names = yyDollar[5].tableNamesUnion() @@ -19796,48 +19886,48 @@ yydefault: yyLOCAL = stmt } yyVAL.union = yyLOCAL - case 818: + case 819: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5538 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5551 { var ifExists = yyDollar[3].boolValUnion() var names = yyDollar[4].tableNamesUnion() yyLOCAL = tree.NewDropView(ifExists, names) } yyVAL.union = yyLOCAL - case 819: + case 820: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5546 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5559 { var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) var ifExists = yyDollar[3].boolValUnion() yyLOCAL = tree.NewDropDatabase(name, ifExists) } yyVAL.union = yyLOCAL - case 820: + case 821: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5552 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5565 { var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) var ifExists = yyDollar[3].boolValUnion() yyLOCAL = tree.NewDropDatabase(name, ifExists) } yyVAL.union = yyLOCAL - case 821: + case 822: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5560 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5573 { yyLOCAL = tree.NewDeallocate(tree.Identifier(yyDollar[3].str), true) } yyVAL.union = yyLOCAL - case 822: + case 823: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5566 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5579 { var ifExists = yyDollar[3].boolValUnion() var name = yyDollar[4].functionNameUnion() @@ -19847,48 +19937,48 @@ yydefault: yyLOCAL = dropFunction } yyVAL.union = yyLOCAL - case 823: + case 824: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5577 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5590 { var name = yyDollar[3].procNameUnion() var ifExists = false yyLOCAL = tree.NewDropProcedure(name, ifExists) } yyVAL.union = yyLOCAL - case 824: + case 825: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5583 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5596 { var name = yyDollar[5].procNameUnion() var ifExists = true yyLOCAL = tree.NewDropProcedure(name, ifExists) } yyVAL.union = yyLOCAL - case 827: + case 828: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5593 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5606 { yyDollar[2].statementUnion().(*tree.Delete).With = yyDollar[1].withClauseUnion() yyLOCAL = yyDollar[2].statementUnion() } yyVAL.union = yyLOCAL - case 828: + case 829: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5598 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5611 { yyDollar[2].statementUnion().(*tree.Delete).With = yyDollar[1].withClauseUnion() yyLOCAL = yyDollar[2].statementUnion() } yyVAL.union = yyLOCAL - case 829: + case 830: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5605 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5618 { // Single-Table Syntax t := &tree.AliasedTableExpr{ @@ -19910,10 +20000,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 830: + case 831: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5626 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5639 { // Multiple-Table Syntax yyLOCAL = &tree.Delete{ @@ -19927,10 +20017,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 831: + case 832: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5641 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5654 { // Multiple-Table Syntax yyLOCAL = &tree.Delete{ @@ -19944,36 +20034,36 @@ yydefault: } } yyVAL.union = yyLOCAL - case 832: + case 833: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableExprs -//line mysql_sql.y:5656 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5669 { yyLOCAL = tree.TableExprs{yyDollar[1].tableNameUnion()} } yyVAL.union = yyLOCAL - case 833: + case 834: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableExprs -//line mysql_sql.y:5660 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5673 { yyLOCAL = append(yyDollar[1].tableExprsUnion(), yyDollar[3].tableNameUnion()) } yyVAL.union = yyLOCAL - case 834: + case 835: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.TableName -//line mysql_sql.y:5666 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5679 { tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) prefix := tree.ObjectNamePrefix{ExplicitSchema: false} yyLOCAL = tree.NewTableName(tree.Identifier(tblName), prefix, nil) } yyVAL.union = yyLOCAL - case 835: + case 836: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.TableName -//line mysql_sql.y:5672 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5685 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[3].cstrUnion().Origin()) @@ -19981,49 +20071,49 @@ yydefault: yyLOCAL = tree.NewTableName(tree.Identifier(tblName), prefix, nil) } yyVAL.union = yyLOCAL - case 836: + case 837: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5681 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5694 { } - case 837: + case 838: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:5683 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5696 { } - case 838: + case 839: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5686 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5699 { yyVAL.str = "" } - case 843: + case 844: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5695 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5708 { yyVAL.str = "" } - case 845: + case 846: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5699 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5712 { yyVAL.str = "" } - case 846: + case 847: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:5703 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5716 { yyVAL.str = "ignore" } - case 847: + case 848: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:5711 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5724 { } - case 850: + case 851: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5717 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5730 { rep := yyDollar[5].replaceUnion() rep.Table = yyDollar[3].tableExprUnion() @@ -20032,10 +20122,10 @@ yydefault: yyLOCAL = rep } yyVAL.union = yyLOCAL - case 851: + case 852: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5727 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5740 { vc := tree.NewValuesClause(yyDollar[2].rowsExprsUnion()) yyLOCAL = &tree.Replace{ @@ -20043,20 +20133,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 852: + case 853: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5734 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5747 { yyLOCAL = &tree.Replace{ Rows: yyDollar[1].selectUnion(), } } yyVAL.union = yyLOCAL - case 853: + case 854: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5740 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5753 { yyLOCAL = &tree.Replace{ Columns: yyDollar[2].identifierListUnion(), @@ -20064,20 +20154,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 854: + case 855: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5747 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5760 { yyLOCAL = &tree.Replace{ Rows: yyDollar[1].selectUnion(), } } yyVAL.union = yyLOCAL - case 855: + case 856: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5753 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5766 { vc := tree.NewValuesClause(yyDollar[5].rowsExprsUnion()) yyLOCAL = &tree.Replace{ @@ -20086,10 +20176,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 856: + case 857: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5761 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5774 { vc := tree.NewValuesClause(yyDollar[4].rowsExprsUnion()) yyLOCAL = &tree.Replace{ @@ -20097,10 +20187,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 857: + case 858: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5768 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5781 { yyLOCAL = &tree.Replace{ Columns: yyDollar[2].identifierListUnion(), @@ -20108,10 +20198,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 858: + case 859: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Replace -//line mysql_sql.y:5775 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5788 { if yyDollar[2].assignmentsUnion() == nil { yylex.Error("the set list of replace can not be empty") @@ -20131,29 +20221,29 @@ yydefault: } } yyVAL.union = yyLOCAL - case 859: + case 860: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:5796 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5809 { // MySQL treats TABLE as a query source, so ORDER BY and LIMIT belong to // the SELECT wrapper produced by the TABLE-to-SELECT rewrite. yyLOCAL = tree.NewSelect(makeSelectStarFromTable(yyDollar[2].tableNameUnion()), yyDollar[3].orderByUnion(), yyDollar[4].limitUnion()) } yyVAL.union = yyLOCAL - case 861: + case 862: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5805 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5818 { yyDollar[2].statementUnion().(*tree.Insert).With = yyDollar[1].withClauseUnion() yyLOCAL = yyDollar[2].statementUnion() } yyVAL.union = yyLOCAL - case 862: + case 863: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5812 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5825 { ins := yyDollar[4].insertUnion() ins.Table = yyDollar[2].tableExprUnion() @@ -20166,10 +20256,10 @@ yydefault: yyLOCAL = ins } yyVAL.union = yyLOCAL - case 863: + case 864: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5824 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5837 { ins := yyDollar[5].insertUnion() ins.Table = yyDollar[3].tableExprUnion() @@ -20182,10 +20272,10 @@ yydefault: yyLOCAL = ins } yyVAL.union = yyLOCAL - case 864: + case 865: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5836 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5849 { ins := yyDollar[5].insertUnion() ins.Table = yyDollar[3].tableExprUnion() @@ -20198,43 +20288,43 @@ yydefault: yyLOCAL = ins } yyVAL.union = yyLOCAL - case 865: + case 866: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.SelectExprs -//line mysql_sql.y:5849 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5862 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 866: + case 867: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.SelectExprs -//line mysql_sql.y:5853 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5866 { yyLOCAL = yyDollar[2].selectExprsUnion() } yyVAL.union = yyLOCAL - case 867: + case 868: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5859 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5872 { yyLOCAL = yyDollar[1].mergeUnion() } yyVAL.union = yyLOCAL - case 868: + case 869: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:5863 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5876 { yyDollar[2].mergeUnion().With = yyDollar[1].withClauseUnion() yyLOCAL = yyDollar[2].mergeUnion() } yyVAL.union = yyLOCAL - case 869: + case 870: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.Merge -//line mysql_sql.y:5870 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5883 { yyLOCAL = &tree.Merge{ Target: yyDollar[3].tableExprUnion(), @@ -20244,10 +20334,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 870: + case 871: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL *tree.Merge -//line mysql_sql.y:5879 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5892 { yyLOCAL = &tree.Merge{ Target: yyDollar[3].tableExprUnion(), @@ -20258,26 +20348,26 @@ yydefault: } } yyVAL.union = yyLOCAL - case 871: + case 872: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.MergeClauses -//line mysql_sql.y:5891 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5904 { yyLOCAL = tree.MergeClauses{yyDollar[1].mergeClauseUnion()} } yyVAL.union = yyLOCAL - case 872: + case 873: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.MergeClauses -//line mysql_sql.y:5895 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5908 { yyLOCAL = append(yyDollar[1].mergeClausesUnion(), yyDollar[2].mergeClauseUnion()) } yyVAL.union = yyLOCAL - case 873: + case 874: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.MergeClause -//line mysql_sql.y:5901 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5914 { yyLOCAL = &tree.MergeClause{ Matched: true, @@ -20287,10 +20377,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 874: + case 875: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.MergeClause -//line mysql_sql.y:5910 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5923 { yyLOCAL = &tree.MergeClause{ Matched: true, @@ -20299,10 +20389,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 875: + case 876: yyDollar = yyS[yypt-13 : yypt+1] var yyLOCAL *tree.MergeClause -//line mysql_sql.y:5918 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5931 { yyLOCAL = &tree.MergeClause{ Matched: false, @@ -20313,10 +20403,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 876: + case 877: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL *tree.MergeClause -//line mysql_sql.y:5928 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5941 { yyLOCAL = &tree.MergeClause{ Matched: false, @@ -20326,9 +20416,9 @@ yydefault: } } yyVAL.union = yyLOCAL - case 877: + case 878: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:5939 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5952 { if !strings.EqualFold(yyDollar[1].cstrUnion().Origin(), "matched") { yylex.Error("expected MATCHED") @@ -20336,42 +20426,42 @@ yydefault: } yyVAL.str = yyDollar[1].cstrUnion().Origin() } - case 878: + case 879: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:5948 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5961 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 879: + case 880: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:5952 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5965 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 880: + case 881: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:5958 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5971 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 881: + case 882: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:5962 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5975 { yyLOCAL = append(yyDollar[1].identifierListUnion(), tree.Identifier(yyDollar[3].str)) } yyVAL.union = yyLOCAL - case 882: + case 883: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Insert -//line mysql_sql.y:5968 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5981 { vc := tree.NewValuesClause(yyDollar[2].rowsExprsUnion()) yyLOCAL = &tree.Insert{ @@ -20379,20 +20469,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 883: + case 884: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Insert -//line mysql_sql.y:5975 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5988 { yyLOCAL = &tree.Insert{ Rows: yyDollar[1].selectUnion(), } } yyVAL.union = yyLOCAL - case 884: + case 885: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.Insert -//line mysql_sql.y:5981 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:5994 { vc := tree.NewValuesClause(yyDollar[5].rowsExprsUnion()) yyLOCAL = &tree.Insert{ @@ -20401,10 +20491,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 885: + case 886: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Insert -//line mysql_sql.y:5989 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6002 { vc := tree.NewValuesClause(yyDollar[4].rowsExprsUnion()) yyLOCAL = &tree.Insert{ @@ -20412,10 +20502,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 886: + case 887: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Insert -//line mysql_sql.y:5996 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6009 { yyLOCAL = &tree.Insert{ Columns: yyDollar[2].identifierListUnion(), @@ -20423,10 +20513,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 887: + case 888: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Insert -//line mysql_sql.y:6003 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6016 { if yyDollar[2].assignmentsUnion() == nil { yylex.Error("the set list of insert can not be empty") @@ -20445,58 +20535,58 @@ yydefault: } } yyVAL.union = yyLOCAL - case 888: + case 889: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:6022 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6035 { yyLOCAL = []*tree.UpdateExpr{} } yyVAL.union = yyLOCAL - case 889: + case 890: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:6026 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6039 { yyLOCAL = yyDollar[5].updateExprsUnion() } yyVAL.union = yyLOCAL - case 890: + case 891: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.UpdateExprs -//line mysql_sql.y:6030 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6043 { yyLOCAL = []*tree.UpdateExpr{nil} } yyVAL.union = yyLOCAL - case 891: + case 892: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.Assignment -//line mysql_sql.y:6036 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6049 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 892: + case 893: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.Assignment -//line mysql_sql.y:6040 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6053 { yyLOCAL = []*tree.Assignment{yyDollar[1].assignmentUnion()} } yyVAL.union = yyLOCAL - case 893: + case 894: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.Assignment -//line mysql_sql.y:6044 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6057 { yyLOCAL = append(yyDollar[1].assignmentsUnion(), yyDollar[3].assignmentUnion()) } yyVAL.union = yyLOCAL - case 894: + case 895: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Assignment -//line mysql_sql.y:6050 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6063 { yyLOCAL = &tree.Assignment{ Column: tree.Identifier(yyDollar[1].str), @@ -20504,195 +20594,195 @@ yydefault: } } yyVAL.union = yyLOCAL - case 895: + case 896: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6059 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6072 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[1].str)} } yyVAL.union = yyLOCAL - case 896: + case 897: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6063 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6076 { yyLOCAL = append(yyDollar[1].identifierListUnion(), tree.Identifier(yyDollar[3].str)) } yyVAL.union = yyLOCAL - case 897: + case 898: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:6069 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6082 { yyVAL.str = yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) } - case 898: + case 899: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:6073 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6086 { yyVAL.str = yylex.(*Lexer).GetDbOrTblName(yyDollar[3].cstrUnion().Origin()) } - case 899: + case 900: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.Exprs -//line mysql_sql.y:6079 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6092 { yyLOCAL = []tree.Exprs{yyDollar[1].exprsUnion()} } yyVAL.union = yyLOCAL - case 900: + case 901: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.Exprs -//line mysql_sql.y:6083 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6096 { yyLOCAL = append(yyDollar[1].rowsExprsUnion(), yyDollar[3].exprsUnion()) } yyVAL.union = yyLOCAL - case 901: + case 902: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:6089 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6102 { yyLOCAL = yyDollar[3].exprsUnion() } yyVAL.union = yyLOCAL - case 902: + case 903: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:6094 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6107 { } - case 904: + case 905: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:6098 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6111 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 906: + case 907: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:6105 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6118 { yyLOCAL = tree.Exprs{yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 907: + case 908: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:6109 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6122 { yyLOCAL = append(yyDollar[1].exprsUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 909: + case 910: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:6116 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6129 { yyLOCAL = &tree.DefaultVal{} } yyVAL.union = yyLOCAL - case 910: + case 911: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6121 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6134 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 911: + case 912: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6125 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6138 { yyLOCAL = yyDollar[3].identifierListUnion() } yyVAL.union = yyLOCAL - case 912: + case 913: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.InsertPartitionClause -//line mysql_sql.y:6130 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6143 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 913: + case 914: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.InsertPartitionClause -//line mysql_sql.y:6134 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6147 { yyLOCAL = &tree.InsertPartitionClause{Names: yyDollar[3].identifierListUnion()} } yyVAL.union = yyLOCAL - case 914: + case 915: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.InsertPartitionClause -//line mysql_sql.y:6138 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6151 { yyLOCAL = &tree.InsertPartitionClause{Values: yyDollar[3].partitionValuesUnion()} } yyVAL.union = yyLOCAL - case 915: + case 916: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.PartitionValues -//line mysql_sql.y:6144 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6157 { yyLOCAL = tree.PartitionValues{{Name: tree.Identifier(yyDollar[1].cstrUnion().Compare()), Expr: yyDollar[3].exprUnion()}} } yyVAL.union = yyLOCAL - case 916: + case 917: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.PartitionValues -//line mysql_sql.y:6148 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6161 { yyLOCAL = append(yyDollar[1].partitionValuesUnion(), tree.PartitionValue{Name: tree.Identifier(yyDollar[3].cstrUnion().Compare()), Expr: yyDollar[5].exprUnion()}) } yyVAL.union = yyLOCAL - case 917: + case 918: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6154 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6167 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[1].cstrUnion().Compare())} } yyVAL.union = yyLOCAL - case 918: + case 919: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6158 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6171 { yyLOCAL = append(yyDollar[1].identifierListUnion(), tree.Identifier(yyDollar[3].cstrUnion().Compare())) } yyVAL.union = yyLOCAL - case 919: + case 920: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:6164 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6177 { yyLOCAL = yyDollar[2].tableNameUnion() } yyVAL.union = yyLOCAL - case 920: + case 921: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:6168 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6181 { yyLOCAL = yyDollar[1].tableNameUnion() } yyVAL.union = yyLOCAL - case 921: + case 922: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ExportParam -//line mysql_sql.y:6173 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6186 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 922: + case 923: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL *tree.ExportParam -//line mysql_sql.y:6177 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6190 { yyLOCAL = &tree.ExportParam{ Outfile: true, @@ -20707,15 +20797,15 @@ yydefault: } } yyVAL.union = yyLOCAL - case 923: + case 924: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:6192 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6205 { yyVAL.str = "" } - case 924: + case 925: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:6196 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6209 { str := strings.ToLower(yyDollar[2].str) if str != "csv" && str != "jsonline" && str != "parquet" { @@ -20724,18 +20814,18 @@ yydefault: } yyVAL.str = str } - case 925: + case 926: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6206 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6219 { yyLOCAL = uint64(0) } yyVAL.union = yyLOCAL - case 926: + case 927: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6210 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6223 { size, err := util.ParseDataSize(yyDollar[2].str) if err != nil { @@ -20745,10 +20835,10 @@ yydefault: yyLOCAL = size } yyVAL.union = yyLOCAL - case 927: + case 928: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:6220 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6233 { yyLOCAL = &tree.Fields{ Terminated: &tree.Terminated{ @@ -20760,10 +20850,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 928: + case 929: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:6231 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6244 { yyLOCAL = &tree.Fields{ Terminated: &tree.Terminated{ @@ -20775,10 +20865,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 929: + case 930: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:6242 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6255 { str := yyDollar[7].str if str != "\\" && len(str) > 1 { @@ -20801,10 +20891,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 930: + case 931: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Fields -//line mysql_sql.y:6264 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6277 { str := yyDollar[4].str if str != "\\" && len(str) > 1 { @@ -20827,10 +20917,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 931: + case 932: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Lines -//line mysql_sql.y:6287 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6300 { yyLOCAL = &tree.Lines{ TerminatedBy: &tree.Terminated{ @@ -20839,10 +20929,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 932: + case 933: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Lines -//line mysql_sql.y:6295 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6308 { yyLOCAL = &tree.Lines{ TerminatedBy: &tree.Terminated{ @@ -20851,18 +20941,18 @@ yydefault: } } yyVAL.union = yyLOCAL - case 933: + case 934: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:6304 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6317 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 934: + case 935: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:6308 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6321 { str := strings.ToLower(yyDollar[2].str) if str == "true" { @@ -20875,131 +20965,131 @@ yydefault: } } yyVAL.union = yyLOCAL - case 935: + case 936: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:6321 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6334 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 936: + case 937: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:6325 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6338 { yyLOCAL = yyDollar[2].item.(int64) } yyVAL.union = yyLOCAL - case 937: + case 938: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:6330 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6343 { yyLOCAL = []string{} } yyVAL.union = yyLOCAL - case 938: + case 939: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:6334 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6347 { yyLOCAL = yyDollar[3].strsUnion() } yyVAL.union = yyLOCAL - case 939: + case 940: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:6340 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6353 { yyLOCAL = make([]string, 0, 4) yyLOCAL = append(yyLOCAL, yyDollar[1].cstrUnion().Compare()) } yyVAL.union = yyLOCAL - case 940: + case 941: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:6345 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6358 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].cstrUnion().Compare()) } yyVAL.union = yyLOCAL - case 942: + case 943: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6352 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6365 { yyLOCAL = &tree.Select{Select: yyDollar[1].selectStatementUnion()} } yyVAL.union = yyLOCAL - case 943: + case 944: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6358 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6371 { yyLOCAL = &tree.Select{Select: yyDollar[1].selectStatementUnion(), TimeWindow: yyDollar[2].timeWindowUnion(), OrderBy: yyDollar[3].orderByUnion(), Limit: yyDollar[4].limitUnion(), RankOption: yyDollar[5].rankOptionUnion(), Ep: yyDollar[6].exportParmUnion(), SelectLockInfo: yyDollar[7].selectLockInfoUnion()} } yyVAL.union = yyLOCAL - case 944: + case 945: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6362 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6375 { yyLOCAL = &tree.Select{Select: yyDollar[1].selectStatementUnion(), TimeWindow: yyDollar[2].timeWindowUnion(), OrderBy: yyDollar[3].orderByUnion(), Ep: yyDollar[4].exportParmUnion()} } yyVAL.union = yyLOCAL - case 945: + case 946: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6366 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6379 { yyLOCAL = &tree.Select{Select: yyDollar[1].selectStatementUnion(), TimeWindow: yyDollar[2].timeWindowUnion(), OrderBy: yyDollar[3].orderByUnion(), Limit: yyDollar[4].limitUnion(), RankOption: yyDollar[5].rankOptionUnion(), Ep: yyDollar[6].exportParmUnion()} } yyVAL.union = yyLOCAL - case 946: + case 947: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6370 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6383 { yyLOCAL = &tree.Select{Select: yyDollar[2].selectStatementUnion(), TimeWindow: yyDollar[3].timeWindowUnion(), OrderBy: yyDollar[4].orderByUnion(), Limit: yyDollar[5].limitUnion(), RankOption: yyDollar[6].rankOptionUnion(), Ep: yyDollar[7].exportParmUnion(), SelectLockInfo: yyDollar[8].selectLockInfoUnion(), With: yyDollar[1].withClauseUnion()} } yyVAL.union = yyLOCAL - case 947: + case 948: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6374 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6387 { yyLOCAL = &tree.Select{Select: yyDollar[2].selectStatementUnion(), OrderBy: yyDollar[3].orderByUnion(), Ep: yyDollar[4].exportParmUnion(), With: yyDollar[1].withClauseUnion()} } yyVAL.union = yyLOCAL - case 948: + case 949: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:6378 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6391 { yyLOCAL = &tree.Select{Select: yyDollar[2].selectStatementUnion(), OrderBy: yyDollar[3].orderByUnion(), Limit: yyDollar[4].limitUnion(), RankOption: yyDollar[5].rankOptionUnion(), Ep: yyDollar[6].exportParmUnion(), With: yyDollar[1].withClauseUnion()} } yyVAL.union = yyLOCAL - case 949: + case 950: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.TimeWindow -//line mysql_sql.y:6383 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6396 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 950: + case 951: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.TimeWindow -//line mysql_sql.y:6387 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6400 { yyLOCAL = yyDollar[1].timeWindowUnion() } yyVAL.union = yyLOCAL - case 951: + case 952: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.TimeWindow -//line mysql_sql.y:6393 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6406 { yyLOCAL = &tree.TimeWindow{ Interval: yyDollar[1].timeIntervalUnion(), @@ -21009,10 +21099,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 952: + case 953: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.Interval -//line mysql_sql.y:6404 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6417 { str := fmt.Sprintf("%v", yyDollar[5].item) v, errStr := util.GetInt64(yyDollar[5].item) @@ -21027,18 +21117,18 @@ yydefault: } } yyVAL.union = yyLOCAL - case 953: + case 954: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Sliding -//line mysql_sql.y:6419 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6432 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 954: + case 955: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.Sliding -//line mysql_sql.y:6423 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6436 { str := fmt.Sprintf("%v", yyDollar[3].item) v, errStr := util.GetInt64(yyDollar[3].item) @@ -21052,44 +21142,44 @@ yydefault: } } yyVAL.union = yyLOCAL - case 955: + case 956: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:6437 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6450 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 956: + case 957: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:6441 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6454 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 957: + case 958: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Fill -//line mysql_sql.y:6446 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6459 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 958: + case 959: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Fill -//line mysql_sql.y:6450 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6463 { yyLOCAL = &tree.Fill{ Mode: yyDollar[3].fillModeUnion(), } } yyVAL.union = yyLOCAL - case 959: + case 960: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.Fill -//line mysql_sql.y:6456 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6469 { yyLOCAL = &tree.Fill{ Mode: tree.FillValue, @@ -21097,50 +21187,50 @@ yydefault: } } yyVAL.union = yyLOCAL - case 960: + case 961: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FillMode -//line mysql_sql.y:6465 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6478 { yyLOCAL = tree.FillPrev } yyVAL.union = yyLOCAL - case 961: + case 962: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FillMode -//line mysql_sql.y:6469 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6482 { yyLOCAL = tree.FillNext } yyVAL.union = yyLOCAL - case 962: + case 963: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FillMode -//line mysql_sql.y:6473 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6486 { yyLOCAL = tree.FillNone } yyVAL.union = yyLOCAL - case 963: + case 964: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FillMode -//line mysql_sql.y:6477 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6490 { yyLOCAL = tree.FillNull } yyVAL.union = yyLOCAL - case 964: + case 965: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FillMode -//line mysql_sql.y:6481 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6494 { yyLOCAL = tree.FillLinear } yyVAL.union = yyLOCAL - case 965: + case 966: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.With -//line mysql_sql.y:6487 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6500 { yyLOCAL = &tree.With{ IsRecursive: false, @@ -21148,10 +21238,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 966: + case 967: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.With -//line mysql_sql.y:6494 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6507 { yyLOCAL = &tree.With{ IsRecursive: true, @@ -21159,26 +21249,26 @@ yydefault: } } yyVAL.union = yyLOCAL - case 967: + case 968: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.CTE -//line mysql_sql.y:6503 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6516 { yyLOCAL = []*tree.CTE{yyDollar[1].cteUnion()} } yyVAL.union = yyLOCAL - case 968: + case 969: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.CTE -//line mysql_sql.y:6507 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6520 { yyLOCAL = append(yyDollar[1].cteListUnion(), yyDollar[3].cteUnion()) } yyVAL.union = yyLOCAL - case 969: + case 970: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.CTE -//line mysql_sql.y:6513 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6526 { yyLOCAL = &tree.CTE{ Name: &tree.AliasClause{Alias: tree.Identifier(yyDollar[1].cstrUnion().Compare()), Cols: yyDollar[2].identifierListUnion()}, @@ -21186,74 +21276,74 @@ yydefault: } } yyVAL.union = yyLOCAL - case 970: + case 971: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6521 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6534 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 971: + case 972: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:6525 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6538 { yyLOCAL = yyDollar[2].identifierListUnion() } yyVAL.union = yyLOCAL - case 972: + case 973: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:6530 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6543 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 973: + case 974: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:6534 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6547 { yyLOCAL = yyDollar[1].limitUnion() } yyVAL.union = yyLOCAL - case 974: + case 975: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:6540 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6553 { yyLOCAL = &tree.Limit{Count: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 975: + case 976: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:6544 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6557 { yyLOCAL = &tree.Limit{Offset: yyDollar[2].exprUnion(), Count: yyDollar[4].exprUnion()} } yyVAL.union = yyLOCAL - case 976: + case 977: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Limit -//line mysql_sql.y:6548 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6561 { yyLOCAL = &tree.Limit{Offset: yyDollar[4].exprUnion(), Count: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 977: + case 978: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.RankOption -//line mysql_sql.y:6553 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6566 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 978: + case 979: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.RankOption -//line mysql_sql.y:6557 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6570 { // Parse option strings to extract key=value pairs into a map optionMap := make(map[string]string) @@ -21288,182 +21378,182 @@ yydefault: } } yyVAL.union = yyLOCAL - case 979: + case 980: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.OrderBy -//line mysql_sql.y:6592 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6605 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 980: + case 981: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.OrderBy -//line mysql_sql.y:6596 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6609 { yyLOCAL = yyDollar[1].orderByUnion() } yyVAL.union = yyLOCAL - case 981: + case 982: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.OrderBy -//line mysql_sql.y:6602 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6615 { yyLOCAL = yyDollar[3].orderByUnion() } yyVAL.union = yyLOCAL - case 982: + case 983: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.OrderBy -//line mysql_sql.y:6608 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6621 { yyLOCAL = tree.OrderBy{yyDollar[1].orderUnion()} } yyVAL.union = yyLOCAL - case 983: + case 984: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.OrderBy -//line mysql_sql.y:6612 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6625 { yyLOCAL = append(yyDollar[1].orderByUnion(), yyDollar[3].orderUnion()) } yyVAL.union = yyLOCAL - case 984: + case 985: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Order -//line mysql_sql.y:6618 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6631 { yyLOCAL = &tree.Order{Expr: yyDollar[1].exprUnion(), Direction: yyDollar[2].directionUnion(), NullsPosition: yyDollar[3].nullsPositionUnion()} } yyVAL.union = yyLOCAL - case 985: + case 986: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Direction -//line mysql_sql.y:6623 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6636 { yyLOCAL = tree.DefaultDirection } yyVAL.union = yyLOCAL - case 986: + case 987: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Direction -//line mysql_sql.y:6627 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6640 { yyLOCAL = tree.Ascending } yyVAL.union = yyLOCAL - case 987: + case 988: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Direction -//line mysql_sql.y:6631 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6644 { yyLOCAL = tree.Descending } yyVAL.union = yyLOCAL - case 988: + case 989: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.NullsPosition -//line mysql_sql.y:6636 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6649 { yyLOCAL = tree.DefaultNullsPosition } yyVAL.union = yyLOCAL - case 989: + case 990: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:6640 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6653 { yylex.Error("NULLS FIRST is not supported in MySQL syntax") return 1 } - case 990: + case 991: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:6644 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6657 { yylex.Error("NULLS LAST is not supported in MySQL syntax") return 1 } - case 991: + case 992: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.SelectLockInfo -//line mysql_sql.y:6649 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6662 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 992: + case 993: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.SelectLockInfo -//line mysql_sql.y:6653 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6666 { yyLOCAL = &tree.SelectLockInfo{ LockType: tree.SelectLockForUpdate, } } yyVAL.union = yyLOCAL - case 993: + case 994: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.SelectLockInfo -//line mysql_sql.y:6659 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6672 { yyLOCAL = &tree.SelectLockInfo{ LockType: tree.SelectLockForShare, } } yyVAL.union = yyLOCAL - case 994: + case 995: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.SelectLockInfo -//line mysql_sql.y:6665 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6678 { yyLOCAL = &tree.SelectLockInfo{ LockType: tree.SelectLockForShare, } } yyVAL.union = yyLOCAL - case 995: + case 996: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6673 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6686 { yyLOCAL = &tree.ParenSelect{Select: yyDollar[2].selectUnion()} } yyVAL.union = yyLOCAL - case 996: + case 997: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6677 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6690 { yyLOCAL = &tree.ParenSelect{Select: &tree.Select{Select: yyDollar[2].selectStatementUnion()}} } yyVAL.union = yyLOCAL - case 997: + case 998: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6681 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6694 { yyLOCAL = yyDollar[1].selectStatementUnion() } yyVAL.union = yyLOCAL - case 998: + case 999: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6685 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6698 { yyLOCAL = yyDollar[1].selectStatementUnion() } yyVAL.union = yyLOCAL - case 999: + case 1000: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6690 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6703 { yyLOCAL = yyDollar[1].selectStatementUnion() } yyVAL.union = yyLOCAL - case 1000: + case 1001: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6694 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6707 { yyLOCAL = &tree.UnionClause{ Type: yyDollar[2].unionTypeRecordUnion().Type, @@ -21474,10 +21564,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1001: + case 1002: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6704 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6717 { yyLOCAL = &tree.UnionClause{ Type: yyDollar[2].unionTypeRecordUnion().Type, @@ -21488,10 +21578,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1002: + case 1003: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6714 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6727 { yyLOCAL = &tree.UnionClause{ Type: yyDollar[2].unionTypeRecordUnion().Type, @@ -21502,10 +21592,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1003: + case 1004: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6724 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6737 { yyLOCAL = &tree.UnionClause{ Type: yyDollar[2].unionTypeRecordUnion().Type, @@ -21516,170 +21606,170 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1004: + case 1005: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6734 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6747 { yyLOCAL = &tree.UnionClause{Type: yyDollar[2].unionTypeRecordUnion().Type, Left: yyDollar[1].selectStatementUnion(), Right: yyDollar[3].selectStatementUnion(), All: yyDollar[2].unionTypeRecordUnion().All, Distinct: yyDollar[2].unionTypeRecordUnion().Distinct} } yyVAL.union = yyLOCAL - case 1005: + case 1006: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6738 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6751 { yyLOCAL = &tree.UnionClause{Type: yyDollar[2].unionTypeRecordUnion().Type, Left: yyDollar[1].selectStatementUnion(), Right: yyDollar[3].selectStatementUnion(), All: yyDollar[2].unionTypeRecordUnion().All, Distinct: yyDollar[2].unionTypeRecordUnion().Distinct} } yyVAL.union = yyLOCAL - case 1006: + case 1007: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6742 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6755 { yyLOCAL = &tree.UnionClause{Type: yyDollar[2].unionTypeRecordUnion().Type, Left: yyDollar[1].selectStatementUnion(), Right: yyDollar[3].selectStatementUnion(), All: yyDollar[2].unionTypeRecordUnion().All, Distinct: yyDollar[2].unionTypeRecordUnion().Distinct} } yyVAL.union = yyLOCAL - case 1007: + case 1008: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6746 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6759 { yyLOCAL = &tree.UnionClause{Type: yyDollar[2].unionTypeRecordUnion().Type, Left: yyDollar[1].selectStatementUnion(), Right: yyDollar[3].selectStatementUnion(), All: yyDollar[2].unionTypeRecordUnion().All, Distinct: yyDollar[2].unionTypeRecordUnion().Distinct} } yyVAL.union = yyLOCAL - case 1008: + case 1009: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6753 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6766 { yyLOCAL = &tree.ParenSelect{Select: tree.NewSelect(yyDollar[2].selectStatementUnion(), yyDollar[3].orderByUnion(), yyDollar[4].limitUnion())} } yyVAL.union = yyLOCAL - case 1009: + case 1010: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6759 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6772 { yyLOCAL = yyDollar[1].selectStatementUnion() } yyVAL.union = yyLOCAL - case 1010: + case 1011: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6763 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6776 { yyLOCAL = &tree.UnionClause{Type: yyDollar[2].unionTypeRecordUnion().Type, Left: yyDollar[1].selectStatementUnion(), Right: yyDollar[3].selectStatementUnion(), All: yyDollar[2].unionTypeRecordUnion().All, Distinct: yyDollar[2].unionTypeRecordUnion().Distinct} } yyVAL.union = yyLOCAL - case 1011: + case 1012: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6769 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6782 { yyLOCAL = makeSelectStarFromTable(yyDollar[2].tableNameUnion()) } yyVAL.union = yyLOCAL - case 1012: + case 1013: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6774 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6787 { yyLOCAL = yyDollar[1].selectStatementUnion() } yyVAL.union = yyLOCAL - case 1013: + case 1014: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6778 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6791 { yyLOCAL = yyDollar[1].selectStatementUnion() } yyVAL.union = yyLOCAL - case 1014: + case 1015: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6782 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6795 { yyLOCAL = yyDollar[1].selectStatementUnion() } yyVAL.union = yyLOCAL - case 1015: + case 1016: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6786 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6799 { yyLOCAL = &tree.ValuesClause{Rows: yyDollar[2].rowsExprsUnion(), RowWord: true} } yyVAL.union = yyLOCAL - case 1016: + case 1017: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6792 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6805 { yyLOCAL = &tree.ParenSelect{Select: tree.NewSelect(yyDollar[2].selectStatementUnion(), yyDollar[3].orderByUnion(), yyDollar[4].limitUnion())} } yyVAL.union = yyLOCAL - case 1017: + case 1018: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6798 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6811 { yyLOCAL = yyDollar[1].selectStatementUnion() } yyVAL.union = yyLOCAL - case 1018: + case 1019: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6802 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6815 { yyLOCAL = &tree.UnionClause{Type: yyDollar[2].unionTypeRecordUnion().Type, Left: yyDollar[1].selectStatementUnion(), Right: yyDollar[3].selectStatementUnion(), All: yyDollar[2].unionTypeRecordUnion().All, Distinct: yyDollar[2].unionTypeRecordUnion().Distinct} } yyVAL.union = yyLOCAL - case 1019: + case 1020: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6808 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6821 { yyLOCAL = &tree.ValuesClause{Rows: yyDollar[2].rowsExprsUnion(), RowWord: true} } yyVAL.union = yyLOCAL - case 1020: + case 1021: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6814 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6827 { yyLOCAL = yyDollar[1].selectStatementUnion() } yyVAL.union = yyLOCAL - case 1021: + case 1022: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6818 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6831 { yyLOCAL = yyDollar[1].selectStatementUnion() } yyVAL.union = yyLOCAL - case 1022: + case 1023: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6822 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6835 { yyLOCAL = yyDollar[1].selectStatementUnion() } yyVAL.union = yyLOCAL - case 1023: + case 1024: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6826 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6839 { yyLOCAL = yyDollar[1].selectStatementUnion() } yyVAL.union = yyLOCAL - case 1024: + case 1025: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:6832 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6845 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.UNION, @@ -21688,10 +21778,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1025: + case 1026: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:6840 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6853 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.UNION, @@ -21700,10 +21790,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1026: + case 1027: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:6848 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6861 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.UNION, @@ -21712,10 +21802,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1027: + case 1028: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:6857 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6870 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.EXCEPT, @@ -21724,10 +21814,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1028: + case 1029: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:6865 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6878 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.EXCEPT, @@ -21736,10 +21826,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1029: + case 1030: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:6873 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6886 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.EXCEPT, @@ -21748,10 +21838,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1030: + case 1031: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:6881 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6894 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.INTERSECT, @@ -21760,10 +21850,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1031: + case 1032: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:6889 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6902 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.INTERSECT, @@ -21772,10 +21862,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1032: + case 1033: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:6897 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6910 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.INTERSECT, @@ -21784,10 +21874,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1033: + case 1034: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:6905 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6918 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.UT_MINUS, @@ -21796,10 +21886,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1034: + case 1035: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:6913 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6926 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.UT_MINUS, @@ -21808,10 +21898,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1035: + case 1036: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UnionTypeRecord -//line mysql_sql.y:6921 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6934 { yyLOCAL = &tree.UnionTypeRecord{ Type: tree.UT_MINUS, @@ -21820,10 +21910,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1036: + case 1037: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.SelectStatement -//line mysql_sql.y:6931 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6944 { yyLOCAL = &tree.SelectClause{ Distinct: tree.QuerySpecOptionDistinct&yyDollar[2].selectOptionsUnion() != 0, @@ -21836,146 +21926,146 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1037: + case 1038: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6945 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6958 { yyLOCAL = tree.QuerySpecOptionNone } yyVAL.union = yyLOCAL - case 1038: + case 1039: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6949 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6962 { yyLOCAL = yyDollar[1].selectOptionsUnion() } yyVAL.union = yyLOCAL - case 1039: + case 1040: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6955 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6968 { yyLOCAL = yyDollar[1].selectOptionUnion() } yyVAL.union = yyLOCAL - case 1040: + case 1041: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6959 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6972 { yyLOCAL = yyDollar[1].selectOptionsUnion() | yyDollar[2].selectOptionUnion() } yyVAL.union = yyLOCAL - case 1041: + case 1042: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6965 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6978 { yyLOCAL = tree.QuerySpecOptionSqlSmallResult } yyVAL.union = yyLOCAL - case 1042: + case 1043: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6969 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6982 { yyLOCAL = tree.QuerySpecOptionSqlBigResult } yyVAL.union = yyLOCAL - case 1043: + case 1044: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6973 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6986 { yyLOCAL = tree.QuerySpecOptionSqlBufferResult } yyVAL.union = yyLOCAL - case 1044: + case 1045: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6977 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6990 { yyLOCAL = tree.QuerySpecOptionStraightJoin } yyVAL.union = yyLOCAL - case 1045: + case 1046: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6981 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6994 { yyLOCAL = tree.QuerySpecOptionHighPriority } yyVAL.union = yyLOCAL - case 1046: + case 1047: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6985 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:6998 { yyLOCAL = tree.QuerySpecOptionSqlCalcFoundRows } yyVAL.union = yyLOCAL - case 1047: + case 1048: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6989 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7002 { yyLOCAL = tree.QuerySpecOptionSqlNoCache } yyVAL.union = yyLOCAL - case 1048: + case 1049: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6993 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7006 { yyLOCAL = tree.QuerySpecOptionAll } yyVAL.union = yyLOCAL - case 1049: + case 1050: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:6997 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7010 { yyLOCAL = tree.QuerySpecOptionDistinct } yyVAL.union = yyLOCAL - case 1050: + case 1051: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL uint64 -//line mysql_sql.y:7001 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7014 { yyLOCAL = tree.QuerySpecOptionDistinctRow } yyVAL.union = yyLOCAL - case 1051: + case 1052: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Where -//line mysql_sql.y:7023 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7036 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1052: + case 1053: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Where -//line mysql_sql.y:7027 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7040 { yyLOCAL = &tree.Where{Type: tree.AstHaving, Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1053: + case 1054: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.GroupByClause -//line mysql_sql.y:7032 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7045 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1054: + case 1055: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.GroupByClause -//line mysql_sql.y:7036 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7049 { exprsList := []tree.Exprs{yyDollar[3].exprsUnion()} yyLOCAL = &tree.GroupByClause{ @@ -21986,10 +22076,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1055: + case 1056: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.GroupByClause -//line mysql_sql.y:7046 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7059 { yyLOCAL = &tree.GroupByClause{ GroupByExprsList: yyDollar[6].rowsExprsUnion(), @@ -21999,10 +22089,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1056: + case 1057: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.GroupByClause -//line mysql_sql.y:7055 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7068 { yyLOCAL = &tree.GroupByClause{ GroupByExprsList: []tree.Exprs{yyDollar[5].exprsUnion()}, @@ -22012,10 +22102,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1057: + case 1058: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.GroupByClause -//line mysql_sql.y:7064 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7077 { yyLOCAL = &tree.GroupByClause{ GroupByExprsList: []tree.Exprs{yyDollar[5].exprsUnion()}, @@ -22025,106 +22115,106 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1058: + case 1059: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.Exprs -//line mysql_sql.y:7075 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7088 { yyLOCAL = []tree.Exprs{yyDollar[2].exprsUnion()} } yyVAL.union = yyLOCAL - case 1059: + case 1060: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL []tree.Exprs -//line mysql_sql.y:7079 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7092 { yyLOCAL = append(yyDollar[1].rowsExprsUnion(), yyDollar[4].exprsUnion()) } yyVAL.union = yyLOCAL - case 1060: + case 1061: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:7085 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7098 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1061: + case 1062: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:7089 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7102 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1062: + case 1063: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.Where -//line mysql_sql.y:7094 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7107 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1063: + case 1064: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.Where -//line mysql_sql.y:7098 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7111 { yyLOCAL = &tree.Where{Type: tree.AstWhere, Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1064: + case 1065: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectExprs -//line mysql_sql.y:7104 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7117 { yyLOCAL = tree.SelectExprs{yyDollar[1].selectExprUnion()} } yyVAL.union = yyLOCAL - case 1065: + case 1066: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectExprs -//line mysql_sql.y:7108 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7121 { yyLOCAL = append(yyDollar[1].selectExprsUnion(), yyDollar[3].selectExprUnion()) } yyVAL.union = yyLOCAL - case 1066: + case 1067: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.SelectExpr -//line mysql_sql.y:7114 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7127 { yyLOCAL = tree.SelectExpr{Expr: tree.StarExpr()} } yyVAL.union = yyLOCAL - case 1067: + case 1068: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.SelectExpr -//line mysql_sql.y:7118 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7131 { yyLOCAL = tree.SelectExpr{Expr: yyDollar[1].exprUnion(), As: yyDollar[2].cstrUnion()} } yyVAL.union = yyLOCAL - case 1068: + case 1069: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.SelectExpr -//line mysql_sql.y:7122 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7135 { yyLOCAL = tree.SelectExpr{Expr: tree.NewUnresolvedNameWithStar(yyDollar[1].cstrUnion())} } yyVAL.union = yyLOCAL - case 1069: + case 1070: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.SelectExpr -//line mysql_sql.y:7126 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7139 { yyLOCAL = tree.SelectExpr{Expr: tree.NewUnresolvedNameWithStar(yyDollar[1].cstrUnion(), yyDollar[3].cstrUnion())} } yyVAL.union = yyLOCAL - case 1070: + case 1071: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.From -//line mysql_sql.y:7131 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7144 { prefix := tree.ObjectNamePrefix{ExplicitSchema: false} tn := tree.NewTableName(tree.Identifier(""), prefix, nil) @@ -22133,28 +22223,28 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1071: + case 1072: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.From -//line mysql_sql.y:7139 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7152 { yyLOCAL = yyDollar[1].fromUnion() } yyVAL.union = yyLOCAL - case 1072: + case 1073: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.From -//line mysql_sql.y:7145 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7158 { yyLOCAL = &tree.From{ Tables: tree.TableExprs{yyDollar[2].tableExprUnion()}, } } yyVAL.union = yyLOCAL - case 1073: + case 1074: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7153 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7166 { if t, ok := yyDollar[1].tableExprUnion().(*tree.JoinTableExpr); ok { yyLOCAL = t @@ -22165,18 +22255,18 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1074: + case 1075: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7163 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7176 { yyLOCAL = &tree.JoinTableExpr{Left: yyDollar[1].tableExprUnion(), Right: yyDollar[3].tableExprUnion(), JoinType: tree.JOIN_TYPE_CROSS} } yyVAL.union = yyLOCAL - case 1076: + case 1077: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7170 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7183 { if !strings.EqualFold(yyDollar[2].str, "OJ") { yylex.Error("expected OJ in table reference escape") @@ -22185,26 +22275,26 @@ yydefault: yyLOCAL = yyDollar[3].tableExprUnion() } yyVAL.union = yyLOCAL - case 1078: + case 1079: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7181 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7194 { yyLOCAL = yyDollar[1].joinTableExprUnion() } yyVAL.union = yyLOCAL - case 1079: + case 1080: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7185 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7198 { yyLOCAL = yyDollar[1].applyTableExprUnion() } yyVAL.union = yyLOCAL - case 1080: + case 1081: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.JoinTableExpr -//line mysql_sql.y:7191 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7204 { if strings.Contains(yyDollar[2].str, ":") { ss := strings.SplitN(yyDollar[2].str, ":", 2) @@ -22225,10 +22315,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1081: + case 1082: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.JoinTableExpr -//line mysql_sql.y:7211 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7224 { yyLOCAL = &tree.JoinTableExpr{ Left: yyDollar[1].tableExprUnion(), @@ -22238,10 +22328,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1082: + case 1083: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.JoinTableExpr -//line mysql_sql.y:7220 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7233 { if yyDollar[4].joinCondUnion() == nil { yylex.Error("outer join requires ON/USING clause") @@ -22255,10 +22345,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1083: + case 1084: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.JoinTableExpr -//line mysql_sql.y:7233 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7246 { yyLOCAL = &tree.JoinTableExpr{ Left: yyDollar[1].tableExprUnion(), @@ -22267,10 +22357,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1084: + case 1085: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.JoinTableExpr -//line mysql_sql.y:7241 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7254 { yyLOCAL = &tree.JoinTableExpr{ Left: yyDollar[1].tableExprUnion(), @@ -22280,10 +22370,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1085: + case 1086: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ApplyTableExpr -//line mysql_sql.y:7252 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7265 { yyLOCAL = &tree.ApplyTableExpr{ Left: yyDollar[1].tableExprUnion(), @@ -22292,27 +22382,27 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1086: + case 1087: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7262 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7275 { yyVAL.str = tree.APPLY_TYPE_CROSS } - case 1087: + case 1088: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7266 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7279 { yyVAL.str = tree.APPLY_TYPE_OUTER } - case 1088: + case 1089: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7272 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7285 { yyVAL.str = tree.JOIN_TYPE_NATURAL } - case 1089: + case 1090: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7276 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7289 { switch yyDollar[2].str { case tree.JOIN_TYPE_LEFT: @@ -22323,52 +22413,52 @@ yydefault: yyVAL.str = tree.JOIN_TYPE_NATURAL_FULL } } - case 1090: + case 1091: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7289 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7302 { yyVAL.str = tree.JOIN_TYPE_LEFT } - case 1091: + case 1092: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:7293 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7306 { yyVAL.str = tree.JOIN_TYPE_LEFT } - case 1092: + case 1093: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7297 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7310 { yyVAL.str = tree.JOIN_TYPE_RIGHT } - case 1093: + case 1094: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:7301 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7314 { yyVAL.str = tree.JOIN_TYPE_RIGHT } - case 1094: + case 1095: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7305 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7318 { yyVAL.str = tree.JOIN_TYPE_FULL } - case 1095: + case 1096: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:7309 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7322 { yyVAL.str = tree.JOIN_TYPE_FULL } - case 1096: + case 1097: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7315 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7328 { yyVAL.str = tree.JOIN_TYPE_DEDUP } - case 1097: + case 1098: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:7321 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7334 { yyLOCAL = &tree.ValuesStatement{ Rows: yyDollar[2].rowsExprsUnion(), @@ -22377,132 +22467,132 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1098: + case 1099: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.Exprs -//line mysql_sql.y:7331 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7344 { yyLOCAL = []tree.Exprs{yyDollar[1].exprsUnion()} } yyVAL.union = yyLOCAL - case 1099: + case 1100: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.Exprs -//line mysql_sql.y:7335 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7348 { yyLOCAL = append(yyDollar[1].rowsExprsUnion(), yyDollar[3].exprsUnion()) } yyVAL.union = yyLOCAL - case 1100: + case 1101: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:7341 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7354 { yyLOCAL = yyDollar[3].exprsUnion() } yyVAL.union = yyLOCAL - case 1101: + case 1102: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:7347 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7360 { yyVAL.str = yyDollar[1].str } - case 1102: + case 1103: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:7353 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7366 { yyVAL.str = yyDollar[2].str } - case 1103: + case 1104: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:7359 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7372 { yyVAL.str = tree.JOIN_TYPE_STRAIGHT } - case 1104: + case 1105: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:7365 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7378 { yyVAL.str = tree.JOIN_TYPE_INNER } - case 1105: + case 1106: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7369 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7382 { yyVAL.str = tree.JOIN_TYPE_INNER } - case 1106: + case 1107: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7373 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7386 { yyVAL.str = tree.JOIN_TYPE_CROSS } - case 1107: + case 1108: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:7377 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7390 { yyVAL.str = tree.JOIN_TYPE_CENTROIDX + ":" + yyDollar[2].str } - case 1108: + case 1109: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.JoinCond -//line mysql_sql.y:7383 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7396 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1109: + case 1110: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.JoinCond -//line mysql_sql.y:7387 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7400 { yyLOCAL = yyDollar[1].joinCondUnion() } yyVAL.union = yyLOCAL - case 1110: + case 1111: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.JoinCond -//line mysql_sql.y:7393 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7406 { yyLOCAL = &tree.OnJoinCond{Expr: yyDollar[2].exprUnion()} } yyVAL.union = yyLOCAL - case 1111: + case 1112: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.JoinCond -//line mysql_sql.y:7397 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7410 { yyLOCAL = &tree.UsingJoinCond{Cols: yyDollar[3].identifierListUnion()} } yyVAL.union = yyLOCAL - case 1112: + case 1113: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:7403 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7416 { yyLOCAL = tree.IdentifierList{tree.Identifier(yyDollar[1].cstrUnion().Compare())} } yyVAL.union = yyLOCAL - case 1113: + case 1114: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:7407 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7420 { yyLOCAL = append(yyDollar[1].identifierListUnion(), tree.Identifier(yyDollar[3].cstrUnion().Compare())) } yyVAL.union = yyLOCAL - case 1114: + case 1115: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7413 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7426 { yyLOCAL = yyDollar[1].aliasedTableExprUnion() } yyVAL.union = yyLOCAL - case 1115: + case 1116: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7417 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7430 { yyLOCAL = &tree.AliasedTableExpr{ Expr: yyDollar[1].parenTableExprUnion(), @@ -22513,10 +22603,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1116: + case 1117: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7427 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7440 { if yyDollar[2].str != "" { yyLOCAL = &tree.AliasedTableExpr{ @@ -22530,26 +22620,26 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1117: + case 1118: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7440 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7453 { yyLOCAL = yyDollar[2].tableExprUnion() } yyVAL.union = yyLOCAL - case 1118: + case 1119: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.ParenTableExpr -//line mysql_sql.y:7446 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7459 { yyLOCAL = &tree.ParenTableExpr{Expr: yyDollar[1].selectStatementUnion().(*tree.ParenSelect).Select} } yyVAL.union = yyLOCAL - case 1119: + case 1120: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableExpr -//line mysql_sql.y:7452 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7465 { name := tree.NewUnresolvedName(yyDollar[1].cstrUnion()) yyLOCAL = &tree.TableFunction{ @@ -22562,10 +22652,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1120: + case 1121: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.AliasedTableExpr -//line mysql_sql.y:7466 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7479 { yyLOCAL = &tree.AliasedTableExpr{ Expr: yyDollar[1].tableNameUnion(), @@ -22576,34 +22666,34 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1121: + case 1122: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.IndexHint -//line mysql_sql.y:7477 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7490 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1123: + case 1124: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.IndexHint -//line mysql_sql.y:7484 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7497 { yyLOCAL = []*tree.IndexHint{yyDollar[1].indexHintUnion()} } yyVAL.union = yyLOCAL - case 1124: + case 1125: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.IndexHint -//line mysql_sql.y:7488 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7501 { yyLOCAL = append(yyDollar[1].indexHintListUnion(), yyDollar[2].indexHintUnion()) } yyVAL.union = yyLOCAL - case 1125: + case 1126: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.IndexHint -//line mysql_sql.y:7494 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7507 { yyLOCAL = &tree.IndexHint{ IndexNames: yyDollar[4].strsUnion(), @@ -22612,182 +22702,182 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1126: + case 1127: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexHintType -//line mysql_sql.y:7504 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7517 { yyLOCAL = tree.HintUse } yyVAL.union = yyLOCAL - case 1127: + case 1128: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexHintType -//line mysql_sql.y:7508 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7521 { yyLOCAL = tree.HintIgnore } yyVAL.union = yyLOCAL - case 1128: + case 1129: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexHintType -//line mysql_sql.y:7512 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7525 { yyLOCAL = tree.HintForce } yyVAL.union = yyLOCAL - case 1129: + case 1130: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IndexHintScope -//line mysql_sql.y:7517 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7530 { yyLOCAL = tree.HintForScan } yyVAL.union = yyLOCAL - case 1130: + case 1131: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexHintScope -//line mysql_sql.y:7521 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7534 { yyLOCAL = tree.HintForJoin } yyVAL.union = yyLOCAL - case 1131: + case 1132: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IndexHintScope -//line mysql_sql.y:7525 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7538 { yyLOCAL = tree.HintForOrderBy } yyVAL.union = yyLOCAL - case 1132: + case 1133: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IndexHintScope -//line mysql_sql.y:7529 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7542 { yyLOCAL = tree.HintForGroupBy } yyVAL.union = yyLOCAL - case 1133: + case 1134: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:7534 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7547 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1134: + case 1135: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:7538 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7551 { yyLOCAL = []string{yyDollar[1].cstrUnion().Compare()} } yyVAL.union = yyLOCAL - case 1135: + case 1136: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:7542 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7555 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].cstrUnion().Compare()) } yyVAL.union = yyLOCAL - case 1136: + case 1137: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:7546 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7559 { yyLOCAL = []string{yyDollar[1].str} } yyVAL.union = yyLOCAL - case 1137: + case 1138: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:7550 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7563 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1138: + case 1139: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:7556 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7569 { yyVAL.str = "" } - case 1139: + case 1140: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:7560 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7573 { yyVAL.str = yyDollar[1].str } - case 1140: + case 1141: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7564 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7577 { yyVAL.str = yyDollar[2].str } - case 1141: + case 1142: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:7570 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7583 { yyVAL.str = yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) } - case 1142: + case 1143: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:7574 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7587 { yyVAL.str = yylex.(*Lexer).GetDbOrTblName(yyDollar[1].str) } - case 1143: + case 1144: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:7580 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7593 { yyLOCAL = tree.NewCStr("", 1) } yyVAL.union = yyLOCAL - case 1144: + case 1145: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:7584 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7597 { yyLOCAL = yyDollar[1].cstrUnion() } yyVAL.union = yyLOCAL - case 1145: + case 1146: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:7588 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7601 { yyLOCAL = yyDollar[2].cstrUnion() } yyVAL.union = yyLOCAL - case 1146: + case 1147: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:7592 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7605 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1147: + case 1148: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:7596 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7609 { yyLOCAL = tree.NewCStr(yyDollar[2].str, 1) } yyVAL.union = yyLOCAL - case 1148: + case 1149: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:7602 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7615 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1172: + case 1173: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:7645 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7658 { yyLOCAL = &tree.CreateIcebergCatalog{ IfNotExists: yyDollar[4].ifNotExistsUnion(), @@ -22796,10 +22886,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1173: + case 1174: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:7655 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7668 { yyLOCAL = &tree.CreateMongoDBConnection{ IfNotExists: yyDollar[4].ifNotExistsUnion(), @@ -22808,10 +22898,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1174: + case 1175: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:7665 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7678 { cronExpr := "" timezone := "" @@ -22831,18 +22921,18 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1175: + case 1176: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.SQLTaskSchedule -//line mysql_sql.y:7685 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7698 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1176: + case 1177: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.SQLTaskSchedule -//line mysql_sql.y:7689 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7702 { yyLOCAL = &tree.SQLTaskSchedule{ CronExpr: yyDollar[2].str, @@ -22850,82 +22940,82 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1177: + case 1178: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:7697 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7710 { yyVAL.str = "" } - case 1178: + case 1179: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7701 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7714 { yyVAL.str = yyDollar[2].str } - case 1179: + case 1180: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:7706 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7719 { yyLOCAL = tree.Expr(nil) } yyVAL.union = yyLOCAL - case 1180: + case 1181: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:7710 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7723 { yyLOCAL = yyDollar[3].exprUnion() } yyVAL.union = yyLOCAL - case 1181: + case 1182: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:7716 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7729 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1182: + case 1183: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:7720 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7733 { yyLOCAL = tree.NewSubquery(yyDollar[1].selectUnion(), false) } yyVAL.union = yyLOCAL - case 1183: + case 1184: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:7725 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7738 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 1184: + case 1185: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:7729 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7742 { yyLOCAL = sqlTaskInt64(yyDollar[2].item) } yyVAL.union = yyLOCAL - case 1185: + case 1186: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:7734 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7747 { yyVAL.str = "" } - case 1186: + case 1187: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7738 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7751 { yyVAL.str = yyDollar[2].str } - case 1187: + case 1188: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:7744 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7757 { var Language = yyDollar[3].str var Name = tree.Identifier(yyDollar[5].str) @@ -22937,135 +23027,135 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1188: + case 1189: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:7757 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7770 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1189: + case 1190: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:7763 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7776 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1190: + case 1191: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:7769 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7782 { yyLOCAL = tree.NewCreateProcedure( yyDollar[2].sourceOptionalUnion(), yyDollar[4].procNameUnion(), yyDollar[6].procArgsUnion(), yyDollar[8].str, yyDollar[9].str, ) } yyVAL.union = yyLOCAL - case 1191: + case 1192: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.ProcedureName -//line mysql_sql.y:7777 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7790 { prefix := tree.ObjectNamePrefix{ExplicitSchema: false} yyLOCAL = tree.NewProcedureName(tree.Identifier(yyDollar[1].cstrUnion().Compare()), prefix) } yyVAL.union = yyLOCAL - case 1192: + case 1193: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ProcedureName -//line mysql_sql.y:7782 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7795 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) prefix := tree.ObjectNamePrefix{SchemaName: tree.Identifier(dbName), ExplicitSchema: true} yyLOCAL = tree.NewProcedureName(tree.Identifier(yyDollar[3].cstrUnion().Compare()), prefix) } yyVAL.union = yyLOCAL - case 1193: + case 1194: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.ProcedureArgs -//line mysql_sql.y:7789 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7802 { yyLOCAL = tree.ProcedureArgs(nil) } yyVAL.union = yyLOCAL - case 1195: + case 1196: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ProcedureArgs -//line mysql_sql.y:7796 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7809 { yyLOCAL = tree.ProcedureArgs{yyDollar[1].procArgUnion()} } yyVAL.union = yyLOCAL - case 1196: + case 1197: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ProcedureArgs -//line mysql_sql.y:7800 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7813 { yyLOCAL = append(yyDollar[1].procArgsUnion(), yyDollar[3].procArgUnion()) } yyVAL.union = yyLOCAL - case 1197: + case 1198: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ProcedureArg -//line mysql_sql.y:7806 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7819 { yyLOCAL = tree.ProcedureArg(yyDollar[1].procArgDeclUnion()) } yyVAL.union = yyLOCAL - case 1198: + case 1199: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ProcedureArgDecl -//line mysql_sql.y:7812 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7825 { yyLOCAL = tree.NewProcedureArgDecl(yyDollar[1].procArgTypeUnion(), yyDollar[2].unresolvedNameUnion(), yyDollar[3].columnTypeUnion()) } yyVAL.union = yyLOCAL - case 1199: + case 1200: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.InOutArgType -//line mysql_sql.y:7817 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7830 { yyLOCAL = tree.TYPE_IN } yyVAL.union = yyLOCAL - case 1200: + case 1201: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.InOutArgType -//line mysql_sql.y:7821 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7834 { yyLOCAL = tree.TYPE_IN } yyVAL.union = yyLOCAL - case 1201: + case 1202: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.InOutArgType -//line mysql_sql.y:7825 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7838 { yyLOCAL = tree.TYPE_OUT } yyVAL.union = yyLOCAL - case 1202: + case 1203: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.InOutArgType -//line mysql_sql.y:7829 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7842 { yyLOCAL = tree.TYPE_INOUT } yyVAL.union = yyLOCAL - case 1203: + case 1204: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:7834 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7847 { yyVAL.str = "sql" } - case 1204: + case 1205: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7838 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7851 { yyVAL.str = yyDollar[2].str } - case 1205: + case 1206: yyDollar = yyS[yypt-14 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:7844 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7857 { if yyDollar[13].str == "" { yylex.Error("no function body error") @@ -23097,127 +23187,127 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1206: + case 1207: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.FunctionName -//line mysql_sql.y:7877 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7890 { prefix := tree.ObjectNamePrefix{ExplicitSchema: false} yyLOCAL = tree.NewFuncName(tree.Identifier(yyDollar[1].cstrUnion().Compare()), prefix) } yyVAL.union = yyLOCAL - case 1207: + case 1208: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.FunctionName -//line mysql_sql.y:7882 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7895 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) prefix := tree.ObjectNamePrefix{SchemaName: tree.Identifier(dbName), ExplicitSchema: true} yyLOCAL = tree.NewFuncName(tree.Identifier(yyDollar[3].cstrUnion().Compare()), prefix) } yyVAL.union = yyLOCAL - case 1208: + case 1209: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.FunctionArgs -//line mysql_sql.y:7889 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7902 { yyLOCAL = tree.FunctionArgs(nil) } yyVAL.union = yyLOCAL - case 1210: + case 1211: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FunctionArgs -//line mysql_sql.y:7896 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7909 { yyLOCAL = tree.FunctionArgs{yyDollar[1].funcArgUnion()} } yyVAL.union = yyLOCAL - case 1211: + case 1212: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.FunctionArgs -//line mysql_sql.y:7900 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7913 { yyLOCAL = append(yyDollar[1].funcArgsUnion(), yyDollar[3].funcArgUnion()) } yyVAL.union = yyLOCAL - case 1212: + case 1213: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FunctionArg -//line mysql_sql.y:7906 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7919 { yyLOCAL = tree.FunctionArg(yyDollar[1].funcArgDeclUnion()) } yyVAL.union = yyLOCAL - case 1213: + case 1214: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.FunctionArgDecl -//line mysql_sql.y:7912 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7925 { yyLOCAL = tree.NewFunctionArgDecl(nil, yyDollar[1].columnTypeUnion(), nil) } yyVAL.union = yyLOCAL - case 1214: + case 1215: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FunctionArgDecl -//line mysql_sql.y:7916 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7929 { yyLOCAL = tree.NewFunctionArgDecl(yyDollar[1].unresolvedNameUnion(), yyDollar[2].columnTypeUnion(), nil) } yyVAL.union = yyLOCAL - case 1215: + case 1216: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FunctionArgDecl -//line mysql_sql.y:7920 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7933 { yyLOCAL = tree.NewFunctionArgDecl(yyDollar[1].unresolvedNameUnion(), yyDollar[2].columnTypeUnion(), yyDollar[4].exprUnion()) } yyVAL.union = yyLOCAL - case 1216: + case 1217: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:7926 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7939 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1217: + case 1218: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.ReturnType -//line mysql_sql.y:7932 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7945 { yyLOCAL = tree.NewReturnType(yyDollar[1].columnTypeUnion()) } yyVAL.union = yyLOCAL - case 1218: + case 1219: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:7938 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7951 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1219: + case 1220: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:7942 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7955 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1220: + case 1221: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:7947 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7960 { yyVAL.str = "" } - case 1222: + case 1223: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:7954 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7967 { yyVAL.str = yyDollar[2].str } - case 1223: + case 1224: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:7960 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7973 { var Replace bool var Name = yyDollar[5].tableNameUnion() @@ -23233,10 +23323,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1224: + case 1225: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:7975 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:7988 { var Replace = yyDollar[2].sourceOptionalUnion() var Name = yyDollar[5].tableNameUnion() @@ -23252,10 +23342,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1225: + case 1226: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:7992 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8005 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Name = yyDollar[4].exprUnion() @@ -23271,10 +23361,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1226: + case 1227: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8007 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8020 { var FromUri = yyDollar[4].str var SubscriptionAccountName = yyDollar[5].cstrUnion().Compare() @@ -23292,81 +23382,81 @@ yydefault: yyLOCAL = cs } yyVAL.union = yyLOCAL - case 1227: + case 1228: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8026 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8039 { yyVAL.str = yyDollar[1].str } - case 1228: + case 1229: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:8030 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8043 { yyVAL.str = yyVAL.str + yyDollar[2].str } - case 1229: + case 1230: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:8036 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8049 { yyVAL.str = "ALGORITHM = " + yyDollar[3].str } - case 1230: + case 1231: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:8040 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8053 { yyVAL.str = "DEFINER = " } - case 1231: + case 1232: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:8044 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8057 { yyVAL.str = "SQL SECURITY " + yyDollar[3].str } - case 1232: + case 1233: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:8049 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8062 { yyVAL.str = "" } - case 1233: + case 1234: yyDollar = yyS[yypt-4 : yypt+1] -//line mysql_sql.y:8053 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8066 { yyVAL.str = "WITH " + yyDollar[2].str + " CHECK OPTION" } - case 1239: + case 1240: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:8067 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8080 { yyVAL.str = "" } - case 1242: + case 1243: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8075 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8088 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1243: + case 1244: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8081 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8094 { var str = yyDollar[1].cstrUnion().Compare() yyLOCAL = tree.NewNumVal(str, str, false, tree.P_char) } yyVAL.union = yyLOCAL - case 1244: + case 1245: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8086 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8099 { yyLOCAL = tree.NewParamExpr(yylex.(*Lexer).GetParamIndex()) } yyVAL.union = yyLOCAL - case 1245: + case 1246: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AccountAuthOption -//line mysql_sql.y:8092 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8105 { var Equal = yyDollar[2].str var AdminName = yyDollar[3].exprUnion() @@ -23378,36 +23468,36 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1246: + case 1247: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8105 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8118 { var str = yyDollar[1].str yyLOCAL = tree.NewNumVal(str, str, false, tree.P_char) } yyVAL.union = yyLOCAL - case 1247: + case 1248: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8110 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8123 { var str = yyDollar[1].cstrUnion().Compare() yyLOCAL = tree.NewNumVal(str, str, false, tree.P_char) } yyVAL.union = yyLOCAL - case 1248: + case 1249: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:8115 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8128 { yyLOCAL = tree.NewParamExpr(yylex.(*Lexer).GetParamIndex()) } yyVAL.union = yyLOCAL - case 1249: + case 1250: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AccountIdentified -//line mysql_sql.y:8121 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8134 { yyLOCAL = *tree.NewAccountIdentified( tree.AccountIdentifiedByPassword, @@ -23415,10 +23505,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1250: + case 1251: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AccountIdentified -//line mysql_sql.y:8128 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8141 { yyLOCAL = *tree.NewAccountIdentified( tree.AccountIdentifiedByPassword, @@ -23426,10 +23516,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1251: + case 1252: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.AccountIdentified -//line mysql_sql.y:8135 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8148 { yyLOCAL = *tree.NewAccountIdentified( tree.AccountIdentifiedByRandomPassword, @@ -23437,10 +23527,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1252: + case 1253: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AccountIdentified -//line mysql_sql.y:8142 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8155 { yyLOCAL = *tree.NewAccountIdentified( tree.AccountIdentifiedWithSSL, @@ -23448,10 +23538,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1253: + case 1254: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.AccountIdentified -//line mysql_sql.y:8149 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8162 { yyLOCAL = *tree.NewAccountIdentified( tree.AccountIdentifiedWithSSL, @@ -23459,20 +23549,20 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1254: + case 1255: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.AccountStatus -//line mysql_sql.y:8157 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8170 { as := tree.NewAccountStatus() as.Exist = false yyLOCAL = *as } yyVAL.union = yyLOCAL - case 1255: + case 1256: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AccountStatus -//line mysql_sql.y:8163 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8176 { as := tree.NewAccountStatus() as.Exist = true @@ -23480,10 +23570,10 @@ yydefault: yyLOCAL = *as } yyVAL.union = yyLOCAL - case 1256: + case 1257: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AccountStatus -//line mysql_sql.y:8170 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8183 { as := tree.NewAccountStatus() as.Exist = true @@ -23491,10 +23581,10 @@ yydefault: yyLOCAL = *as } yyVAL.union = yyLOCAL - case 1257: + case 1258: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.AccountStatus -//line mysql_sql.y:8177 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8190 { as := tree.NewAccountStatus() as.Exist = true @@ -23502,20 +23592,20 @@ yydefault: yyLOCAL = *as } yyVAL.union = yyLOCAL - case 1258: + case 1259: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.AccountComment -//line mysql_sql.y:8185 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8198 { ac := tree.NewAccountComment() ac.Exist = false yyLOCAL = *ac } yyVAL.union = yyLOCAL - case 1259: + case 1260: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AccountComment -//line mysql_sql.y:8191 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8204 { ac := tree.NewAccountComment() ac.Exist = true @@ -23523,10 +23613,10 @@ yydefault: yyLOCAL = *ac } yyVAL.union = yyLOCAL - case 1260: + case 1261: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8200 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8213 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Users = yyDollar[4].usersUnion() @@ -23542,10 +23632,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1261: + case 1262: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8217 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8230 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -23562,10 +23652,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1262: + case 1263: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8233 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8246 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -23583,10 +23673,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1263: + case 1264: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8250 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8263 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -23603,30 +23693,30 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1264: + case 1265: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:8268 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8281 { yyLOCAL = &tree.AccountsSetOption{ All: true, } } yyVAL.union = yyLOCAL - case 1265: + case 1266: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:8274 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8287 { yyLOCAL = &tree.AccountsSetOption{ SetAccounts: yyDollar[2].identifierListUnion(), } } yyVAL.union = yyLOCAL - case 1266: + case 1267: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8282 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8295 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -23644,20 +23734,20 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1267: + case 1268: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.StageStatus -//line mysql_sql.y:8300 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8313 { yyLOCAL = tree.StageStatus{ Exist: false, } } yyVAL.union = yyLOCAL - case 1268: + case 1269: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.StageStatus -//line mysql_sql.y:8306 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8319 { yyLOCAL = tree.StageStatus{ Exist: true, @@ -23665,10 +23755,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1269: + case 1270: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.StageStatus -//line mysql_sql.y:8313 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8326 { yyLOCAL = tree.StageStatus{ Exist: true, @@ -23676,20 +23766,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1270: + case 1271: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.StageComment -//line mysql_sql.y:8321 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8334 { yyLOCAL = tree.StageComment{ Exist: false, } } yyVAL.union = yyLOCAL - case 1271: + case 1272: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.StageComment -//line mysql_sql.y:8327 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8340 { yyLOCAL = tree.StageComment{ Exist: true, @@ -23697,18 +23787,18 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1272: + case 1273: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:8336 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8349 { yyLOCAL = int64(0) } yyVAL.union = yyLOCAL - case 1273: + case 1274: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:8340 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8353 { switch v := yyDollar[3].item.(type) { case int64: @@ -23720,20 +23810,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1274: + case 1275: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.StageUrl -//line mysql_sql.y:8352 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8365 { yyLOCAL = tree.StageUrl{ Exist: false, } } yyVAL.union = yyLOCAL - case 1275: + case 1276: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.StageUrl -//line mysql_sql.y:8358 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8371 { yyLOCAL = tree.StageUrl{ Exist: true, @@ -23741,20 +23831,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1276: + case 1277: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.StageCredentials -//line mysql_sql.y:8366 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8379 { yyLOCAL = tree.StageCredentials{ Exist: false, } } yyVAL.union = yyLOCAL - case 1277: + case 1278: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.StageCredentials -//line mysql_sql.y:8372 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8385 { yyLOCAL = tree.StageCredentials{ Exist: true, @@ -23762,61 +23852,61 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1278: + case 1279: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:8381 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8394 { yyLOCAL = yyDollar[1].strsUnion() } yyVAL.union = yyLOCAL - case 1279: + case 1280: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:8385 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8398 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].strsUnion()...) } yyVAL.union = yyLOCAL - case 1280: + case 1281: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:8390 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8403 { yyLOCAL = []string{} } yyVAL.union = yyLOCAL - case 1281: + case 1282: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:8394 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8407 { yyLOCAL = append(yyLOCAL, yyDollar[1].str) yyLOCAL = append(yyLOCAL, yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1282: + case 1283: yyDollar = yyS[yypt-3 : yypt+1] -//line mysql_sql.y:8401 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8414 { yyVAL.str = yyDollar[3].str } - case 1283: + case 1284: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:8406 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8419 { yyVAL.str = "" } - case 1284: + case 1285: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:8410 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8423 { yyVAL.str = yyDollar[2].str } - case 1285: + case 1286: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8416 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8429 { var ifNotExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -23827,10 +23917,10 @@ yydefault: yyLOCAL = tree.NewAlterStage(ifNotExists, name, urlOption, credentialsOption, statusOption, comment) } yyVAL.union = yyLOCAL - case 1286: + case 1287: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8428 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8441 { var ifExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -23841,154 +23931,154 @@ yydefault: yyLOCAL = tree.NewAlterPublication(ifExists, name, accountsSet, dbName, table, comment) } yyVAL.union = yyLOCAL - case 1287: + case 1288: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:8439 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8452 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1288: + case 1289: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:8443 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8456 { yyLOCAL = &tree.AccountsSetOption{ All: true, } } yyVAL.union = yyLOCAL - case 1289: + case 1290: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:8449 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8462 { yyLOCAL = &tree.AccountsSetOption{ SetAccounts: yyDollar[2].identifierListUnion(), } } yyVAL.union = yyLOCAL - case 1290: + case 1291: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:8455 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8468 { yyLOCAL = &tree.AccountsSetOption{ AddAccounts: yyDollar[3].identifierListUnion(), } } yyVAL.union = yyLOCAL - case 1291: + case 1292: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.AccountsSetOption -//line mysql_sql.y:8461 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8474 { yyLOCAL = &tree.AccountsSetOption{ DropAccounts: yyDollar[3].identifierListUnion(), } } yyVAL.union = yyLOCAL - case 1292: + case 1293: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:8468 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8481 { yyVAL.str = "" } - case 1293: + case 1294: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:8472 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8485 { yyVAL.str = yyDollar[2].str } - case 1294: + case 1295: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.TableNames -//line mysql_sql.y:8477 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8490 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1295: + case 1296: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableNames -//line mysql_sql.y:8481 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8494 { yyLOCAL = yyDollar[2].tableNamesUnion() } yyVAL.union = yyLOCAL - case 1296: + case 1297: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8487 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8500 { var ifExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) yyLOCAL = tree.NewDropPublication(ifExists, name) } yyVAL.union = yyLOCAL - case 1297: + case 1298: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8495 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8508 { var ifExists = yyDollar[4].boolValUnion() var taskID = yyDollar[5].str yyLOCAL = tree.NewDropCcprSubscription(ifExists, taskID) } yyVAL.union = yyLOCAL - case 1298: + case 1299: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8503 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8516 { var taskID = yyDollar[4].str yyLOCAL = tree.NewResumeCcprSubscription(taskID) } yyVAL.union = yyLOCAL - case 1299: + case 1300: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8510 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8523 { var taskID = yyDollar[4].str yyLOCAL = tree.NewPauseCcprSubscription(taskID) } yyVAL.union = yyLOCAL - case 1300: + case 1301: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8517 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8530 { var ifNotExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) yyLOCAL = tree.NewDropStage(ifNotExists, name) } yyVAL.union = yyLOCAL - case 1301: + case 1302: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8525 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8538 { var ifExists = yyDollar[5].boolValUnion() var path = yyDollar[6].str yyLOCAL = tree.NewRemoveStageFiles(ifExists, path) } yyVAL.union = yyLOCAL - case 1302: + case 1303: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8533 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8546 { var ifExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) yyLOCAL = tree.NewDropSnapShot(ifExists, name, "", "") } yyVAL.union = yyLOCAL - case 1303: + case 1304: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8539 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8552 { var ifExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -23997,10 +24087,10 @@ yydefault: yyLOCAL = tree.NewDropSnapShot(ifExists, name, accountName, pubName) } yyVAL.union = yyLOCAL - case 1304: + case 1305: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8549 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8562 { var ifExists = yyDollar[3].boolValUnion() var name = tree.Identifier(yyDollar[4].cstrUnion().Compare()) @@ -24012,16 +24102,16 @@ yydefault: } yyVAL.union = yyLOCAL - case 1305: + case 1306: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8562 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8575 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1306: + case 1307: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.AccountCommentOrAttribute -//line mysql_sql.y:8567 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8580 { var Exist = false var IsComment bool @@ -24034,10 +24124,10 @@ yydefault: } yyVAL.union = yyLOCAL - case 1307: + case 1308: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AccountCommentOrAttribute -//line mysql_sql.y:8579 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8592 { var Exist = true var IsComment = true @@ -24049,10 +24139,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1308: + case 1309: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.AccountCommentOrAttribute -//line mysql_sql.y:8590 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8603 { var Exist = true var IsComment = false @@ -24064,26 +24154,26 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1309: + case 1310: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.User -//line mysql_sql.y:8698 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8711 { yyLOCAL = []*tree.User{yyDollar[1].userUnion()} } yyVAL.union = yyLOCAL - case 1310: + case 1311: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.User -//line mysql_sql.y:8702 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8715 { yyLOCAL = append(yyDollar[1].usersUnion(), yyDollar[3].userUnion()) } yyVAL.union = yyLOCAL - case 1311: + case 1312: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.User -//line mysql_sql.y:8708 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8721 { var Username = yyDollar[1].usernameRecordUnion().Username var Hostname = yyDollar[1].usernameRecordUnion().Hostname @@ -24095,26 +24185,26 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1312: + case 1313: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.User -//line mysql_sql.y:8721 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8734 { yyLOCAL = []*tree.User{yyDollar[1].userUnion()} } yyVAL.union = yyLOCAL - case 1313: + case 1314: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.User -//line mysql_sql.y:8725 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8738 { yyLOCAL = append(yyDollar[1].usersUnion(), yyDollar[3].userUnion()) } yyVAL.union = yyLOCAL - case 1314: + case 1315: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.User -//line mysql_sql.y:8731 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8744 { var Username = yyDollar[1].usernameRecordUnion().Username var Hostname = yyDollar[1].usernameRecordUnion().Hostname @@ -24126,50 +24216,50 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1315: + case 1316: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UsernameRecord -//line mysql_sql.y:8744 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8757 { yyLOCAL = &tree.UsernameRecord{Username: yyDollar[1].str, Hostname: "%"} } yyVAL.union = yyLOCAL - case 1316: + case 1317: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UsernameRecord -//line mysql_sql.y:8748 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8761 { yyLOCAL = &tree.UsernameRecord{Username: yyDollar[1].str, Hostname: yyDollar[3].str} } yyVAL.union = yyLOCAL - case 1317: + case 1318: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.UsernameRecord -//line mysql_sql.y:8752 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8765 { yyLOCAL = &tree.UsernameRecord{Username: yyDollar[1].str, Hostname: yyDollar[2].str} } yyVAL.union = yyLOCAL - case 1318: + case 1319: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.AccountIdentified -//line mysql_sql.y:8757 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8770 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1319: + case 1320: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.AccountIdentified -//line mysql_sql.y:8761 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8774 { yyLOCAL = yyDollar[1].userIdentifiedUnion() } yyVAL.union = yyLOCAL - case 1320: + case 1321: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.AccountIdentified -//line mysql_sql.y:8767 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8780 { yyLOCAL = &tree.AccountIdentified{ Typ: tree.AccountIdentifiedByPassword, @@ -24177,20 +24267,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1321: + case 1322: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.AccountIdentified -//line mysql_sql.y:8774 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8787 { yyLOCAL = &tree.AccountIdentified{ Typ: tree.AccountIdentifiedByRandomPassword, } } yyVAL.union = yyLOCAL - case 1322: + case 1323: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.AccountIdentified -//line mysql_sql.y:8780 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8793 { yyLOCAL = &tree.AccountIdentified{ Typ: tree.AccountIdentifiedWithSSL, @@ -24198,16 +24288,16 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1323: + case 1324: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:8789 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8802 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1325: + case 1326: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8796 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8809 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Roles = yyDollar[4].rolesUnion() @@ -24217,26 +24307,26 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1326: + case 1327: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.Role -//line mysql_sql.y:8807 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8820 { yyLOCAL = []*tree.Role{yyDollar[1].roleUnion()} } yyVAL.union = yyLOCAL - case 1327: + case 1328: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.Role -//line mysql_sql.y:8811 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8824 { yyLOCAL = append(yyDollar[1].rolesUnion(), yyDollar[3].roleUnion()) } yyVAL.union = yyLOCAL - case 1328: + case 1329: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Role -//line mysql_sql.y:8817 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8830 { var UserName = yyDollar[1].cstrUnion().Compare() yyLOCAL = tree.NewRole( @@ -24244,106 +24334,114 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1329: + case 1330: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8826 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8839 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1330: + case 1331: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8830 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8843 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1331: + case 1332: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8834 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8847 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1332: + case 1333: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8838 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8851 { yyLOCAL = tree.NewCStr("lag", 1) } yyVAL.union = yyLOCAL - case 1333: + case 1334: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8842 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8855 { yyLOCAL = tree.NewCStr("lead", 1) } yyVAL.union = yyLOCAL - case 1334: + case 1335: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8846 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8859 { yyLOCAL = tree.NewCStr("first_value", 1) } yyVAL.union = yyLOCAL - case 1335: + case 1336: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8850 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8863 { yyLOCAL = tree.NewCStr("last_value", 1) } yyVAL.union = yyLOCAL - case 1336: + case 1337: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:8854 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8867 { yyLOCAL = tree.NewCStr("nth_value", 1) } yyVAL.union = yyLOCAL - case 1337: + case 1338: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IndexCategory -//line mysql_sql.y:8859 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8872 { yyLOCAL = tree.INDEX_CATEGORY_NONE } yyVAL.union = yyLOCAL - case 1338: + case 1339: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IndexCategory -//line mysql_sql.y:8863 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8876 { yyLOCAL = tree.INDEX_CATEGORY_FULLTEXT } yyVAL.union = yyLOCAL - case 1339: + case 1340: + yyDollar = yyS[yypt-1 : yypt+1] + var yyLOCAL tree.IndexCategory +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8880 + { + yyLOCAL = tree.INDEX_CATEGORY_FULLTEXT2 + } + yyVAL.union = yyLOCAL + case 1341: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IndexCategory -//line mysql_sql.y:8867 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8884 { yyLOCAL = tree.INDEX_CATEGORY_SPATIAL } yyVAL.union = yyLOCAL - case 1340: + case 1342: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IndexCategory -//line mysql_sql.y:8871 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8888 { yyLOCAL = tree.INDEX_CATEGORY_UNIQUE } yyVAL.union = yyLOCAL - case 1341: + case 1343: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:8877 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8894 { var io *tree.IndexOption = nil if yyDollar[11].indexOptionUnion() == nil && yyDollar[5].indexTypeUnion() != tree.INDEX_TYPE_INVALID { @@ -24374,18 +24472,18 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1342: + case 1344: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:8908 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8925 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1343: + case 1345: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:8912 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:8929 { // Merge the options if yyDollar[1].indexOptionUnion() == nil { @@ -24415,12 +24513,16 @@ yydefault: opt1.Async = opt2.Async } else if opt2.ForceSync { opt1.ForceSync = opt2.ForceSync + } else if opt2.Merge { + opt1.Merge = opt2.Merge } else if opt2.AutoUpdate { opt1.AutoUpdate = opt2.AutoUpdate } else if opt2.Day > 0 { opt1.Day = opt2.Day } else if opt2.Hour > 0 { opt1.Hour = opt2.Hour + } else if opt2.Second > 0 { + opt1.Second = opt2.Second } else if opt2.IntermediateGraphDegree > 0 { opt1.IntermediateGraphDegree = opt2.IntermediateGraphDegree } else if opt2.GraphDegree > 0 { @@ -24441,8 +24543,13 @@ yydefault: opt1.KmeansMaxIteration = opt2.KmeansMaxIteration } else if opt2.MaxIndexCapacity > 0 { opt1.MaxIndexCapacity = opt2.MaxIndexCapacity + } else if opt2.MaxPostingsCapacity > 0 { + opt1.MaxPostingsCapacity = opt2.MaxPostingsCapacity } else if opt2.QuantizerTrainLimit > 0 { opt1.QuantizerTrainLimit = opt2.QuantizerTrainLimit + } else if opt2.PositionFreeSet { + opt1.PositionFree = opt2.PositionFree + opt1.PositionFreeSet = true } else if len(opt2.IncludeColumns) > 0 { opt1.IncludeColumns = opt2.IncludeColumns } @@ -24450,20 +24557,20 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1344: + case 1346: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:8978 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9004 { io := tree.NewIndexOption() io.KeyBlockSize = uint64(yyDollar[3].item.(int64)) yyLOCAL = io } yyVAL.union = yyLOCAL - case 1345: + case 1347: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:8984 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9010 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -24476,60 +24583,60 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1346: + case 1348: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:8996 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9022 { io := tree.NewIndexOption() io.AlgoParamVectorOpType = yyDollar[2].str yyLOCAL = io } yyVAL.union = yyLOCAL - case 1347: + case 1349: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9002 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9028 { io := tree.NewIndexOption() io.Comment = yyDollar[2].str yyLOCAL = io } yyVAL.union = yyLOCAL - case 1348: + case 1350: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9008 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9034 { io := tree.NewIndexOption() io.ParserName = yyDollar[3].cstrUnion().Compare() yyLOCAL = io } yyVAL.union = yyLOCAL - case 1349: + case 1351: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9014 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9040 { io := tree.NewIndexOption() io.Visible = tree.VISIBLE_TYPE_VISIBLE yyLOCAL = io } yyVAL.union = yyLOCAL - case 1350: + case 1352: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9020 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9046 { io := tree.NewIndexOption() io.Visible = tree.VISIBLE_TYPE_INVISIBLE yyLOCAL = io } yyVAL.union = yyLOCAL - case 1351: + case 1353: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9026 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9052 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -24541,10 +24648,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1352: + case 1354: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9037 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9063 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -24556,10 +24663,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1353: + case 1355: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9048 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9074 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -24571,10 +24678,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1354: + case 1356: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9059 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9085 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -24586,10 +24693,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1355: + case 1357: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9070 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9096 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -24601,10 +24708,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1356: + case 1358: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9081 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9107 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -24616,40 +24723,40 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1357: + case 1359: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9092 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9118 { io := tree.NewIndexOption() io.IncludeColumns = yyDollar[3].unresolveNamesUnion() yyLOCAL = io } yyVAL.union = yyLOCAL - case 1358: + case 1360: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9098 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9124 { io := tree.NewIndexOption() io.Quantization = yyDollar[2].str yyLOCAL = io } yyVAL.union = yyLOCAL - case 1359: + case 1361: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9104 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9130 { io := tree.NewIndexOption() io.DistributionMode = yyDollar[2].str yyLOCAL = io } yyVAL.union = yyLOCAL - case 1360: + case 1362: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9110 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9136 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -24661,10 +24768,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1361: + case 1363: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9121 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9147 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -24676,10 +24783,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1362: + case 1364: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9132 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9158 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -24691,10 +24798,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1363: + case 1365: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9143 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9169 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -24706,10 +24813,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1364: + case 1366: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9154 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9180 { val := int64(yyDollar[3].item.(int64)) if val <= 0 { @@ -24721,50 +24828,97 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1365: + case 1367: + yyDollar = yyS[yypt-3 : yypt+1] + var yyLOCAL *tree.IndexOption +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9191 + { + val := int64(yyDollar[3].item.(int64)) + if val <= 0 { + yylex.Error("MAX_POSTINGS_CAPACITY should be greater than 0") + return 1 + } + io := tree.NewIndexOption() + io.MaxPostingsCapacity = val + yyLOCAL = io + } + yyVAL.union = yyLOCAL + case 1368: + yyDollar = yyS[yypt-3 : yypt+1] + var yyLOCAL *tree.IndexOption +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9202 + { + io := tree.NewIndexOption() + io.PositionFree = true + io.PositionFreeSet = true + yyLOCAL = io + } + yyVAL.union = yyLOCAL + case 1369: + yyDollar = yyS[yypt-3 : yypt+1] + var yyLOCAL *tree.IndexOption +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9209 + { + io := tree.NewIndexOption() + io.PositionFree = false + io.PositionFreeSet = true + yyLOCAL = io + } + yyVAL.union = yyLOCAL + case 1370: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9165 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9216 { io := tree.NewIndexOption() io.Async = true yyLOCAL = io } yyVAL.union = yyLOCAL - case 1366: + case 1371: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9171 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9222 { io := tree.NewIndexOption() io.ForceSync = true yyLOCAL = io } yyVAL.union = yyLOCAL - case 1367: + case 1372: + yyDollar = yyS[yypt-1 : yypt+1] + var yyLOCAL *tree.IndexOption +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9228 + { + io := tree.NewIndexOption() + io.Merge = true + yyLOCAL = io + } + yyVAL.union = yyLOCAL + case 1373: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9177 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9234 { io := tree.NewIndexOption() io.AutoUpdate = true yyLOCAL = io } yyVAL.union = yyLOCAL - case 1368: + case 1374: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9183 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9240 { io := tree.NewIndexOption() io.AutoUpdate = false yyLOCAL = io } yyVAL.union = yyLOCAL - case 1369: + case 1375: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9189 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9246 { val := int64(yyDollar[3].item.(int64)) if val < 0 { @@ -24776,10 +24930,10 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1370: + case 1376: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IndexOption -//line mysql_sql.y:9200 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9257 { val := int64(yyDollar[3].item.(int64)) if val < 0 || val > 23 { @@ -24791,26 +24945,41 @@ yydefault: yyLOCAL = io } yyVAL.union = yyLOCAL - case 1371: + case 1377: + yyDollar = yyS[yypt-3 : yypt+1] + var yyLOCAL *tree.IndexOption +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9268 + { + val := int64(yyDollar[3].item.(int64)) + if val < 0 { + yylex.Error("SECOND should be greater than or equal to 0") + return 1 + } + io := tree.NewIndexOption() + io.Second = val + yyLOCAL = io + } + yyVAL.union = yyLOCAL + case 1378: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.KeyPart -//line mysql_sql.y:9214 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9282 { yyLOCAL = []*tree.KeyPart{yyDollar[1].keyPartUnion()} } yyVAL.union = yyLOCAL - case 1372: + case 1379: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.KeyPart -//line mysql_sql.y:9218 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9286 { yyLOCAL = append(yyDollar[1].keyPartsUnion(), yyDollar[3].keyPartUnion()) } yyVAL.union = yyLOCAL - case 1373: + case 1380: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.KeyPart -//line mysql_sql.y:9224 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9292 { // Order is parsed but just ignored as MySQL dtree. var ColName = yyDollar[1].unresolvedNameUnion() @@ -24825,10 +24994,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1374: + case 1381: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.KeyPart -//line mysql_sql.y:9238 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9306 { var ColName *tree.UnresolvedName var Length int @@ -24842,90 +25011,90 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1375: + case 1382: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:9252 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9320 { yyLOCAL = tree.INDEX_TYPE_INVALID } yyVAL.union = yyLOCAL - case 1376: + case 1383: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:9256 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9324 { yyLOCAL = tree.INDEX_TYPE_BTREE } yyVAL.union = yyLOCAL - case 1377: + case 1384: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:9260 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9328 { yyLOCAL = tree.INDEX_TYPE_IVFFLAT } yyVAL.union = yyLOCAL - case 1378: + case 1385: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:9264 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9332 { yyLOCAL = tree.INDEX_TYPE_HNSW } yyVAL.union = yyLOCAL - case 1379: + case 1386: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:9268 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9336 { yyLOCAL = tree.INDEX_TYPE_IVFPQ } yyVAL.union = yyLOCAL - case 1380: + case 1387: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:9272 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9340 { yyLOCAL = tree.INDEX_TYPE_CAGRA } yyVAL.union = yyLOCAL - case 1381: + case 1388: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:9276 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9344 { yyLOCAL = tree.INDEX_TYPE_MASTER } yyVAL.union = yyLOCAL - case 1382: + case 1389: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:9280 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9348 { yyLOCAL = tree.INDEX_TYPE_HASH } yyVAL.union = yyLOCAL - case 1383: + case 1390: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:9284 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9352 { yyLOCAL = tree.INDEX_TYPE_RTREE } yyVAL.union = yyLOCAL - case 1384: + case 1391: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.IndexType -//line mysql_sql.y:9288 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9356 { yyLOCAL = tree.INDEX_TYPE_BSI } yyVAL.union = yyLOCAL - case 1385: + case 1392: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9294 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9362 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var Name = tree.Identifier(yyDollar[4].str) @@ -24939,10 +25108,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1386: + case 1393: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9308 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9376 { var t = tree.NewCloneDatabase() t.DstDatabase = tree.Identifier(yyDollar[4].str) @@ -24952,10 +25121,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1387: + case 1394: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9317 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9385 { var DbName = tree.Identifier(yyDollar[4].str) var FromUri = yyDollar[6].str @@ -24973,92 +25142,92 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1388: + case 1395: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.SubscriptionOption -//line mysql_sql.y:9335 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9403 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1389: + case 1396: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.SubscriptionOption -//line mysql_sql.y:9339 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9407 { var From = tree.Identifier(yyDollar[2].str) var Publication = tree.Identifier(yyDollar[4].cstrUnion().Compare()) yyLOCAL = tree.NewSubscriptionOption(From, Publication) } yyVAL.union = yyLOCAL - case 1392: + case 1399: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:9350 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9418 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1393: + case 1400: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:9354 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9422 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1394: + case 1401: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:9359 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9427 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1395: + case 1402: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:9363 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9431 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1396: + case 1403: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []tree.CreateOption -//line mysql_sql.y:9368 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9436 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1397: + case 1404: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.CreateOption -//line mysql_sql.y:9372 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9440 { yyLOCAL = yyDollar[1].createOptionsUnion() } yyVAL.union = yyLOCAL - case 1398: + case 1405: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.CreateOption -//line mysql_sql.y:9378 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9446 { yyLOCAL = []tree.CreateOption{yyDollar[1].createOptionUnion()} } yyVAL.union = yyLOCAL - case 1399: + case 1406: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []tree.CreateOption -//line mysql_sql.y:9382 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9450 { yyLOCAL = append(yyDollar[1].createOptionsUnion(), yyDollar[2].createOptionUnion()) } yyVAL.union = yyLOCAL - case 1400: + case 1407: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.CreateOption -//line mysql_sql.y:9388 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9456 { var IsDefault = yyDollar[1].defaultOptionalUnion() var Charset = yyDollar[4].str @@ -25068,10 +25237,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1401: + case 1408: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.CreateOption -//line mysql_sql.y:9397 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9465 { var IsDefault = yyDollar[1].defaultOptionalUnion() var Collate = yyDollar[4].str @@ -25081,35 +25250,35 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1402: + case 1409: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.CreateOption -//line mysql_sql.y:9406 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9474 { var Encrypt = yyDollar[4].str yyLOCAL = tree.NewCreateOptionEncryption(Encrypt) } yyVAL.union = yyLOCAL - case 1403: + case 1410: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:9412 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9480 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1404: + case 1411: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:9416 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9484 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1405: + case 1412: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9422 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9490 { var taskID uint64 switch v := yyDollar[4].item.(type) { @@ -25126,10 +25295,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1406: + case 1413: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9440 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9508 { var taskID uint64 switch v := yyDollar[4].item.(type) { @@ -25146,10 +25315,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1407: + case 1414: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9458 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9526 { var taskID uint64 switch v := yyDollar[4].item.(type) { @@ -25166,26 +25335,26 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1408: + case 1415: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:9475 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9543 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1409: + case 1416: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:9479 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9547 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1410: + case 1417: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9485 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9553 { t := tree.NewDataBranchCreateTable() t.CreateTable.Table = *yyDollar[5].tableNameUnion() @@ -25196,10 +25365,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1411: + case 1418: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9495 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9563 { t := tree.NewDataBranchCreateDatabase() t.DstDatabase = tree.Identifier(yyDollar[5].str) @@ -25209,30 +25378,30 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1412: + case 1419: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9504 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9572 { t := tree.NewDataBranchDeleteTable() t.TableName = *yyDollar[5].tableNameUnion() yyLOCAL = t } yyVAL.union = yyLOCAL - case 1413: + case 1420: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9510 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9578 { t := tree.NewDataBranchDeleteDatabase() t.DatabaseName = tree.Identifier(yyDollar[5].str) yyLOCAL = t } yyVAL.union = yyLOCAL - case 1414: + case 1421: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9516 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9584 { t := tree.NewDataBranchDiff() t.TargetTable = *yyDollar[4].tableNameUnion() @@ -25242,10 +25411,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1415: + case 1422: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9525 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9593 { t := tree.NewDataBranchMerge() t.SrcTable = *yyDollar[4].tableNameUnion() @@ -25254,10 +25423,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1416: + case 1423: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9533 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9601 { t := tree.NewDataBranchPick() t.SrcTable = *yyDollar[4].tableNameUnion() @@ -25267,10 +25436,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1417: + case 1424: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9542 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9610 { t := tree.NewDataBranchPick() t.SrcTable = *yyDollar[4].tableNameUnion() @@ -25281,10 +25450,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1418: + case 1425: yyDollar = yyS[yypt-12 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9552 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9620 { t := tree.NewDataBranchPick() t.SrcTable = *yyDollar[4].tableNameUnion() @@ -25295,10 +25464,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1419: + case 1426: yyDollar = yyS[yypt-13 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9562 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9630 { t := tree.NewDataBranchPick() t.SrcTable = *yyDollar[4].tableNameUnion() @@ -25310,10 +25479,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1420: + case 1427: yyDollar = yyS[yypt-13 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9573 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9641 { t := tree.NewDataBranchPick() t.SrcTable = *yyDollar[4].tableNameUnion() @@ -25325,54 +25494,54 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1421: + case 1428: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:9585 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9653 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1422: + case 1429: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.IdentifierList -//line mysql_sql.y:9589 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9657 { yyLOCAL = yyDollar[3].identifierListUnion() } yyVAL.union = yyLOCAL - case 1423: + case 1430: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.DiffOutputOpt -//line mysql_sql.y:9594 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9662 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1424: + case 1431: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.DiffOutputOpt -//line mysql_sql.y:9598 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9666 { yyLOCAL = &tree.DiffOutputOpt{ As: *yyDollar[3].tableNameUnion(), } } yyVAL.union = yyLOCAL - case 1425: + case 1432: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.DiffOutputOpt -//line mysql_sql.y:9604 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9672 { yyLOCAL = &tree.DiffOutputOpt{ DirPath: yyDollar[3].str, } } yyVAL.union = yyLOCAL - case 1426: + case 1433: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.DiffOutputOpt -//line mysql_sql.y:9610 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9678 { x, errStr := util.GetInt64(yyDollar[3].item) if errStr != "" { @@ -25384,68 +25553,68 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1427: + case 1434: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.DiffOutputOpt -//line mysql_sql.y:9617 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9685 { yyLOCAL = &tree.DiffOutputOpt{ Count: true, } } yyVAL.union = yyLOCAL - case 1428: + case 1435: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.DiffOutputOpt -//line mysql_sql.y:9623 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9691 { yyLOCAL = &tree.DiffOutputOpt{ Summary: true, } } yyVAL.union = yyLOCAL - case 1429: + case 1436: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ConflictOpt -//line mysql_sql.y:9631 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9699 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1430: + case 1437: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ConflictOpt -//line mysql_sql.y:9635 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9703 { yyLOCAL = &tree.ConflictOpt{ Opt: tree.CONFLICT_FAIL, } } yyVAL.union = yyLOCAL - case 1431: + case 1438: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ConflictOpt -//line mysql_sql.y:9641 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9709 { yyLOCAL = &tree.ConflictOpt{ Opt: tree.CONFLICT_SKIP, } } yyVAL.union = yyLOCAL - case 1432: + case 1439: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ConflictOpt -//line mysql_sql.y:9647 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9715 { yyLOCAL = &tree.ConflictOpt{ Opt: tree.CONFLICT_ACCEPT, } } yyVAL.union = yyLOCAL - case 1433: + case 1440: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.PickKeys -//line mysql_sql.y:9655 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9723 { yyLOCAL = &tree.PickKeys{ Type: tree.PickKeysValues, @@ -25453,10 +25622,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1434: + case 1441: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.PickKeys -//line mysql_sql.y:9662 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9730 { yyLOCAL = &tree.PickKeys{ Type: tree.PickKeysSubquery, @@ -25464,44 +25633,44 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1435: + case 1442: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ToAccountOpt -//line mysql_sql.y:9671 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9739 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1436: + case 1443: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ToAccountOpt -//line mysql_sql.y:9675 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9743 { yyLOCAL = &tree.ToAccountOpt{ AccountName: tree.Identifier(yyDollar[3].cstrUnion().Compare()), } } yyVAL.union = yyLOCAL - case 1437: + case 1444: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:9683 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9751 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1438: + case 1445: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:9687 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9755 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1439: + case 1446: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9693 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9761 { t := tree.NewCreateTable() t.Temporary = yyDollar[2].boolValUnion() @@ -25514,10 +25683,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1440: + case 1447: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9705 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9773 { t := tree.NewCreateTable() t.IfNotExists = yyDollar[4].ifNotExistsUnion() @@ -25527,10 +25696,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1441: + case 1448: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9714 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9782 { t := tree.NewCreateTable() t.IfNotExists = yyDollar[4].ifNotExistsUnion() @@ -25540,10 +25709,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1442: + case 1449: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9723 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9791 { t := tree.NewCreateTable() t.IfNotExists = yyDollar[4].ifNotExistsUnion() @@ -25553,10 +25722,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1443: + case 1450: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9732 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9800 { t := tree.NewCreateTable() t.IfNotExists = yyDollar[4].ifNotExistsUnion() @@ -25566,10 +25735,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1444: + case 1451: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9741 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9809 { t := tree.NewCreateTable() t.IfNotExists = yyDollar[4].ifNotExistsUnion() @@ -25578,10 +25747,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1445: + case 1452: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9749 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9817 { t := tree.NewCreateTable() t.IsClusterTable = true @@ -25594,10 +25763,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1446: + case 1453: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9761 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9829 { t := tree.NewCreateTable() t.IsAsSelect = true @@ -25608,10 +25777,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1447: + case 1454: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9771 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9839 { t := tree.NewCreateTable() t.IsAsSelect = true @@ -25623,10 +25792,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1448: + case 1455: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9782 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9850 { t := tree.NewCreateTable() t.IsAsSelect = true @@ -25637,10 +25806,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1449: + case 1456: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9792 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9860 { t := tree.NewCreateTable() t.IsAsSelect = true @@ -25652,10 +25821,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1450: + case 1457: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9803 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9871 { t := tree.NewCreateTable() t.IsAsLike = true @@ -25666,10 +25835,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1451: + case 1458: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9813 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9881 { t := tree.NewCreateTable() t.Temporary = yyDollar[2].boolValUnion() @@ -25679,10 +25848,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1452: + case 1459: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9822 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9890 { t := tree.NewCloneTable() t.CreateTable.Temporary = yyDollar[2].boolValUnion() @@ -25696,10 +25865,10 @@ yydefault: yyLOCAL = t } yyVAL.union = yyLOCAL - case 1453: + case 1460: yyDollar = yyS[yypt-11 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9835 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9903 { var TableName = yyDollar[5].tableNameUnion() var FromUri = yyDollar[7].str @@ -25723,19 +25892,19 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1454: + case 1461: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ExternParam -//line mysql_sql.y:9860 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9928 { yyLOCAL = yyDollar[1].loadParamUnion() yyLOCAL.Tail = yyDollar[2].tailParamUnion() } yyVAL.union = yyLOCAL - case 1455: + case 1462: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ExternParam -//line mysql_sql.y:9867 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9935 { yyLOCAL = &tree.ExternParam{ ExParamConst: tree.ExParamConst{ @@ -25746,10 +25915,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1456: + case 1463: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL *tree.ExternParam -//line mysql_sql.y:9877 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9945 { yyLOCAL = &tree.ExternParam{ ExParamConst: tree.ExParamConst{ @@ -25763,10 +25932,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1457: + case 1464: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.ExternParam -//line mysql_sql.y:9890 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9958 { yyLOCAL = &tree.ExternParam{ ExParamConst: tree.ExParamConst{ @@ -25775,10 +25944,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1458: + case 1465: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.ExternParam -//line mysql_sql.y:9898 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9966 { yyLOCAL = &tree.ExternParam{ ExParamConst: tree.ExParamConst{ @@ -25788,10 +25957,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1459: + case 1466: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ExternParam -//line mysql_sql.y:9907 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9975 { yyLOCAL = &tree.ExternParam{ ExParamConst: tree.ExParamConst{ @@ -25800,55 +25969,55 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1460: + case 1467: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:9916 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9984 { yyVAL.str = "" } - case 1461: + case 1468: yyDollar = yyS[yypt-4 : yypt+1] -//line mysql_sql.y:9920 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9988 { yyVAL.str = yyDollar[4].str } - case 1462: + case 1469: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:9926 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9994 { yyLOCAL = yyDollar[1].strsUnion() } yyVAL.union = yyLOCAL - case 1463: + case 1470: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:9930 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:9998 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].strsUnion()...) } yyVAL.union = yyLOCAL - case 1464: + case 1471: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:9935 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10003 { yyLOCAL = []string{} } yyVAL.union = yyLOCAL - case 1465: + case 1472: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:9939 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10007 { yyLOCAL = append(yyLOCAL, yyDollar[1].str) yyLOCAL = append(yyLOCAL, yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1466: + case 1473: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.TailParameter -//line mysql_sql.y:9946 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10014 { yyLOCAL = &tree.TailParameter{ Charset: yyDollar[1].str, @@ -25860,22 +26029,22 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1467: + case 1474: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:9958 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10026 { yyVAL.str = "" } - case 1468: + case 1475: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:9962 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10030 { yyVAL.str = yyDollar[2].str } - case 1469: + case 1476: yyDollar = yyS[yypt-10 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:9968 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10036 { var Name = yyDollar[4].tableNameUnion() var Type = yyDollar[5].columnTypeUnion() @@ -25897,10 +26066,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1470: + case 1477: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:9989 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10057 { locale := "" fstr := "bigint" @@ -25915,44 +26084,44 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1471: + case 1478: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:10003 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10071 { yyLOCAL = yyDollar[2].columnTypeUnion() } yyVAL.union = yyLOCAL - case 1472: + case 1479: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.TypeOption -//line mysql_sql.y:10007 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10075 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1473: + case 1480: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.TypeOption -//line mysql_sql.y:10011 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10079 { yyLOCAL = &tree.TypeOption{ Type: yyDollar[2].columnTypeUnion(), } } yyVAL.union = yyLOCAL - case 1474: + case 1481: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.IncrementByOption -//line mysql_sql.y:10017 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10085 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1475: + case 1482: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IncrementByOption -//line mysql_sql.y:10021 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10089 { yyLOCAL = &tree.IncrementByOption{ Minus: false, @@ -25960,10 +26129,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1476: + case 1483: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.IncrementByOption -//line mysql_sql.y:10028 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10096 { yyLOCAL = &tree.IncrementByOption{ Minus: false, @@ -25971,10 +26140,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1477: + case 1484: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.IncrementByOption -//line mysql_sql.y:10035 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10103 { yyLOCAL = &tree.IncrementByOption{ Minus: true, @@ -25982,10 +26151,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1478: + case 1485: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IncrementByOption -//line mysql_sql.y:10042 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10110 { yyLOCAL = &tree.IncrementByOption{ Minus: true, @@ -25993,42 +26162,42 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1479: + case 1486: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10049 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10117 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1480: + case 1487: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10053 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10121 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1481: + case 1488: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10057 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10125 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1482: + case 1489: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.MinValueOption -//line mysql_sql.y:10061 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10129 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1483: + case 1490: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.MinValueOption -//line mysql_sql.y:10065 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10133 { yyLOCAL = &tree.MinValueOption{ Minus: false, @@ -26036,10 +26205,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1484: + case 1491: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.MinValueOption -//line mysql_sql.y:10072 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10140 { yyLOCAL = &tree.MinValueOption{ Minus: true, @@ -26047,18 +26216,18 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1485: + case 1492: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.MaxValueOption -//line mysql_sql.y:10079 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10147 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1486: + case 1493: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.MaxValueOption -//line mysql_sql.y:10083 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10151 { yyLOCAL = &tree.MaxValueOption{ Minus: false, @@ -26066,10 +26235,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1487: + case 1494: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.MaxValueOption -//line mysql_sql.y:10090 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10158 { yyLOCAL = &tree.MaxValueOption{ Minus: true, @@ -26077,46 +26246,46 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1488: + case 1495: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.CycleOption -//line mysql_sql.y:10097 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10165 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1489: + case 1496: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.CycleOption -//line mysql_sql.y:10101 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10169 { yyLOCAL = &tree.CycleOption{ Cycle: false, } } yyVAL.union = yyLOCAL - case 1490: + case 1497: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CycleOption -//line mysql_sql.y:10107 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10175 { yyLOCAL = &tree.CycleOption{ Cycle: true, } } yyVAL.union = yyLOCAL - case 1491: + case 1498: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.StartWithOption -//line mysql_sql.y:10113 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10181 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1492: + case 1499: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.StartWithOption -//line mysql_sql.y:10117 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10185 { yyLOCAL = &tree.StartWithOption{ Minus: false, @@ -26124,10 +26293,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1493: + case 1500: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.StartWithOption -//line mysql_sql.y:10124 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10192 { yyLOCAL = &tree.StartWithOption{ Minus: false, @@ -26135,10 +26304,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1494: + case 1501: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.StartWithOption -//line mysql_sql.y:10131 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10199 { yyLOCAL = &tree.StartWithOption{ Minus: true, @@ -26146,10 +26315,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1495: + case 1502: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.StartWithOption -//line mysql_sql.y:10138 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10206 { yyLOCAL = &tree.StartWithOption{ Minus: true, @@ -26157,58 +26326,58 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1496: + case 1503: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10145 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10213 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1497: + case 1504: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10149 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10217 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1498: + case 1505: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10154 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10222 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1499: + case 1506: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10158 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10226 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1500: + case 1507: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10162 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10230 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1501: + case 1508: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.PartitionOption -//line mysql_sql.y:10167 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10235 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1502: + case 1509: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.PartitionOption -//line mysql_sql.y:10171 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10239 { yyDollar[3].partitionByUnion().Num = uint64(yyDollar[4].int64ValUnion()) var PartBy = yyDollar[3].partitionByUnion() @@ -26221,18 +26390,18 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1503: + case 1510: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ClusterByOption -//line mysql_sql.y:10184 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10252 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1504: + case 1511: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ClusterByOption -//line mysql_sql.y:10188 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10256 { var ColumnList = []*tree.UnresolvedName{yyDollar[3].unresolvedNameUnion()} yyLOCAL = tree.NewClusterByOption( @@ -26241,10 +26410,10 @@ yydefault: } yyVAL.union = yyLOCAL - case 1505: + case 1512: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.ClusterByOption -//line mysql_sql.y:10196 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10264 { var ColumnList = yyDollar[4].unresolveNamesUnion() yyLOCAL = tree.NewClusterByOption( @@ -26252,18 +26421,18 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1506: + case 1513: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:10204 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10272 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1507: + case 1514: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:10208 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10276 { var IsSubPartition = true var PType = yyDollar[3].partitionByUnion().PType @@ -26277,42 +26446,42 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1508: + case 1515: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.Partition -//line mysql_sql.y:10222 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10290 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1509: + case 1516: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.Partition -//line mysql_sql.y:10226 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10294 { yyLOCAL = yyDollar[2].partitionsUnion() } yyVAL.union = yyLOCAL - case 1510: + case 1517: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.Partition -//line mysql_sql.y:10232 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10300 { yyLOCAL = []*tree.Partition{yyDollar[1].partitionUnion()} } yyVAL.union = yyLOCAL - case 1511: + case 1518: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.Partition -//line mysql_sql.y:10236 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10304 { yyLOCAL = append(yyDollar[1].partitionsUnion(), yyDollar[3].partitionUnion()) } yyVAL.union = yyLOCAL - case 1512: + case 1519: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.Partition -//line mysql_sql.y:10242 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10310 { var Name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) var Values = yyDollar[3].valuesUnion() @@ -26326,10 +26495,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1513: + case 1520: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.Partition -//line mysql_sql.y:10255 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10323 { var Name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) var Values = yyDollar[3].valuesUnion() @@ -26343,42 +26512,42 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1514: + case 1521: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.SubPartition -//line mysql_sql.y:10269 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10337 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1515: + case 1522: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.SubPartition -//line mysql_sql.y:10273 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10341 { yyLOCAL = yyDollar[2].subPartitionsUnion() } yyVAL.union = yyLOCAL - case 1516: + case 1523: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.SubPartition -//line mysql_sql.y:10279 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10347 { yyLOCAL = []*tree.SubPartition{yyDollar[1].subPartitionUnion()} } yyVAL.union = yyLOCAL - case 1517: + case 1524: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.SubPartition -//line mysql_sql.y:10283 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10351 { yyLOCAL = append(yyDollar[1].subPartitionsUnion(), yyDollar[3].subPartitionUnion()) } yyVAL.union = yyLOCAL - case 1518: + case 1525: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.SubPartition -//line mysql_sql.y:10289 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10357 { var Name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) var Options []tree.TableOption @@ -26388,10 +26557,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1519: + case 1526: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.SubPartition -//line mysql_sql.y:10298 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10366 { var Name = tree.Identifier(yyDollar[2].cstrUnion().Compare()) var Options = yyDollar[3].tableOptionsUnion() @@ -26401,53 +26570,53 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1520: + case 1527: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:10309 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10377 { yyLOCAL = []tree.TableOption{yyDollar[1].tableOptionUnion()} } yyVAL.union = yyLOCAL - case 1521: + case 1528: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:10313 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10381 { yyLOCAL = append(yyDollar[1].tableOptionsUnion(), yyDollar[2].tableOptionUnion()) } yyVAL.union = yyLOCAL - case 1522: + case 1529: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Values -//line mysql_sql.y:10318 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10386 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1523: + case 1530: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Values -//line mysql_sql.y:10322 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10390 { expr := tree.NewMaxValue() var valueList = tree.Exprs{expr} yyLOCAL = tree.NewValuesLessThan(valueList) } yyVAL.union = yyLOCAL - case 1524: + case 1531: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Values -//line mysql_sql.y:10328 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10396 { var valueList = yyDollar[5].exprsUnion() yyLOCAL = tree.NewValuesLessThan(valueList) } yyVAL.union = yyLOCAL - case 1525: + case 1532: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Values -//line mysql_sql.y:10333 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10401 { var valueList = yyDollar[4].exprsUnion() yyLOCAL = tree.NewValuesIn( @@ -26455,18 +26624,18 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1526: + case 1533: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:10341 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10409 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 1527: + case 1534: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:10345 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10413 { res := yyDollar[2].item.(int64) if res == 0 { @@ -26476,18 +26645,18 @@ yydefault: yyLOCAL = res } yyVAL.union = yyLOCAL - case 1528: + case 1535: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:10355 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10423 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 1529: + case 1536: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:10359 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10427 { res := yyDollar[2].item.(int64) if res == 0 { @@ -26497,10 +26666,10 @@ yydefault: yyLOCAL = res } yyVAL.union = yyLOCAL - case 1530: + case 1537: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:10370 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10438 { rangeTyp := tree.NewRangeType() rangeTyp.Expr = yyDollar[3].exprUnion() @@ -26509,10 +26678,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1531: + case 1538: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:10378 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10446 { rangeTyp := tree.NewRangeType() rangeTyp.ColumnList = yyDollar[4].unresolveNamesUnion() @@ -26521,10 +26690,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1532: + case 1539: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:10386 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10454 { listTyp := tree.NewListType() listTyp.Expr = yyDollar[3].exprUnion() @@ -26533,10 +26702,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1533: + case 1540: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:10394 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10462 { listTyp := tree.NewListType() listTyp.ColumnList = yyDollar[4].unresolveNamesUnion() @@ -26545,10 +26714,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1535: + case 1542: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:10405 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10473 { keyTyp := tree.NewKeyType() keyTyp.Linear = yyDollar[1].boolValUnion() @@ -26558,10 +26727,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1536: + case 1543: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:10414 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10482 { keyTyp := tree.NewKeyType() keyTyp.Linear = yyDollar[1].boolValUnion() @@ -26572,10 +26741,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1537: + case 1544: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.PartitionBy -//line mysql_sql.y:10424 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10492 { Linear := yyDollar[1].boolValUnion() Expr := yyDollar[4].exprUnion() @@ -26585,612 +26754,612 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1538: + case 1545: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:10434 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10502 { yyLOCAL = 2 } yyVAL.union = yyLOCAL - case 1539: + case 1546: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:10438 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10506 { yyLOCAL = yyDollar[3].item.(int64) } yyVAL.union = yyLOCAL - case 1540: + case 1547: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10443 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10511 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1541: + case 1548: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:10447 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10515 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1542: + case 1549: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.IcebergTableParam -//line mysql_sql.y:10453 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10521 { yyLOCAL = tree.NewIcebergTableParam(yyDollar[4].icebergOptionsUnion()) } yyVAL.union = yyLOCAL - case 1543: + case 1550: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.IcebergOptions -//line mysql_sql.y:10458 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10526 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1544: + case 1551: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.IcebergOptions -//line mysql_sql.y:10462 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10530 { yyLOCAL = yyDollar[3].icebergOptionsUnion() } yyVAL.union = yyLOCAL - case 1545: + case 1552: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.IcebergOptions -//line mysql_sql.y:10468 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10536 { yyLOCAL = tree.IcebergOptions{yyDollar[1].icebergOptionUnion()} } yyVAL.union = yyLOCAL - case 1546: + case 1553: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.IcebergOptions -//line mysql_sql.y:10472 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10540 { yyLOCAL = append(yyDollar[1].icebergOptionsUnion(), yyDollar[3].icebergOptionUnion()) } yyVAL.union = yyLOCAL - case 1547: + case 1554: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IcebergOption -//line mysql_sql.y:10478 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10546 { yyLOCAL = tree.NewIcebergOption(tree.Identifier(yyDollar[1].str), yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1548: + case 1555: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10484 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10552 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1549: + case 1556: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10488 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10556 { yyVAL.str = yyDollar[1].str } - case 1550: + case 1557: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10494 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10562 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1551: + case 1558: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10498 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10566 { yyVAL.str = yyDollar[1].str } - case 1552: + case 1559: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.MongoDBTableParam -//line mysql_sql.y:10504 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10572 { yyLOCAL = tree.NewMongoDBTableParam(yyDollar[4].mongodbOptionsUnion()) } yyVAL.union = yyLOCAL - case 1553: + case 1560: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.MongoDBOptions -//line mysql_sql.y:10509 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10577 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1554: + case 1561: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.MongoDBOptions -//line mysql_sql.y:10513 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10581 { yyLOCAL = yyDollar[3].mongodbOptionsUnion() } yyVAL.union = yyLOCAL - case 1555: + case 1562: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.MongoDBOptions -//line mysql_sql.y:10519 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10587 { yyLOCAL = tree.MongoDBOptions{yyDollar[1].mongodbOptionUnion()} } yyVAL.union = yyLOCAL - case 1556: + case 1563: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.MongoDBOptions -//line mysql_sql.y:10523 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10591 { yyLOCAL = append(yyDollar[1].mongodbOptionsUnion(), yyDollar[3].mongodbOptionUnion()) } yyVAL.union = yyLOCAL - case 1557: + case 1564: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.MongoDBOption -//line mysql_sql.y:10529 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10597 { yyLOCAL = tree.NewMongoDBOption(tree.Identifier(yyDollar[1].str), yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1558: + case 1565: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10535 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10603 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1559: + case 1566: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10539 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10607 { yyVAL.str = yyDollar[1].str } - case 1560: + case 1567: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10545 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10613 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1561: + case 1568: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10549 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10617 { yyVAL.str = yyDollar[1].str } - case 1562: + case 1569: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.DataStreamTableParam -//line mysql_sql.y:10555 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10623 { yyLOCAL = tree.NewDataStreamTableParam(yyDollar[4].datastreamOptionsUnion()) } yyVAL.union = yyLOCAL - case 1563: + case 1570: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.DataStreamOptions -//line mysql_sql.y:10560 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10628 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1564: + case 1571: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.DataStreamOptions -//line mysql_sql.y:10564 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10632 { yyLOCAL = yyDollar[3].datastreamOptionsUnion() } yyVAL.union = yyLOCAL - case 1565: + case 1572: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.DataStreamOptions -//line mysql_sql.y:10570 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10638 { yyLOCAL = tree.DataStreamOptions{yyDollar[1].datastreamOptionUnion()} } yyVAL.union = yyLOCAL - case 1566: + case 1573: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.DataStreamOptions -//line mysql_sql.y:10574 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10642 { yyLOCAL = append(yyDollar[1].datastreamOptionsUnion(), yyDollar[3].datastreamOptionUnion()) } yyVAL.union = yyLOCAL - case 1567: + case 1574: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.DataStreamOption -//line mysql_sql.y:10580 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10648 { yyLOCAL = tree.NewDataStreamOption(tree.Identifier(yyDollar[1].str), yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1568: + case 1575: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10586 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10654 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1569: + case 1576: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10590 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10658 { yyVAL.str = yyDollar[1].str } - case 1570: + case 1577: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10596 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10664 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1571: + case 1578: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10600 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10668 { yyVAL.str = yyDollar[1].str } - case 1572: + case 1579: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:10605 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10673 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1573: + case 1580: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:10609 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10677 { yyLOCAL = yyDollar[1].tableOptionsUnion() } yyVAL.union = yyLOCAL - case 1574: + case 1581: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:10615 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10683 { yyLOCAL = []tree.TableOption{yyDollar[1].tableOptionUnion()} } yyVAL.union = yyLOCAL - case 1575: + case 1582: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:10619 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10687 { yyLOCAL = append(yyDollar[1].tableOptionsUnion(), yyDollar[3].tableOptionUnion()) } yyVAL.union = yyLOCAL - case 1576: + case 1583: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []tree.TableOption -//line mysql_sql.y:10623 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10691 { yyLOCAL = append(yyDollar[1].tableOptionsUnion(), yyDollar[2].tableOptionUnion()) } yyVAL.union = yyLOCAL - case 1577: + case 1584: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10629 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10697 { yyLOCAL = tree.NewTableOptionAUTOEXTEND_SIZE(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1578: + case 1585: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10633 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10701 { yyLOCAL = tree.NewTableOptionAutoIncrement(integralToUint64(yyDollar[3].item)) } yyVAL.union = yyLOCAL - case 1579: + case 1586: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10637 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10705 { yyLOCAL = tree.NewTableOptionAvgRowLength(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1580: + case 1587: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10641 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10709 { yyLOCAL = tree.NewTableOptionCharset(yyDollar[4].str) } yyVAL.union = yyLOCAL - case 1581: + case 1588: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10645 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10713 { yyLOCAL = tree.NewTableOptionCollate(yyDollar[4].str) } yyVAL.union = yyLOCAL - case 1582: + case 1589: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10649 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10717 { yyLOCAL = tree.NewTableOptionChecksum(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1583: + case 1590: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10653 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10721 { str := util.DealCommentString(yyDollar[3].str) yyLOCAL = tree.NewTableOptionComment(str) } yyVAL.union = yyLOCAL - case 1584: + case 1591: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10658 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10726 { yyLOCAL = tree.NewTableOptionCompression(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1585: + case 1592: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10662 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10730 { yyLOCAL = tree.NewTableOptionConnection(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1586: + case 1593: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10666 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10734 { yyLOCAL = tree.NewTableOptionDataDirectory(yyDollar[4].str) } yyVAL.union = yyLOCAL - case 1587: + case 1594: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10670 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10738 { yyLOCAL = tree.NewTableOptionIndexDirectory(yyDollar[4].str) } yyVAL.union = yyLOCAL - case 1588: + case 1595: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10674 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10742 { yyLOCAL = tree.NewTableOptionDelayKeyWrite(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1589: + case 1596: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10678 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10746 { yyLOCAL = tree.NewTableOptionEncryption(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1590: + case 1597: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10682 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10750 { yyLOCAL = tree.NewTableOptionEngine(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1591: + case 1598: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10686 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10754 { yyLOCAL = tree.NewTableOptionEngineAttr(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1592: + case 1599: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10690 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10758 { yyLOCAL = tree.NewTableOptionInsertMethod(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1593: + case 1600: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10694 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10762 { yyLOCAL = tree.NewTableOptionKeyBlockSize(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1594: + case 1601: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10698 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10766 { yyLOCAL = tree.NewTableOptionMaxRows(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1595: + case 1602: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10702 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10770 { yyLOCAL = tree.NewTableOptionMinRows(uint64(yyDollar[3].item.(int64))) } yyVAL.union = yyLOCAL - case 1596: + case 1603: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10706 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10774 { t := tree.NewTableOptionPackKeys() t.Value = yyDollar[3].item.(int64) yyLOCAL = t } yyVAL.union = yyLOCAL - case 1597: + case 1604: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10712 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10780 { t := tree.NewTableOptionPackKeys() t.Default = true yyLOCAL = t } yyVAL.union = yyLOCAL - case 1598: + case 1605: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10718 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10786 { yyLOCAL = tree.NewTableOptionPassword(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1599: + case 1606: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10722 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10790 { yyLOCAL = tree.NewTableOptionRowFormat(yyDollar[3].rowFormatTypeUnion()) } yyVAL.union = yyLOCAL - case 1600: + case 1607: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10726 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10794 { yyLOCAL = tree.NewTTableOptionStartTrans(true) } yyVAL.union = yyLOCAL - case 1601: + case 1608: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10730 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10798 { yyLOCAL = tree.NewTTableOptionSecondaryEngineAttr(yyDollar[3].str) } yyVAL.union = yyLOCAL - case 1602: + case 1609: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10734 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10802 { t := tree.NewTableOptionStatsAutoRecalc() t.Value = uint64(yyDollar[3].item.(int64)) yyLOCAL = t } yyVAL.union = yyLOCAL - case 1603: + case 1610: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10740 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10808 { t := tree.NewTableOptionStatsAutoRecalc() t.Default = true yyLOCAL = t } yyVAL.union = yyLOCAL - case 1604: + case 1611: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10746 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10814 { t := tree.NewTableOptionStatsPersistent() t.Value = uint64(yyDollar[3].item.(int64)) yyLOCAL = t } yyVAL.union = yyLOCAL - case 1605: + case 1612: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10752 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10820 { t := tree.NewTableOptionStatsPersistent() t.Default = true yyLOCAL = t } yyVAL.union = yyLOCAL - case 1606: + case 1613: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10758 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10826 { t := tree.NewTableOptionStatsSamplePages() t.Value = uint64(yyDollar[3].item.(int64)) yyLOCAL = t } yyVAL.union = yyLOCAL - case 1607: + case 1614: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10764 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10832 { t := tree.NewTableOptionStatsSamplePages() t.Default = true yyLOCAL = t } yyVAL.union = yyLOCAL - case 1608: + case 1615: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10770 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10838 { yyLOCAL = tree.NewTableOptionTablespace(yyDollar[3].cstrUnion().Compare(), "") } yyVAL.union = yyLOCAL - case 1609: + case 1616: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10774 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10842 { yyLOCAL = tree.NewTableOptionTablespace("", yyDollar[1].str) } yyVAL.union = yyLOCAL - case 1610: + case 1617: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10778 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10846 { yyLOCAL = tree.NewTableOptionUnion(yyDollar[4].tableNamesUnion()) } yyVAL.union = yyLOCAL - case 1611: + case 1618: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.TableOption -//line mysql_sql.y:10782 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10850 { var Preperties = yyDollar[3].propertiesUnion() yyLOCAL = tree.NewTableOptionProperties(Preperties) } yyVAL.union = yyLOCAL - case 1612: + case 1619: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.Property -//line mysql_sql.y:10789 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10857 { yyLOCAL = []tree.Property{yyDollar[1].propertyUnion()} } yyVAL.union = yyLOCAL - case 1613: + case 1620: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []tree.Property -//line mysql_sql.y:10793 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10861 { yyLOCAL = append(yyDollar[1].propertiesUnion(), yyDollar[3].propertyUnion()) } yyVAL.union = yyLOCAL - case 1614: + case 1621: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Property -//line mysql_sql.y:10799 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10867 { var Key = yyDollar[1].str var Value = yyDollar[3].str @@ -27200,86 +27369,86 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1615: + case 1622: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:10810 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10878 { yyVAL.str = " " + yyDollar[1].str + " " + yyDollar[2].str } - case 1616: + case 1623: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:10814 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10882 { yyVAL.str = " " + yyDollar[1].str + " " + yyDollar[2].str } - case 1617: + case 1624: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.RowFormatType -//line mysql_sql.y:10820 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10888 { yyLOCAL = tree.ROW_FORMAT_DEFAULT } yyVAL.union = yyLOCAL - case 1618: + case 1625: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.RowFormatType -//line mysql_sql.y:10824 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10892 { yyLOCAL = tree.ROW_FORMAT_DYNAMIC } yyVAL.union = yyLOCAL - case 1619: + case 1626: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.RowFormatType -//line mysql_sql.y:10828 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10896 { yyLOCAL = tree.ROW_FORMAT_FIXED } yyVAL.union = yyLOCAL - case 1620: + case 1627: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.RowFormatType -//line mysql_sql.y:10832 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10900 { yyLOCAL = tree.ROW_FORMAT_COMPRESSED } yyVAL.union = yyLOCAL - case 1621: + case 1628: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.RowFormatType -//line mysql_sql.y:10836 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10904 { yyLOCAL = tree.ROW_FORMAT_REDUNDANT } yyVAL.union = yyLOCAL - case 1622: + case 1629: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.RowFormatType -//line mysql_sql.y:10840 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10908 { yyLOCAL = tree.ROW_FORMAT_COMPACT } yyVAL.union = yyLOCAL - case 1627: + case 1634: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableNames -//line mysql_sql.y:10854 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10922 { yyLOCAL = tree.TableNames{yyDollar[1].tableNameUnion()} } yyVAL.union = yyLOCAL - case 1628: + case 1635: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableNames -//line mysql_sql.y:10858 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10926 { yyLOCAL = append(yyDollar[1].tableNamesUnion(), yyDollar[3].tableNameUnion()) } yyVAL.union = yyLOCAL - case 1629: + case 1636: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.TableName -//line mysql_sql.y:10867 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10935 { tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) prefix := tree.ObjectNamePrefix{ExplicitSchema: false} @@ -27287,10 +27456,10 @@ yydefault: yyLOCAL.IcebergRef = yyDollar[3].icebergRefSpecUnion() } yyVAL.union = yyLOCAL - case 1630: + case 1637: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.TableName -//line mysql_sql.y:10874 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10942 { dbName := yylex.(*Lexer).GetDbOrTblName(yyDollar[1].cstrUnion().Origin()) tblName := yylex.(*Lexer).GetDbOrTblName(yyDollar[3].cstrUnion().Origin()) @@ -27299,85 +27468,85 @@ yydefault: yyLOCAL.IcebergRef = yyDollar[5].icebergRefSpecUnion() } yyVAL.union = yyLOCAL - case 1631: + case 1638: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.IcebergRefSpec -//line mysql_sql.y:10883 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10951 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1632: + case 1639: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IcebergRefSpec -//line mysql_sql.y:10887 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10955 { raw := fmt.Sprintf("%v", yyDollar[3].item) yyLOCAL = tree.NewIcebergSnapshotRef(tree.NewNumVal(raw, raw, false, tree.P_int64)) } yyVAL.union = yyLOCAL - case 1633: + case 1640: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IcebergRefSpec -//line mysql_sql.y:10892 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10960 { yyLOCAL = tree.NewIcebergSnapshotRef(tree.NewNumVal(yyDollar[3].str, yyDollar[3].str, false, tree.P_char)) } yyVAL.union = yyLOCAL - case 1634: + case 1641: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.IcebergRefSpec -//line mysql_sql.y:10896 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10964 { yyLOCAL = tree.NewIcebergTimestampRef(tree.NewNumVal(yyDollar[6].str, yyDollar[6].str, false, tree.P_char)) } yyVAL.union = yyLOCAL - case 1635: + case 1642: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.IcebergRefSpec -//line mysql_sql.y:10900 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10968 { yyLOCAL = tree.NewIcebergTimestampRef(tree.NewNumVal(yyDollar[5].str, yyDollar[5].str, false, tree.P_char)) } yyVAL.union = yyLOCAL - case 1636: + case 1643: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.IcebergRefSpec -//line mysql_sql.y:10904 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10972 { yyLOCAL = tree.NewIcebergNamedRef(tree.Identifier(yyDollar[3].str)) } yyVAL.union = yyLOCAL - case 1637: + case 1644: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10910 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10978 { yyVAL.str = yyDollar[1].str } - case 1638: + case 1645: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10914 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10982 { yyVAL.str = yyDollar[1].str } - case 1639: + case 1646: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:10918 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10986 { yyVAL.str = yyDollar[1].str } - case 1640: + case 1647: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.AtTimeStamp -//line mysql_sql.y:10923 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10991 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1641: + case 1648: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.AtTimeStamp -//line mysql_sql.y:10927 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:10995 { yyLOCAL = &tree.AtTimeStamp{ Type: tree.ATTIMESTAMPTIME, @@ -27385,10 +27554,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1642: + case 1649: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.AtTimeStamp -//line mysql_sql.y:10934 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11002 { var str = yyDollar[4].cstrUnion().Compare() yyLOCAL = &tree.AtTimeStamp{ @@ -27398,10 +27567,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1643: + case 1650: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.AtTimeStamp -//line mysql_sql.y:10943 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11011 { yyLOCAL = &tree.AtTimeStamp{ Type: tree.ATTIMESTAMPSNAPSHOT, @@ -27410,10 +27579,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1644: + case 1651: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.AtTimeStamp -//line mysql_sql.y:10951 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11019 { yyLOCAL = &tree.AtTimeStamp{ Type: tree.ATMOTIMESTAMP, @@ -27421,10 +27590,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1645: + case 1652: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.AtTimeStamp -//line mysql_sql.y:10958 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11026 { yyLOCAL = &tree.AtTimeStamp{ Type: tree.ASOFTIMESTAMP, @@ -27432,74 +27601,74 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1646: + case 1653: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.TableDefs -//line mysql_sql.y:10966 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11034 { yyLOCAL = tree.TableDefs(nil) } yyVAL.union = yyLOCAL - case 1648: + case 1655: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDefs -//line mysql_sql.y:10973 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11041 { yyLOCAL = tree.TableDefs{yyDollar[1].tableDefUnion()} } yyVAL.union = yyLOCAL - case 1649: + case 1656: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.TableDefs -//line mysql_sql.y:10977 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11045 { yyLOCAL = append(yyDollar[1].tableDefsUnion(), yyDollar[3].tableDefUnion()) } yyVAL.union = yyLOCAL - case 1650: + case 1657: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:10983 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11051 { yyLOCAL = tree.TableDef(yyDollar[1].columnTableDefUnion()) } yyVAL.union = yyLOCAL - case 1651: + case 1658: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:10987 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11055 { yyLOCAL = yyDollar[1].tableDefUnion() } yyVAL.union = yyLOCAL - case 1652: + case 1659: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:10991 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11059 { yyLOCAL = yyDollar[1].tableDefUnion() } yyVAL.union = yyLOCAL - case 1653: + case 1660: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:10997 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11065 { yyLOCAL = yyDollar[1].tableDefUnion() } yyVAL.union = yyLOCAL - case 1654: + case 1661: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11001 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11069 { yyLOCAL = yyDollar[1].tableDefUnion() } yyVAL.union = yyLOCAL - case 1655: + case 1662: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11007 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11075 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].str @@ -27513,10 +27682,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1656: + case 1663: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11020 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11088 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].str @@ -27530,10 +27699,29 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1657: + case 1664: + yyDollar = yyS[yypt-7 : yypt+1] + var yyLOCAL tree.TableDef +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11101 + { + var KeyParts = yyDollar[5].keyPartsUnion() + var Name = yyDollar[3].str + var Empty = true + var IndexOption = yyDollar[7].indexOptionUnion() + fti := tree.NewFullTextIndex( + KeyParts, + Name, + Empty, + IndexOption, + ) + fti.IsV2 = true + yyLOCAL = fti + } + yyVAL.union = yyLOCAL + case 1665: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11033 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11116 { keyTyp := tree.INDEX_TYPE_INVALID if yyDollar[3].strsUnion()[1] != "" { @@ -27579,10 +27767,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1658: + case 1666: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11078 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11161 { keyTyp := tree.INDEX_TYPE_INVALID if yyDollar[3].strsUnion()[1] != "" { @@ -27627,10 +27815,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1659: + case 1667: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11123 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11206 { if yyDollar[1].str != "" { switch v := yyDollar[2].tableDefUnion().(type) { @@ -27647,18 +27835,18 @@ yydefault: yyLOCAL = yyDollar[2].tableDefUnion() } yyVAL.union = yyLOCAL - case 1660: + case 1668: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11138 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11221 { yyLOCAL = yyDollar[1].tableDefUnion() } yyVAL.union = yyLOCAL - case 1661: + case 1669: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11144 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11227 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].strsUnion()[0] @@ -27672,10 +27860,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1662: + case 1670: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11157 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11240 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].strsUnion()[0] @@ -27689,10 +27877,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1663: + case 1671: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11170 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11253 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].strsUnion()[0] @@ -27706,10 +27894,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1664: + case 1672: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11183 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11266 { var KeyParts = yyDollar[5].keyPartsUnion() var Name = yyDollar[3].strsUnion()[0] @@ -27723,10 +27911,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1665: + case 1673: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11196 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11279 { var IfNotExists = yyDollar[3].ifNotExistsUnion() var KeyParts = yyDollar[6].keyPartsUnion() @@ -27742,10 +27930,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1666: + case 1674: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.TableDef -//line mysql_sql.y:11211 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11294 { var Expr = yyDollar[3].exprUnion() var Enforced = yyDollar[5].boolValUnion() @@ -27755,343 +27943,343 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1667: + case 1675: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:11221 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11304 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1669: + case 1677: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:11227 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11310 { yyVAL.str = "" } - case 1670: + case 1678: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11231 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11314 { yyVAL.str = yyDollar[1].str } - case 1673: + case 1681: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:11241 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11324 { yyLOCAL = make([]string, 2) yyLOCAL[0] = yyDollar[1].str yyLOCAL[1] = "" } yyVAL.union = yyLOCAL - case 1674: + case 1682: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:11247 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11330 { yyLOCAL = make([]string, 2) yyLOCAL[0] = yyDollar[1].str yyLOCAL[1] = yyDollar[3].str } yyVAL.union = yyLOCAL - case 1675: + case 1683: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:11253 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11336 { yyLOCAL = make([]string, 2) yyLOCAL[0] = yyDollar[1].cstrUnion().Compare() yyLOCAL[1] = yyDollar[3].str } yyVAL.union = yyLOCAL - case 1689: + case 1698: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:11277 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11361 { yyVAL.str = "" } - case 1690: + case 1699: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11281 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11365 { yyVAL.str = yyDollar[1].cstrUnion().Compare() } - case 1691: + case 1700: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.ColumnTableDef -//line mysql_sql.y:11287 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11371 { yyLOCAL = tree.NewColumnTableDef(yyDollar[1].unresolvedNameUnion(), yyDollar[2].columnTypeUnion(), yyDollar[3].columnAttributesUnion()) } yyVAL.union = yyLOCAL - case 1692: + case 1701: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:11293 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11377 { yyLOCAL = tree.NewUnresolvedName(yyDollar[1].cstrUnion()) } yyVAL.union = yyLOCAL - case 1693: + case 1702: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:11297 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11381 { tblNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedName(tblNameCStr, yyDollar[3].cstrUnion()) } yyVAL.union = yyLOCAL - case 1694: + case 1703: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:11302 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11386 { dbNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) tblNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[3].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedName(dbNameCStr, tblNameCStr, yyDollar[5].cstrUnion()) } yyVAL.union = yyLOCAL - case 1695: + case 1704: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:11310 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11394 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1696: + case 1705: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:11314 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11398 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1697: + case 1706: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:11318 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11402 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1698: + case 1707: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:11322 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11406 { yyLOCAL = tree.NewCStr(yyDollar[1].str, 1) } yyVAL.union = yyLOCAL - case 1699: + case 1708: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.CStr -//line mysql_sql.y:11328 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11412 { yyLOCAL = yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) } yyVAL.union = yyLOCAL - case 1700: + case 1709: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:11334 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11418 { yyLOCAL = tree.NewUnresolvedName(yyDollar[1].cstrUnion()) } yyVAL.union = yyLOCAL - case 1701: + case 1710: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:11338 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11422 { tblNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedName(tblNameCStr, yyDollar[3].cstrUnion()) } yyVAL.union = yyLOCAL - case 1702: + case 1711: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.UnresolvedName -//line mysql_sql.y:11343 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11427 { dbNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[1].cstrUnion().Origin()) tblNameCStr := yylex.(*Lexer).GetDbOrTblNameCStr(yyDollar[3].cstrUnion().Origin()) yyLOCAL = tree.NewUnresolvedName(dbNameCStr, tblNameCStr, yyDollar[5].cstrUnion()) } yyVAL.union = yyLOCAL - case 1703: + case 1712: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []tree.ColumnAttribute -//line mysql_sql.y:11350 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11434 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1704: + case 1713: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.ColumnAttribute -//line mysql_sql.y:11354 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11438 { yyLOCAL = yyDollar[1].columnAttributesUnion() } yyVAL.union = yyLOCAL - case 1705: + case 1714: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []tree.ColumnAttribute -//line mysql_sql.y:11360 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11444 { yyLOCAL = []tree.ColumnAttribute{yyDollar[1].columnAttributeUnion()} } yyVAL.union = yyLOCAL - case 1706: + case 1715: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []tree.ColumnAttribute -//line mysql_sql.y:11364 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11448 { yyLOCAL = append(yyDollar[1].columnAttributesUnion(), yyDollar[2].columnAttributeUnion()) } yyVAL.union = yyLOCAL - case 1707: + case 1716: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11370 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11454 { yyLOCAL = tree.NewAttributeNull(true) } yyVAL.union = yyLOCAL - case 1708: + case 1717: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11374 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11458 { yyLOCAL = tree.NewAttributeNull(false) } yyVAL.union = yyLOCAL - case 1709: + case 1718: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11378 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11462 { yyLOCAL = tree.NewAttributeDefault(yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1710: + case 1719: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11382 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11466 { yyLOCAL = tree.NewAttributeAutoIncrement() } yyVAL.union = yyLOCAL - case 1711: + case 1720: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11386 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11470 { yyLOCAL = yyDollar[1].columnAttributeUnion() } yyVAL.union = yyLOCAL - case 1712: + case 1721: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11390 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11474 { str := util.DealCommentString(yyDollar[2].str) yyLOCAL = tree.NewAttributeComment(tree.NewNumVal(str, str, false, tree.P_char)) } yyVAL.union = yyLOCAL - case 1713: + case 1722: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11395 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11479 { yyLOCAL = tree.NewAttributeMongoDBPath(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 1714: + case 1723: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11399 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11483 { yyLOCAL = tree.NewAttributeMongoDBConvert(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 1715: + case 1724: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11403 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11487 { yyLOCAL = tree.NewAttributeCollate(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 1716: + case 1725: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11407 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11491 { yyLOCAL = tree.NewAttributeColumnFormat(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 1717: + case 1726: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11411 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11495 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1718: + case 1727: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11415 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11499 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1719: + case 1728: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11419 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11503 { yyLOCAL = tree.NewAttributeStorage(yyDollar[2].str) } yyVAL.union = yyLOCAL - case 1720: + case 1729: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11423 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11507 { yyLOCAL = tree.NewAttributeAutoRandom(int(yyDollar[2].int64ValUnion())) } yyVAL.union = yyLOCAL - case 1721: + case 1730: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11427 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11511 { yyLOCAL = yyDollar[1].attributeReferenceUnion() } yyVAL.union = yyLOCAL - case 1722: + case 1731: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11431 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11515 { yyLOCAL = tree.NewAttributeCheckConstraint(yyDollar[4].exprUnion(), true, yyDollar[1].str) } yyVAL.union = yyLOCAL - case 1723: + case 1732: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11435 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11519 { yyLOCAL = tree.NewAttributeCheckConstraint(yyDollar[4].exprUnion(), yyDollar[6].boolValUnion(), yyDollar[1].str) } yyVAL.union = yyLOCAL - case 1724: + case 1733: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11439 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11523 { name := tree.NewUnresolvedColName(yyDollar[3].str) var es tree.Exprs = nil @@ -28106,10 +28294,10 @@ yydefault: yyLOCAL = tree.NewAttributeOnUpdate(expr) } yyVAL.union = yyLOCAL - case 1725: + case 1734: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11453 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11537 { v, errStr := util.GetInt64(yyDollar[2].item) if errStr != "" { @@ -28123,122 +28311,122 @@ yydefault: yyLOCAL = tree.NewAttributeSRID(uint32(v)) } yyVAL.union = yyLOCAL - case 1726: + case 1735: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11466 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11550 { yyLOCAL = tree.NewAttributeLowCardinality() } yyVAL.union = yyLOCAL - case 1727: + case 1736: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11470 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11554 { yyLOCAL = tree.NewAttributeVisable(true) } yyVAL.union = yyLOCAL - case 1728: + case 1737: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11474 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11558 { yyLOCAL = tree.NewAttributeVisable(false) } yyVAL.union = yyLOCAL - case 1729: + case 1738: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11478 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11562 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1730: + case 1739: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11482 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11566 { yyLOCAL = tree.NewAttributeGeneratedAlways(yyDollar[5].exprUnion(), yyDollar[7].boolValUnion()) } yyVAL.union = yyLOCAL - case 1731: + case 1740: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:11486 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11570 { yyLOCAL = tree.NewAttributeGeneratedAlways(yyDollar[3].exprUnion(), yyDollar[5].boolValUnion()) } yyVAL.union = yyLOCAL - case 1732: + case 1741: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:11491 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11575 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1733: + case 1742: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:11495 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11579 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1734: + case 1743: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:11499 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11583 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1735: + case 1744: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:11505 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11589 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 1736: + case 1745: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:11509 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11593 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 1737: + case 1746: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:11514 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11598 { yyVAL.str = "" } - case 1738: + case 1747: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11518 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11602 { yyVAL.str = yyDollar[1].str } - case 1739: + case 1748: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:11524 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11608 { yyVAL.str = "" } - case 1740: + case 1749: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:11528 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11612 { yyVAL.str = yyDollar[2].cstrUnion().Compare() } - case 1741: + case 1750: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.AttributeReference -//line mysql_sql.y:11534 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11618 { var TableName = yyDollar[2].tableNameUnion() var KeyParts = yyDollar[3].keyPartsUnion() @@ -28254,10 +28442,10 @@ yydefault: ) } yyVAL.union = yyLOCAL - case 1742: + case 1751: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.ReferenceOnRecord -//line mysql_sql.y:11551 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11635 { yyLOCAL = &tree.ReferenceOnRecord{ OnDelete: tree.REFERENCE_OPTION_INVALID, @@ -28265,10 +28453,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1743: + case 1752: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.ReferenceOnRecord -//line mysql_sql.y:11558 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11642 { yyLOCAL = &tree.ReferenceOnRecord{ OnDelete: yyDollar[1].referenceOptionTypeUnion(), @@ -28276,10 +28464,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1744: + case 1753: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.ReferenceOnRecord -//line mysql_sql.y:11565 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11649 { yyLOCAL = &tree.ReferenceOnRecord{ OnDelete: tree.REFERENCE_OPTION_INVALID, @@ -28287,10 +28475,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1745: + case 1754: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ReferenceOnRecord -//line mysql_sql.y:11572 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11656 { yyLOCAL = &tree.ReferenceOnRecord{ OnDelete: yyDollar[1].referenceOptionTypeUnion(), @@ -28298,10 +28486,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1746: + case 1755: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.ReferenceOnRecord -//line mysql_sql.y:11579 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11663 { yyLOCAL = &tree.ReferenceOnRecord{ OnDelete: yyDollar[2].referenceOptionTypeUnion(), @@ -28309,274 +28497,282 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1747: + case 1756: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:11588 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11672 { yyLOCAL = yyDollar[3].referenceOptionTypeUnion() } yyVAL.union = yyLOCAL - case 1748: + case 1757: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:11594 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11678 { yyLOCAL = yyDollar[3].referenceOptionTypeUnion() } yyVAL.union = yyLOCAL - case 1749: + case 1758: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:11600 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11684 { yyLOCAL = tree.REFERENCE_OPTION_RESTRICT } yyVAL.union = yyLOCAL - case 1750: + case 1759: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:11604 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11688 { yyLOCAL = tree.REFERENCE_OPTION_CASCADE } yyVAL.union = yyLOCAL - case 1751: + case 1760: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:11608 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11692 { yyLOCAL = tree.REFERENCE_OPTION_SET_NULL } yyVAL.union = yyLOCAL - case 1752: + case 1761: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:11612 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11696 { yyLOCAL = tree.REFERENCE_OPTION_NO_ACTION } yyVAL.union = yyLOCAL - case 1753: + case 1762: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ReferenceOptionType -//line mysql_sql.y:11616 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11700 { yyLOCAL = tree.REFERENCE_OPTION_SET_DEFAULT } yyVAL.union = yyLOCAL - case 1754: + case 1763: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.MatchType -//line mysql_sql.y:11621 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11705 { yyLOCAL = tree.MATCH_INVALID } yyVAL.union = yyLOCAL - case 1756: + case 1765: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.MatchType -//line mysql_sql.y:11628 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11712 { yyLOCAL = tree.MATCH_FULL } yyVAL.union = yyLOCAL - case 1757: + case 1766: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.MatchType -//line mysql_sql.y:11632 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11716 { yyLOCAL = tree.MATCH_PARTIAL } yyVAL.union = yyLOCAL - case 1758: + case 1767: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.MatchType -//line mysql_sql.y:11636 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11720 { yyLOCAL = tree.MATCH_SIMPLE } yyVAL.union = yyLOCAL - case 1759: + case 1768: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.FullTextSearchType -//line mysql_sql.y:11641 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11725 { yyLOCAL = tree.FULLTEXT_DEFAULT } yyVAL.union = yyLOCAL - case 1760: + case 1769: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.FullTextSearchType -//line mysql_sql.y:11645 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11729 { yyLOCAL = tree.FULLTEXT_NL } yyVAL.union = yyLOCAL - case 1761: + case 1770: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.FullTextSearchType -//line mysql_sql.y:11649 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11733 { yyLOCAL = tree.FULLTEXT_NL_QUERY_EXPANSION } yyVAL.union = yyLOCAL - case 1762: + case 1771: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.FullTextSearchType -//line mysql_sql.y:11653 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11737 { yyLOCAL = tree.FULLTEXT_BOOLEAN } yyVAL.union = yyLOCAL - case 1763: + case 1772: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.FullTextSearchType -//line mysql_sql.y:11657 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11741 { yyLOCAL = tree.FULLTEXT_QUERY_EXPANSION } yyVAL.union = yyLOCAL - case 1764: + case 1773: + yyDollar = yyS[yypt-3 : yypt+1] + var yyLOCAL tree.FullTextSearchType +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11745 + { + yyLOCAL = tree.FULLTEXT_BM25 + } + yyVAL.union = yyLOCAL + case 1774: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL []*tree.KeyPart -//line mysql_sql.y:11662 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11750 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1765: + case 1775: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []*tree.KeyPart -//line mysql_sql.y:11666 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11754 { yyLOCAL = yyDollar[2].keyPartsUnion() } yyVAL.union = yyLOCAL - case 1766: + case 1776: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:11671 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11759 { yyLOCAL = -1 } yyVAL.union = yyLOCAL - case 1767: + case 1777: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int64 -//line mysql_sql.y:11675 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11763 { yyLOCAL = yyDollar[2].item.(int64) } yyVAL.union = yyLOCAL - case 1774: + case 1784: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.Subquery -//line mysql_sql.y:11691 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11779 { yyLOCAL = &tree.Subquery{Select: yyDollar[1].selectStatementUnion(), Exists: false} } yyVAL.union = yyLOCAL - case 1775: + case 1785: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11697 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11785 { yyLOCAL = tree.NewBinaryExpr(tree.BIT_AND, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1776: + case 1786: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11701 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11789 { yyLOCAL = tree.NewBinaryExpr(tree.BIT_OR, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1777: + case 1787: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11705 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11793 { yyLOCAL = tree.NewBinaryExpr(tree.BIT_XOR, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1778: + case 1788: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11709 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11797 { yyLOCAL = tree.NewBinaryExpr(tree.PLUS, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1779: + case 1789: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11713 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11801 { yyLOCAL = tree.NewBinaryExpr(tree.MINUS, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1780: + case 1790: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11717 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11805 { yyLOCAL = tree.NewBinaryExpr(tree.MULTI, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1781: + case 1791: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11721 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11809 { yyLOCAL = tree.NewBinaryExpr(tree.DIV, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1782: + case 1792: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11725 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11813 { yyLOCAL = tree.NewBinaryExpr(tree.INTEGER_DIV, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1783: + case 1793: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11729 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11817 { yyLOCAL = tree.NewBinaryExpr(tree.MOD, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1784: + case 1794: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11733 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11821 { yyLOCAL = tree.NewBinaryExpr(tree.MOD, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1785: + case 1795: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11737 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11825 { yyLOCAL = tree.NewBinaryExpr(tree.LEFT_SHIFT, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1786: + case 1796: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11741 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11829 { yyLOCAL = tree.NewBinaryExpr(tree.RIGHT_SHIFT, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 1787: + case 1797: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11745 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11833 { name := tree.NewUnresolvedColName("json_extract") yyLOCAL = &tree.FuncExpr{ @@ -28586,10 +28782,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1788: + case 1798: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11754 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11842 { extractName := tree.NewUnresolvedColName("json_extract") inner := &tree.FuncExpr{ @@ -28605,42 +28801,42 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1789: + case 1799: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11769 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11857 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1790: + case 1800: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11775 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11863 { yyLOCAL = yyDollar[1].unresolvedNameUnion() } yyVAL.union = yyLOCAL - case 1791: + case 1801: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11779 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11867 { yyLOCAL = yyDollar[1].varExprUnion() } yyVAL.union = yyLOCAL - case 1792: + case 1802: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11783 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11871 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1793: + case 1803: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11787 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11875 { name := tree.NewUnresolvedColName("concat") yyLOCAL = &tree.FuncExpr{ @@ -28650,58 +28846,58 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1794: + case 1804: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11796 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11884 { yyLOCAL = tree.NewParentExpr(yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1795: + case 1805: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11800 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11888 { yyLOCAL = tree.NewTuple(append(yyDollar[2].exprsUnion(), yyDollar[4].exprUnion())) } yyVAL.union = yyLOCAL - case 1796: + case 1806: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11804 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11892 { yyLOCAL = tree.NewUnaryExpr(tree.UNARY_PLUS, yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1797: + case 1807: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11808 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11896 { yyLOCAL = tree.NewUnaryExpr(tree.UNARY_MINUS, yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1798: + case 1808: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11812 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11900 { yyLOCAL = tree.NewUnaryExpr(tree.UNARY_TILDE, yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1799: + case 1809: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11816 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11904 { yyLOCAL = tree.NewUnaryExpr(tree.UNARY_MARK, yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 1800: + case 1810: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11820 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11908 { hint := strings.ToLower(yyDollar[2].cstrUnion().Compare()) switch hint { @@ -28744,35 +28940,35 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1801: + case 1811: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11862 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11950 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1802: + case 1812: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11866 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11954 { yyLOCAL = yyDollar[1].subqueryUnion() } yyVAL.union = yyLOCAL - case 1803: + case 1813: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11870 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11958 { yyDollar[2].subqueryUnion().Exists = true yyLOCAL = yyDollar[2].subqueryUnion() } yyVAL.union = yyLOCAL - case 1804: + case 1814: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11875 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11963 { yyLOCAL = &tree.CaseExpr{ Expr: yyDollar[2].exprUnion(), @@ -28781,50 +28977,50 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1805: + case 1815: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11883 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11971 { yyLOCAL = tree.NewCastExpr(yyDollar[3].exprUnion(), yyDollar[5].columnTypeUnion()) } yyVAL.union = yyLOCAL - case 1806: + case 1816: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11887 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11975 { yyLOCAL = tree.NewSerialExtractExpr(yyDollar[3].exprUnion(), yyDollar[5].exprUnion(), yyDollar[7].columnTypeUnion()) } yyVAL.union = yyLOCAL - case 1807: + case 1817: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11891 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11979 { yyLOCAL = tree.NewBitCastExpr(yyDollar[3].exprUnion(), yyDollar[5].columnTypeUnion()) } yyVAL.union = yyLOCAL - case 1808: + case 1818: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11895 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11983 { yyLOCAL = tree.NewCastExpr(yyDollar[1].exprUnion(), yyDollar[3].columnTypeUnion()) } yyVAL.union = yyLOCAL - case 1809: + case 1819: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11899 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11987 { yyLOCAL = tree.NewCastExpr(yyDollar[3].exprUnion(), yyDollar[5].columnTypeUnion()) } yyVAL.union = yyLOCAL - case 1810: + case 1820: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11903 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:11991 { name := tree.NewUnresolvedColName(yyDollar[1].str) es := tree.NewNumVal(yyDollar[5].str, yyDollar[5].str, false, tree.P_char) @@ -28835,66 +29031,66 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1811: + case 1821: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11913 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12001 { yyLOCAL = yyDollar[1].funcExprUnion() } yyVAL.union = yyLOCAL - case 1812: + case 1822: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11917 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12005 { yyLOCAL = yyDollar[1].funcExprUnion() } yyVAL.union = yyLOCAL - case 1813: + case 1823: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11921 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12009 { yyLOCAL = yyDollar[1].funcExprUnion() } yyVAL.union = yyLOCAL - case 1814: + case 1824: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11925 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12013 { yyLOCAL = yyDollar[1].funcExprUnion() } yyVAL.union = yyLOCAL - case 1815: + case 1825: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11929 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12017 { yyLOCAL = yyDollar[1].funcExprUnion() } yyVAL.union = yyLOCAL - case 1816: + case 1826: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11933 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12021 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1817: + case 1827: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11937 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12025 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1818: + case 1828: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11941 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12029 { val, err := tree.NewFullTextMatchFuncExpression(yyDollar[3].keyPartsUnion(), yyDollar[7].exprUnion(), yyDollar[8].fullTextSearchTypeUnion()) if err != nil { @@ -28904,26 +29100,26 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1819: + case 1829: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11952 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12039 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_char) } yyVAL.union = yyLOCAL - case 1820: + case 1830: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:11956 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12043 { yyLOCAL = tree.NewParamExpr(yylex.(*Lexer).GetParamIndex()) } yyVAL.union = yyLOCAL - case 1821: + case 1831: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:11962 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12049 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -28933,10 +29129,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1822: + case 1832: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:11971 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12058 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -28946,10 +29142,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1823: + case 1833: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:11980 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12067 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -28959,10 +29155,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1824: + case 1834: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:11989 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12076 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -28972,10 +29168,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1825: + case 1835: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:11998 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12085 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -28986,10 +29182,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1826: + case 1836: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12008 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12095 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -28999,10 +29195,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1827: + case 1837: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12017 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12104 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29013,10 +29209,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1828: + case 1838: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12027 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12114 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29027,10 +29223,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1829: + case 1839: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12037 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12124 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29041,10 +29237,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1830: + case 1840: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12047 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12134 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29055,10 +29251,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1831: + case 1841: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12057 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12144 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29069,10 +29265,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1832: + case 1842: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12067 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12154 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29083,10 +29279,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1833: + case 1843: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12077 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12164 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29097,10 +29293,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1834: + case 1844: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12087 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12174 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29111,10 +29307,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1835: + case 1845: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12097 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12184 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29125,10 +29321,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1836: + case 1846: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12109 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12196 { v := int(yyDollar[5].item.(int64)) val, err := tree.NewSampleRowsFuncExpression(v, true, nil, "block") @@ -29139,10 +29335,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1837: + case 1847: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12119 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12206 { v := int(yyDollar[5].item.(int64)) val, err := tree.NewSampleRowsFuncExpression(v, true, nil, yyDollar[8].str) @@ -29153,10 +29349,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1838: + case 1848: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12129 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12216 { val, err := tree.NewSamplePercentFuncExpression1(yyDollar[5].item.(int64), true, nil) if err != nil { @@ -29166,10 +29362,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1839: + case 1849: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12138 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12225 { val, err := tree.NewSamplePercentFuncExpression2(yyDollar[5].item.(float64), true, nil) if err != nil { @@ -29179,10 +29375,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1840: + case 1850: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12148 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12235 { v := int(yyDollar[5].item.(int64)) val, err := tree.NewSampleRowsFuncExpression(v, false, yyDollar[3].exprsUnion(), "block") @@ -29193,10 +29389,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1841: + case 1851: yyDollar = yyS[yypt-9 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12158 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12245 { v := int(yyDollar[5].item.(int64)) val, err := tree.NewSampleRowsFuncExpression(v, false, yyDollar[3].exprsUnion(), yyDollar[8].str) @@ -29207,10 +29403,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1842: + case 1852: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12168 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12255 { val, err := tree.NewSamplePercentFuncExpression1(yyDollar[5].item.(int64), false, yyDollar[3].exprsUnion()) if err != nil { @@ -29220,10 +29416,10 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1843: + case 1853: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12177 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12264 { val, err := tree.NewSamplePercentFuncExpression2(yyDollar[5].item.(float64), false, yyDollar[3].exprsUnion()) if err != nil { @@ -29233,58 +29429,58 @@ yydefault: yyLOCAL = val } yyVAL.union = yyLOCAL - case 1844: + case 1854: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12187 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12274 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1845: + case 1855: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12191 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12278 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 1846: + case 1856: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12196 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12283 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1847: + case 1857: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:12200 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12287 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 1848: + case 1858: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []*tree.When -//line mysql_sql.y:12206 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12293 { yyLOCAL = []*tree.When{yyDollar[1].whenClauseUnion()} } yyVAL.union = yyLOCAL - case 1849: + case 1859: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL []*tree.When -//line mysql_sql.y:12210 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12297 { yyLOCAL = append(yyDollar[1].whenClauseListUnion(), yyDollar[2].whenClauseUnion()) } yyVAL.union = yyLOCAL - case 1850: + case 1860: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.When -//line mysql_sql.y:12216 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12303 { yyLOCAL = &tree.When{ Cond: yyDollar[2].exprUnion(), @@ -29292,9 +29488,9 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1851: + case 1861: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:12225 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12312 { t := yyVAL.columnTypeUnion() str := strings.ToLower(t.InternalType.FamilyString) @@ -29307,10 +29503,10 @@ yydefault: } } } - case 1852: + case 1862: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:12237 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12324 { name := yyDollar[1].str if yyDollar[2].str != "" { @@ -29328,10 +29524,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1853: + case 1863: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:12254 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12341 { locale := "" yyLOCAL = &tree.T{ @@ -29346,10 +29542,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1855: + case 1865: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:12271 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12358 { locale := "" yyLOCAL = &tree.T{ @@ -29362,10 +29558,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1856: + case 1866: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:12283 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12370 { locale := "" yyLOCAL = &tree.T{ @@ -29380,10 +29576,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1857: + case 1867: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:12297 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12384 { locale := "" oid := uint32(defines.MYSQL_TYPE_STRING) @@ -29403,10 +29599,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1858: + case 1868: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:12316 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12403 { locale := "" yyLOCAL = &tree.T{ @@ -29419,10 +29615,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1859: + case 1869: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:12328 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12415 { locale := "" yyLOCAL = &tree.T{ @@ -29437,10 +29633,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1860: + case 1870: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:12342 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12429 { locale := "" yyLOCAL = &tree.T{ @@ -29456,10 +29652,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1861: + case 1871: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:12357 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12444 { locale := "" yyLOCAL = &tree.T{ @@ -29475,10 +29671,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1862: + case 1872: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:12372 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12459 { name := yyDollar[1].str if yyDollar[2].str != "" { @@ -29496,10 +29692,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1863: + case 1873: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:12389 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12476 { locale := "" yyLOCAL = &tree.T{ @@ -29514,112 +29710,112 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1864: + case 1874: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:12405 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12492 { yyVAL.str = "" } - case 1868: + case 1878: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:12414 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12501 { yyLOCAL = &tree.FrameBound{Type: tree.Following, UnBounded: true} } yyVAL.union = yyLOCAL - case 1869: + case 1879: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:12418 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12505 { yyLOCAL = &tree.FrameBound{Type: tree.Following, Expr: yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1870: + case 1880: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:12422 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12509 { yyLOCAL = &tree.FrameBound{Type: tree.Following, Expr: tree.NewParamExpr(yylex.(*Lexer).GetParamIndex())} } yyVAL.union = yyLOCAL - case 1871: + case 1881: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:12426 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12513 { yyLOCAL = &tree.FrameBound{Type: tree.Following, Expr: yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1872: + case 1882: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:12432 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12519 { yyLOCAL = &tree.FrameBound{Type: tree.CurrentRow} } yyVAL.union = yyLOCAL - case 1873: + case 1883: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:12436 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12523 { yyLOCAL = &tree.FrameBound{Type: tree.Preceding, UnBounded: true} } yyVAL.union = yyLOCAL - case 1874: + case 1884: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:12440 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12527 { yyLOCAL = &tree.FrameBound{Type: tree.Preceding, Expr: yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1875: + case 1885: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:12444 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12531 { yyLOCAL = &tree.FrameBound{Type: tree.Preceding, Expr: tree.NewParamExpr(yylex.(*Lexer).GetParamIndex())} } yyVAL.union = yyLOCAL - case 1876: + case 1886: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameBound -//line mysql_sql.y:12448 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12535 { yyLOCAL = &tree.FrameBound{Type: tree.Preceding, Expr: yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 1877: + case 1887: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FrameType -//line mysql_sql.y:12454 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12541 { yyLOCAL = tree.Rows } yyVAL.union = yyLOCAL - case 1878: + case 1888: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FrameType -//line mysql_sql.y:12458 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12545 { yyLOCAL = tree.Range } yyVAL.union = yyLOCAL - case 1879: + case 1889: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FrameType -//line mysql_sql.y:12462 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12549 { yyLOCAL = tree.Groups } yyVAL.union = yyLOCAL - case 1880: + case 1890: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FrameClause -//line mysql_sql.y:12468 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12555 { yyLOCAL = &tree.FrameClause{ Type: yyDollar[1].frameTypeUnion(), @@ -29628,10 +29824,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1881: + case 1891: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FrameClause -//line mysql_sql.y:12476 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12563 { yyLOCAL = &tree.FrameClause{ Type: yyDollar[1].frameTypeUnion(), @@ -29641,82 +29837,82 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1882: + case 1892: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.FrameClause -//line mysql_sql.y:12486 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12573 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1883: + case 1893: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.FrameClause -//line mysql_sql.y:12490 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12577 { yyLOCAL = yyDollar[1].frameClauseUnion() } yyVAL.union = yyLOCAL - case 1884: + case 1894: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:12496 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12583 { yyLOCAL = yyDollar[3].exprsUnion() } yyVAL.union = yyLOCAL - case 1885: + case 1895: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:12501 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12588 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1886: + case 1896: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:12505 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12592 { yyLOCAL = yyDollar[1].exprsUnion() } yyVAL.union = yyLOCAL - case 1887: + case 1897: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:12510 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12597 { yyVAL.str = "," } - case 1888: + case 1898: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:12514 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12601 { yyVAL.str = yyDollar[2].str } - case 1889: + case 1899: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:12519 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12606 { yyVAL.str = "1,vector_l2_ops,random,false" } - case 1890: + case 1900: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:12523 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12610 { yyVAL.str = yyDollar[2].str } - case 1891: + case 1901: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL *tree.WindowSpec -//line mysql_sql.y:12528 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12615 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 1893: + case 1903: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.WindowSpec -//line mysql_sql.y:12535 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12622 { hasFrame := true var f *tree.FrameClause @@ -29741,10 +29937,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1894: + case 1904: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12561 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12648 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29757,10 +29953,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1895: + case 1905: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12573 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12660 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29773,10 +29969,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1896: + case 1906: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12585 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12672 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29788,10 +29984,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1897: + case 1907: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12596 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12683 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29803,10 +29999,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1898: + case 1908: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12607 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12694 { name := tree.NewUnresolvedColName(yyDollar[1].str) es := tree.NewNumVal("*", "*", false, tree.P_star) @@ -29818,10 +30014,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1899: + case 1909: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12618 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12705 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29832,10 +30028,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1900: + case 1910: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12628 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12715 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29846,10 +30042,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1901: + case 1911: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12638 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12725 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29861,10 +30057,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1902: + case 1912: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12649 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12736 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29876,10 +30072,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1903: + case 1913: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12660 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12747 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29891,10 +30087,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1904: + case 1914: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12671 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12758 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29906,10 +30102,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1905: + case 1915: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12682 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12769 { name := tree.NewUnresolvedColName(yyDollar[1].str) es := tree.NewNumVal("*", "*", false, tree.P_star) @@ -29921,10 +30117,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1906: + case 1916: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12693 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12780 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29936,10 +30132,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1907: + case 1917: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12704 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12791 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29951,10 +30147,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1908: + case 1918: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12715 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12802 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29966,10 +30162,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1909: + case 1919: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12726 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12813 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29981,10 +30177,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1910: + case 1920: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12737 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12824 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -29996,10 +30192,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1911: + case 1921: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12748 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12835 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30011,10 +30207,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1912: + case 1922: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12759 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12846 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30026,10 +30222,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1913: + case 1923: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12770 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12857 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30041,10 +30237,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1914: + case 1924: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12781 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12868 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30056,10 +30252,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1915: + case 1925: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12792 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12879 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30071,10 +30267,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1916: + case 1926: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12803 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12890 { name := tree.NewUnresolvedColName(yyDollar[1].str) var columnList tree.Exprs @@ -30092,10 +30288,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1917: + case 1927: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12820 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12907 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30107,10 +30303,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1918: + case 1928: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12831 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12918 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30122,10 +30318,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1922: + case 1932: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12849 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12936 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30135,10 +30331,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1923: + case 1933: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12858 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12945 { name := tree.NewUnresolvedColName(yyDollar[1].str) exprs := tree.Exprs{yyDollar[3].exprUnion()} @@ -30150,10 +30346,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1924: + case 1934: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12869 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12956 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30163,10 +30359,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1925: + case 1935: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12878 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12965 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30176,10 +30372,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1926: + case 1936: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12887 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12974 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30189,10 +30385,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1927: + case 1937: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12896 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12983 { name := tree.NewUnresolvedColName(yyDollar[1].str) str := strings.ToLower(yyDollar[3].str) @@ -30204,10 +30400,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1928: + case 1938: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12907 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:12994 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30217,10 +30413,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1929: + case 1939: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12916 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13003 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30230,10 +30426,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1930: + case 1940: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12925 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13012 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30245,10 +30441,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1931: + case 1941: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12936 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13023 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30258,10 +30454,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1932: + case 1942: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12945 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13032 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30271,10 +30467,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1933: + case 1943: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12954 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13041 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30284,10 +30480,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1934: + case 1944: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12963 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13050 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30297,10 +30493,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1935: + case 1945: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12972 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13059 { name := tree.NewUnresolvedColName(yyDollar[1].str) arg0 := tree.NewNumVal(int64(0), "0", false, tree.P_int64) @@ -30313,10 +30509,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1936: + case 1946: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12984 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13071 { name := tree.NewUnresolvedColName(yyDollar[1].str) arg0 := tree.NewNumVal(int64(1), "1", false, tree.P_int64) @@ -30328,10 +30524,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1937: + case 1947: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:12995 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13082 { name := tree.NewUnresolvedColName(yyDollar[1].str) arg0 := tree.NewNumVal(int64(2), "2", false, tree.P_int64) @@ -30345,10 +30541,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1938: + case 1948: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13008 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13095 { name := tree.NewUnresolvedColName(yyDollar[1].str) arg0 := tree.NewNumVal(int64(3), "3", false, tree.P_int64) @@ -30361,10 +30557,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1939: + case 1949: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13020 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13107 { column := tree.NewUnresolvedColName(yyDollar[3].str) name := tree.NewUnresolvedColName(yyDollar[1].str) @@ -30375,16 +30571,16 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1946: + case 1956: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:13042 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13129 { yyVAL.str = yyDollar[1].str } - case 1979: + case 1989: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13084 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13171 { name := tree.NewUnresolvedColName(yyDollar[1].str) var es tree.Exprs = nil @@ -30398,10 +30594,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1980: + case 1990: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13097 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13184 { name := tree.NewUnresolvedColName(yyDollar[1].str) var es tree.Exprs = nil @@ -30415,10 +30611,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1981: + case 1991: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13110 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13197 { name := tree.NewUnresolvedColName(yyDollar[1].str) str := strings.ToLower(yyDollar[3].str) @@ -30430,10 +30626,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1982: + case 1992: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13121 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13208 { name := tree.NewUnresolvedColName(yyDollar[1].str) str := strings.ToLower(yyDollar[3].str) @@ -30445,10 +30641,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1983: + case 1993: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13132 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13219 { name := tree.NewUnresolvedColName(yyDollar[1].str) str := strings.ToUpper(yyDollar[3].str) @@ -30460,10 +30656,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1984: + case 1994: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13144 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13231 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30473,10 +30669,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1985: + case 1995: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13153 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13240 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30485,10 +30681,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1986: + case 1996: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13161 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13248 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30497,10 +30693,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1987: + case 1997: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13169 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13256 { name := tree.NewUnresolvedColName(yyDollar[1].str) var es tree.Exprs = nil @@ -30514,10 +30710,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1988: + case 1998: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13182 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13269 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30527,10 +30723,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1989: + case 1999: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13191 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13278 { name := tree.NewUnresolvedColName(yyDollar[1].str) exprs := make([]tree.Expr, 1) @@ -30542,10 +30738,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1990: + case 2000: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13202 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13289 { name := tree.NewUnresolvedColName(yyDollar[1].str) exprs := make([]tree.Expr, 1) @@ -30557,10 +30753,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1991: + case 2001: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13213 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13300 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30570,10 +30766,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1992: + case 2002: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13222 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13309 { cn := tree.NewNumVal(yyDollar[5].str, yyDollar[5].str, false, tree.P_char) es := yyDollar[3].exprsUnion() @@ -30586,10 +30782,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1993: + case 2003: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13234 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13321 { val := tree.NewNumVal(yyDollar[2].str, yyDollar[2].str, false, tree.P_char) name := tree.NewUnresolvedColName(yyDollar[1].str) @@ -30600,10 +30796,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1994: + case 2004: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13244 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13331 { val := tree.NewNumVal(yyDollar[2].str, yyDollar[2].str, false, tree.P_char) name := tree.NewUnresolvedColName(yyDollar[1].str) @@ -30614,10 +30810,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1995: + case 2005: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13254 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13341 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30627,10 +30823,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1996: + case 2006: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13263 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13350 { es := tree.Exprs{yyDollar[3].exprUnion()} es = append(es, yyDollar[5].exprUnion()) @@ -30642,10 +30838,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1997: + case 2007: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13274 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13361 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30655,10 +30851,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1998: + case 2008: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13283 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13370 { val := tree.NewNumVal(yyDollar[2].str, yyDollar[2].str, false, tree.P_char) name := tree.NewUnresolvedColName(yyDollar[1].str) @@ -30669,10 +30865,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 1999: + case 2009: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13293 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13380 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30682,10 +30878,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2000: + case 2010: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13302 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13389 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30695,10 +30891,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2001: + case 2011: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.FuncExpr -//line mysql_sql.y:13311 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13398 { name := tree.NewUnresolvedColName(yyDollar[1].str) yyLOCAL = &tree.FuncExpr{ @@ -30708,34 +30904,34 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2002: + case 2012: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13321 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13408 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 2003: + case 2013: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13325 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13412 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 2004: + case 2014: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13331 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13418 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 2005: + case 2015: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13335 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13422 { ival, errStr := util.GetInt64(yyDollar[2].item) if errStr != "" { @@ -30746,20 +30942,20 @@ yydefault: yyLOCAL = tree.NewNumVal(ival, str, false, tree.P_int64) } yyVAL.union = yyLOCAL - case 2012: + case 2022: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:13354 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13441 { } - case 2013: + case 2023: yyDollar = yyS[yypt-2 : yypt+1] -//line mysql_sql.y:13356 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13443 { } - case 2047: + case 2057: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13397 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13484 { name := tree.NewUnresolvedColName(yyDollar[1].str) str := strings.ToLower(yyDollar[3].str) @@ -30771,451 +30967,451 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2048: + case 2058: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.FuncType -//line mysql_sql.y:13409 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13496 { yyLOCAL = tree.FUNC_TYPE_DEFAULT } yyVAL.union = yyLOCAL - case 2049: + case 2059: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FuncType -//line mysql_sql.y:13413 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13500 { yyLOCAL = tree.FUNC_TYPE_DISTINCT } yyVAL.union = yyLOCAL - case 2050: + case 2060: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.FuncType -//line mysql_sql.y:13417 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13504 { yyLOCAL = tree.FUNC_TYPE_ALL } yyVAL.union = yyLOCAL - case 2051: + case 2061: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.Tuple -//line mysql_sql.y:13423 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13510 { yyLOCAL = tree.NewTuple(yyDollar[2].exprsUnion()) } yyVAL.union = yyLOCAL - case 2052: + case 2062: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:13428 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13515 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 2053: + case 2063: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:13432 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13519 { yyLOCAL = yyDollar[1].exprsUnion() } yyVAL.union = yyLOCAL - case 2054: + case 2064: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:13438 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13525 { yyLOCAL = tree.Exprs{yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 2055: + case 2065: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:13442 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13529 { yyLOCAL = append(yyDollar[1].exprsUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2056: + case 2066: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:13448 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13535 { yyLOCAL = tree.Exprs{yyDollar[1].exprUnion()} } yyVAL.union = yyLOCAL - case 2057: + case 2067: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Exprs -//line mysql_sql.y:13452 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13539 { yyLOCAL = append(yyDollar[1].exprsUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2058: + case 2068: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13459 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13546 { yyLOCAL = tree.NewAndExpr(yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2059: + case 2069: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13463 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13550 { yyLOCAL = tree.NewOrExpr(yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2060: + case 2070: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13467 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13554 { yyLOCAL = tree.NewXorExpr(yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2061: + case 2071: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13471 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13558 { yyLOCAL = tree.NewNotExpr(yyDollar[2].exprUnion()) } yyVAL.union = yyLOCAL - case 2062: + case 2072: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13475 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13562 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 2063: + case 2073: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13480 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13567 { yyLOCAL = yyDollar[1].exprUnion() } yyVAL.union = yyLOCAL - case 2064: + case 2074: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13484 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13571 { yyLOCAL = tree.NewMaxValue() } yyVAL.union = yyLOCAL - case 2065: + case 2075: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13490 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13577 { yyLOCAL = tree.NewIsNullExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2066: + case 2076: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13494 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13581 { yyLOCAL = tree.NewIsNotNullExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2067: + case 2077: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13498 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13585 { yyLOCAL = tree.NewIsUnknownExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2068: + case 2078: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13502 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13589 { yyLOCAL = tree.NewIsNotUnknownExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2069: + case 2079: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13506 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13593 { yyLOCAL = tree.NewIsTrueExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2070: + case 2080: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13510 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13597 { yyLOCAL = tree.NewIsNotTrueExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2071: + case 2081: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13514 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13601 { yyLOCAL = tree.NewIsFalseExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2072: + case 2082: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13518 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13605 { yyLOCAL = tree.NewIsNotFalseExpr(yyDollar[1].exprUnion()) } yyVAL.union = yyLOCAL - case 2073: + case 2083: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13522 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13609 { yyLOCAL = tree.NewComparisonExpr(yyDollar[2].comparisonOpUnion(), yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2074: + case 2084: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13526 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13613 { yyLOCAL = tree.NewSubqueryComparisonExpr(yyDollar[2].comparisonOpUnion(), yyDollar[3].comparisonOpUnion(), yyDollar[1].exprUnion(), yyDollar[4].subqueryUnion()) yyLOCAL = tree.NewSubqueryComparisonExpr(yyDollar[2].comparisonOpUnion(), yyDollar[3].comparisonOpUnion(), yyDollar[1].exprUnion(), yyDollar[4].subqueryUnion()) } yyVAL.union = yyLOCAL - case 2076: + case 2086: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13534 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13621 { yyLOCAL = tree.NewComparisonExpr(tree.IN, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2077: + case 2087: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13538 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13625 { yyLOCAL = tree.NewComparisonExpr(tree.NOT_IN, yyDollar[1].exprUnion(), yyDollar[4].exprUnion()) } yyVAL.union = yyLOCAL - case 2078: + case 2088: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13542 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13629 { yyLOCAL = tree.NewComparisonExprWithEscape(tree.LIKE, yyDollar[1].exprUnion(), yyDollar[3].exprUnion(), yyDollar[4].exprUnion()) } yyVAL.union = yyLOCAL - case 2079: + case 2089: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13546 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13633 { yyLOCAL = tree.NewComparisonExprWithEscape(tree.NOT_LIKE, yyDollar[1].exprUnion(), yyDollar[4].exprUnion(), yyDollar[5].exprUnion()) } yyVAL.union = yyLOCAL - case 2080: + case 2090: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13550 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13637 { yyLOCAL = tree.NewComparisonExprWithEscape(tree.ILIKE, yyDollar[1].exprUnion(), yyDollar[3].exprUnion(), yyDollar[4].exprUnion()) } yyVAL.union = yyLOCAL - case 2081: + case 2091: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13554 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13641 { yyLOCAL = tree.NewComparisonExprWithEscape(tree.NOT_ILIKE, yyDollar[1].exprUnion(), yyDollar[4].exprUnion(), yyDollar[5].exprUnion()) } yyVAL.union = yyLOCAL - case 2082: + case 2092: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13558 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13645 { yyLOCAL = tree.NewComparisonExpr(tree.REG_MATCH, yyDollar[1].exprUnion(), yyDollar[3].exprUnion()) } yyVAL.union = yyLOCAL - case 2083: + case 2093: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13562 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13649 { yyLOCAL = tree.NewComparisonExpr(tree.NOT_REG_MATCH, yyDollar[1].exprUnion(), yyDollar[4].exprUnion()) } yyVAL.union = yyLOCAL - case 2084: + case 2094: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13566 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13653 { yyLOCAL = tree.NewRangeCond(false, yyDollar[1].exprUnion(), yyDollar[3].exprUnion(), yyDollar[5].exprUnion()) } yyVAL.union = yyLOCAL - case 2085: + case 2095: yyDollar = yyS[yypt-6 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13570 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13657 { yyLOCAL = tree.NewRangeCond(true, yyDollar[1].exprUnion(), yyDollar[4].exprUnion(), yyDollar[6].exprUnion()) } yyVAL.union = yyLOCAL - case 2087: + case 2097: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13576 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13663 { yyLOCAL = nil } yyVAL.union = yyLOCAL - case 2088: + case 2098: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13580 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13667 { yyLOCAL = yyDollar[2].exprUnion() } yyVAL.union = yyLOCAL - case 2089: + case 2099: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13586 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13673 { yyLOCAL = yyDollar[1].tupleUnion() } yyVAL.union = yyLOCAL - case 2090: + case 2100: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13590 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13677 { yyLOCAL = yyDollar[1].subqueryUnion() } yyVAL.union = yyLOCAL - case 2091: + case 2101: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:13597 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13684 { yyLOCAL = tree.ALL } yyVAL.union = yyLOCAL - case 2092: + case 2102: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:13601 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13688 { yyLOCAL = tree.ANY } yyVAL.union = yyLOCAL - case 2093: + case 2103: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:13605 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13692 { yyLOCAL = tree.SOME } yyVAL.union = yyLOCAL - case 2094: + case 2104: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:13611 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13698 { yyLOCAL = tree.EQUAL } yyVAL.union = yyLOCAL - case 2095: + case 2105: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:13615 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13702 { yyLOCAL = tree.LESS_THAN } yyVAL.union = yyLOCAL - case 2096: + case 2106: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:13619 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13706 { yyLOCAL = tree.GREAT_THAN } yyVAL.union = yyLOCAL - case 2097: + case 2107: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:13623 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13710 { yyLOCAL = tree.LESS_THAN_EQUAL } yyVAL.union = yyLOCAL - case 2098: + case 2108: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:13627 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13714 { yyLOCAL = tree.GREAT_THAN_EQUAL } yyVAL.union = yyLOCAL - case 2099: + case 2109: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:13631 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13718 { yyLOCAL = tree.NOT_EQUAL } yyVAL.union = yyLOCAL - case 2100: + case 2110: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ComparisonOp -//line mysql_sql.y:13635 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13722 { yyLOCAL = tree.NULL_SAFE_EQUAL } yyVAL.union = yyLOCAL - case 2101: + case 2111: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:13641 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13728 { yyLOCAL = tree.NewAttributePrimaryKey() } yyVAL.union = yyLOCAL - case 2102: + case 2112: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:13645 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13732 { yyLOCAL = tree.NewAttributeUniqueKey() } yyVAL.union = yyLOCAL - case 2103: + case 2113: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:13649 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13736 { yyLOCAL = tree.NewAttributeUnique() } yyVAL.union = yyLOCAL - case 2104: + case 2114: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.ColumnAttribute -//line mysql_sql.y:13653 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13740 { yyLOCAL = tree.NewAttributeKey() } yyVAL.union = yyLOCAL - case 2105: + case 2115: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13659 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13746 { str := fmt.Sprintf("%v", yyDollar[1].item) switch v := yyDollar[1].item.(type) { @@ -31229,35 +31425,35 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2106: + case 2116: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13672 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13759 { fval := yyDollar[1].item.(float64) yyLOCAL = tree.NewNumVal(fval, yylex.(*Lexer).scanner.LastToken, false, tree.P_float64) } yyVAL.union = yyLOCAL - case 2107: + case 2117: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13677 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13764 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_decimal) } yyVAL.union = yyLOCAL - case 2108: + case 2118: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13683 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13770 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_char) } yyVAL.union = yyLOCAL - case 2109: + case 2119: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13687 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13774 { str := fmt.Sprintf("%v", yyDollar[1].item) switch v := yyDollar[1].item.(type) { @@ -31271,101 +31467,101 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2110: + case 2120: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13700 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13787 { fval := yyDollar[1].item.(float64) yyLOCAL = tree.NewNumVal(fval, yylex.(*Lexer).scanner.LastToken, false, tree.P_float64) } yyVAL.union = yyLOCAL - case 2111: + case 2121: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13705 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13792 { yyLOCAL = tree.NewNumVal(true, "true", false, tree.P_bool) } yyVAL.union = yyLOCAL - case 2112: + case 2122: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13709 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13796 { yyLOCAL = tree.NewNumVal(false, "false", false, tree.P_bool) } yyVAL.union = yyLOCAL - case 2113: + case 2123: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13713 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13800 { yyLOCAL = tree.NewNumVal("null", "null", false, tree.P_null) } yyVAL.union = yyLOCAL - case 2114: + case 2124: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13717 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13804 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_hexnum) } yyVAL.union = yyLOCAL - case 2115: + case 2125: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13721 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13808 { yyLOCAL = tree.NewNumVal(yyDollar[2].str, yyDollar[2].str, false, tree.P_ScoreBinaryHexnum) } yyVAL.union = yyLOCAL - case 2116: + case 2126: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13725 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13812 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_decimal) } yyVAL.union = yyLOCAL - case 2117: + case 2127: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13729 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13816 { yyLOCAL = tree.NewNumVal(yyDollar[1].str, yyDollar[1].str, false, tree.P_bit) } yyVAL.union = yyLOCAL - case 2118: + case 2128: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13733 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13820 { yyLOCAL = tree.NewParamExpr(yylex.(*Lexer).GetParamIndex()) } yyVAL.union = yyLOCAL - case 2119: + case 2129: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Expr -//line mysql_sql.y:13737 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13824 { yyLOCAL = tree.NewNumVal(yyDollar[2].str, yyDollar[2].str, false, tree.P_ScoreBinary) } yyVAL.union = yyLOCAL - case 2120: + case 2130: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13743 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13830 { yyLOCAL = yyDollar[1].columnTypeUnion() yyLOCAL.InternalType.Unsigned = yyDollar[2].unsignedOptUnion() yyLOCAL.InternalType.Zerofill = yyDollar[3].zeroFillOptUnion() } yyVAL.union = yyLOCAL - case 2124: + case 2134: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13752 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13839 { locale := "" yyLOCAL = &tree.T{ @@ -31379,27 +31575,27 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2125: + case 2135: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13767 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13854 { yyLOCAL = yyDollar[1].columnTypeUnion() yyLOCAL.InternalType.DisplayWith = yyDollar[2].lengthOptUnion() } yyVAL.union = yyLOCAL - case 2126: + case 2136: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13772 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13859 { yyLOCAL = yyDollar[1].columnTypeUnion() } yyVAL.union = yyLOCAL - case 2127: + case 2137: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13778 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13865 { locale := "" yyLOCAL = &tree.T{ @@ -31412,10 +31608,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2128: + case 2138: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13790 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13877 { locale := "" yyLOCAL = &tree.T{ @@ -31428,10 +31624,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2129: + case 2139: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13802 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13889 { locale := "" yyLOCAL = &tree.T{ @@ -31444,10 +31640,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2130: + case 2140: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13814 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13901 { locale := "" yyLOCAL = &tree.T{ @@ -31461,10 +31657,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2131: + case 2141: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13827 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13914 { locale := "" yyLOCAL = &tree.T{ @@ -31478,10 +31674,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2132: + case 2142: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13840 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13927 { locale := "" yyLOCAL = &tree.T{ @@ -31495,10 +31691,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2133: + case 2143: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13853 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13940 { locale := "" yyLOCAL = &tree.T{ @@ -31512,10 +31708,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2134: + case 2144: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13866 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13953 { locale := "" yyLOCAL = &tree.T{ @@ -31529,10 +31725,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2135: + case 2145: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13879 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13966 { locale := "" yyLOCAL = &tree.T{ @@ -31546,10 +31742,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2136: + case 2146: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13892 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13979 { locale := "" yyLOCAL = &tree.T{ @@ -31563,10 +31759,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2137: + case 2147: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13905 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:13992 { locale := "" yyLOCAL = &tree.T{ @@ -31580,10 +31776,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2138: + case 2148: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13918 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14005 { locale := "" yyLOCAL = &tree.T{ @@ -31597,10 +31793,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2139: + case 2149: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13931 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14018 { locale := "" yyLOCAL = &tree.T{ @@ -31614,10 +31810,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2140: + case 2150: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13944 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14031 { locale := "" yyLOCAL = &tree.T{ @@ -31631,10 +31827,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2141: + case 2151: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13959 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14046 { locale := "" if yyDollar[2].lengthScaleOptUnion().DisplayWith > 255 { @@ -31662,10 +31858,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2142: + case 2152: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:13986 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14073 { // DOUBLE PRECISION is the SQL-standard synonym for DOUBLE (float64). locale := "" @@ -31694,10 +31890,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2143: + case 2153: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14014 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14101 { locale := "" if yyDollar[2].lengthScaleOptUnion().DisplayWith > 255 { @@ -31739,10 +31935,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2144: + case 2154: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14056 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14143 { locale := "" if yyDollar[2].lengthScaleOptUnion().Scale != tree.NotDefineDec && yyDollar[2].lengthScaleOptUnion().Scale > yyDollar[2].lengthScaleOptUnion().DisplayWith { @@ -31791,10 +31987,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2145: + case 2155: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14104 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14191 { locale := "" if yyDollar[2].lengthScaleOptUnion().Scale != tree.NotDefineDec && yyDollar[2].lengthScaleOptUnion().Scale > yyDollar[2].lengthScaleOptUnion().DisplayWith { @@ -31843,10 +32039,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2146: + case 2156: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14152 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14239 { locale := "" width := int32(64) @@ -31868,10 +32064,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2147: + case 2157: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14175 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14262 { locale := "" yyLOCAL = &tree.T{ @@ -31884,10 +32080,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2148: + case 2158: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14187 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14274 { locale := "" if yyDollar[2].lengthOptUnion() < 0 || yyDollar[2].lengthOptUnion() > 6 { @@ -31908,10 +32104,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2149: + case 2159: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14207 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14294 { locale := "" if yyDollar[2].lengthOptUnion() < 0 || yyDollar[2].lengthOptUnion() > 6 { @@ -31932,10 +32128,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2150: + case 2160: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14227 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14314 { locale := "" if yyDollar[2].lengthOptUnion() < 0 || yyDollar[2].lengthOptUnion() > 6 { @@ -31956,10 +32152,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2151: + case 2161: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14247 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14334 { locale := "" yyLOCAL = &tree.T{ @@ -31974,10 +32170,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2152: + case 2162: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14263 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14350 { locale := "" yyLOCAL = &tree.T{ @@ -31991,10 +32187,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2153: + case 2163: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14276 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14363 { locale := "" yyLOCAL = &tree.T{ @@ -32008,10 +32204,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2154: + case 2164: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14289 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14376 { locale := "" yyLOCAL = &tree.T{ @@ -32025,10 +32221,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2155: + case 2165: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14302 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14389 { locale := "" yyLOCAL = &tree.T{ @@ -32042,10 +32238,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2156: + case 2166: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14315 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14402 { locale := "" yyLOCAL = &tree.T{ @@ -32058,10 +32254,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2157: + case 2167: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14327 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14414 { locale := "" yyLOCAL = &tree.T{ @@ -32074,10 +32270,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2158: + case 2168: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14339 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14426 { locale := "" yyLOCAL = &tree.T{ @@ -32090,10 +32286,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2159: + case 2169: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14351 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14438 { locale := "" yyLOCAL = &tree.T{ @@ -32106,10 +32302,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2160: + case 2170: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14363 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14450 { locale := "" yyLOCAL = &tree.T{ @@ -32122,10 +32318,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2161: + case 2171: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14375 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14462 { locale := "" yyLOCAL = &tree.T{ @@ -32138,10 +32334,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2162: + case 2172: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14387 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14474 { locale := "" yyLOCAL = &tree.T{ @@ -32154,10 +32350,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2163: + case 2173: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14399 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14486 { locale := "" yyLOCAL = &tree.T{ @@ -32170,10 +32366,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2164: + case 2174: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14411 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14498 { locale := "" yyLOCAL = &tree.T{ @@ -32186,10 +32382,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2165: + case 2175: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14423 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14510 { locale := "" yyLOCAL = &tree.T{ @@ -32202,10 +32398,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2166: + case 2176: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14435 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14522 { locale := "" yyLOCAL = &tree.T{ @@ -32218,10 +32414,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2167: + case 2177: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14447 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14534 { locale := "" yyLOCAL = &tree.T{ @@ -32234,10 +32430,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2168: + case 2178: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14459 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14546 { locale := "" yyLOCAL = &tree.T{ @@ -32251,10 +32447,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2169: + case 2179: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14472 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14559 { locale := "" yyLOCAL = &tree.T{ @@ -32268,10 +32464,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2170: + case 2180: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14485 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14572 { locale := "" yyLOCAL = &tree.T{ @@ -32285,10 +32481,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2171: + case 2181: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14498 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14585 { locale := "" yyLOCAL = &tree.T{ @@ -32302,10 +32498,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2172: + case 2182: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14511 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14598 { locale := "" yyLOCAL = &tree.T{ @@ -32319,10 +32515,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2173: + case 2183: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14524 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14611 { locale := "" yyLOCAL = &tree.T{ @@ -32336,10 +32532,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2174: + case 2184: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14537 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14624 { locale := "" yyLOCAL = &tree.T{ @@ -32353,10 +32549,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2175: + case 2185: yyDollar = yyS[yypt-4 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14550 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14637 { locale := "" yyLOCAL = &tree.T{ @@ -32370,10 +32566,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2176: + case 2186: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14563 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14650 { locale := "" yyLOCAL = &tree.T{ @@ -32387,45 +32583,45 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2177: + case 2187: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:14578 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14665 { yyLOCAL = &tree.Do{ Exprs: yyDollar[2].exprsUnion(), } } yyVAL.union = yyLOCAL - case 2178: + case 2188: yyDollar = yyS[yypt-2 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:14586 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14673 { yyDollar[2].selectUnion().IsPerform = true yyLOCAL = yyDollar[2].selectUnion() } yyVAL.union = yyLOCAL - case 2179: + case 2189: yyDollar = yyS[yypt-7 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:14593 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14680 { yyLOCAL = &tree.Select{Select: yyDollar[1].selectStatementUnion(), TimeWindow: yyDollar[2].timeWindowUnion(), OrderBy: yyDollar[3].orderByUnion(), Limit: yyDollar[4].limitUnion(), RankOption: yyDollar[5].rankOptionUnion(), Ep: yyDollar[6].exportParmUnion(), SelectLockInfo: yyDollar[7].selectLockInfoUnion()} } yyVAL.union = yyLOCAL - case 2180: + case 2190: yyDollar = yyS[yypt-8 : yypt+1] var yyLOCAL *tree.Select -//line mysql_sql.y:14597 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14684 { yyLOCAL = &tree.Select{Select: yyDollar[2].selectStatementUnion(), TimeWindow: yyDollar[3].timeWindowUnion(), OrderBy: yyDollar[4].orderByUnion(), Limit: yyDollar[5].limitUnion(), RankOption: yyDollar[6].rankOptionUnion(), Ep: yyDollar[7].exportParmUnion(), SelectLockInfo: yyDollar[8].selectLockInfoUnion(), With: yyDollar[1].withClauseUnion()} } yyVAL.union = yyLOCAL - case 2181: + case 2191: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:14603 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14690 { yyLOCAL = &tree.Declare{ Variables: yyDollar[2].strsUnion(), @@ -32434,10 +32630,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2182: + case 2192: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.Statement -//line mysql_sql.y:14612 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14699 { yyLOCAL = &tree.Declare{ Variables: yyDollar[2].strsUnion(), @@ -32446,83 +32642,83 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2183: + case 2193: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL *tree.T -//line mysql_sql.y:14622 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14709 { yyLOCAL = tree.NewSpatialType(yyDollar[1].str) } yyVAL.union = yyLOCAL - case 2202: + case 2212: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:14650 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14737 { yyLOCAL = make([]string, 0, 4) yyLOCAL = append(yyLOCAL, yyDollar[1].str) } yyVAL.union = yyLOCAL - case 2203: + case 2213: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL []string -//line mysql_sql.y:14655 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14742 { yyLOCAL = append(yyDollar[1].strsUnion(), yyDollar[3].str) } yyVAL.union = yyLOCAL - case 2204: + case 2214: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int32 -//line mysql_sql.y:14661 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14748 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 2206: + case 2216: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int32 -//line mysql_sql.y:14668 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14755 { yyLOCAL = 0 } yyVAL.union = yyLOCAL - case 2207: + case 2217: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int32 -//line mysql_sql.y:14672 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14759 { yyLOCAL = int32(yyDollar[2].item.(int64)) } yyVAL.union = yyLOCAL - case 2208: + case 2218: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL int32 -//line mysql_sql.y:14677 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14764 { yyLOCAL = int32(-1) } yyVAL.union = yyLOCAL - case 2209: + case 2219: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int32 -//line mysql_sql.y:14681 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14768 { yyLOCAL = int32(yyDollar[2].item.(int64)) } yyVAL.union = yyLOCAL - case 2210: + case 2220: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL int32 -//line mysql_sql.y:14687 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14774 { yyLOCAL = tree.GetDisplayWith(int32(yyDollar[2].item.(int64))) } yyVAL.union = yyLOCAL - case 2211: + case 2221: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.LengthScaleOpt -//line mysql_sql.y:14693 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14780 { yyLOCAL = tree.LengthScaleOpt{ DisplayWith: tree.NotDefineDisplayWidth, @@ -32530,10 +32726,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2212: + case 2222: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.LengthScaleOpt -//line mysql_sql.y:14700 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14787 { yyLOCAL = tree.LengthScaleOpt{ DisplayWith: tree.GetDisplayWith(int32(yyDollar[2].item.(int64))), @@ -32541,10 +32737,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2213: + case 2223: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.LengthScaleOpt -//line mysql_sql.y:14707 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14794 { yyLOCAL = tree.LengthScaleOpt{ DisplayWith: tree.GetDisplayWith(int32(yyDollar[2].item.(int64))), @@ -32552,10 +32748,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2214: + case 2224: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL tree.LengthScaleOpt -//line mysql_sql.y:14716 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14803 { yyLOCAL = tree.LengthScaleOpt{ DisplayWith: 38, // this is the default precision for decimal @@ -32563,10 +32759,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2215: + case 2225: yyDollar = yyS[yypt-3 : yypt+1] var yyLOCAL tree.LengthScaleOpt -//line mysql_sql.y:14723 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14810 { yyLOCAL = tree.LengthScaleOpt{ DisplayWith: tree.GetDisplayWith(int32(yyDollar[2].item.(int64))), @@ -32574,10 +32770,10 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2216: + case 2226: yyDollar = yyS[yypt-5 : yypt+1] var yyLOCAL tree.LengthScaleOpt -//line mysql_sql.y:14730 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14817 { yyLOCAL = tree.LengthScaleOpt{ DisplayWith: tree.GetDisplayWith(int32(yyDollar[2].item.(int64))), @@ -32585,52 +32781,52 @@ yydefault: } } yyVAL.union = yyLOCAL - case 2217: + case 2227: yyDollar = yyS[yypt-0 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:14739 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14826 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 2218: + case 2228: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:14743 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14830 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 2219: + case 2229: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:14747 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14834 { yyLOCAL = false } yyVAL.union = yyLOCAL - case 2220: + case 2230: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:14753 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14840 { } - case 2221: + case 2231: yyDollar = yyS[yypt-1 : yypt+1] var yyLOCAL bool -//line mysql_sql.y:14755 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14842 { yyLOCAL = true } yyVAL.union = yyLOCAL - case 2225: + case 2235: yyDollar = yyS[yypt-0 : yypt+1] -//line mysql_sql.y:14765 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14852 { yyVAL.str = "" } - case 2226: + case 2236: yyDollar = yyS[yypt-1 : yypt+1] -//line mysql_sql.y:14769 +//line pkg/sql/parsers/dialect/mysql/mysql_sql.y:14856 { yyVAL.str = string(yyDollar[1].str) } diff --git a/pkg/sql/parsers/dialect/mysql/mysql_sql.y b/pkg/sql/parsers/dialect/mysql/mysql_sql.y index 0c8e63b962aad..8b4d182685dc8 100644 --- a/pkg/sql/parsers/dialect/mysql/mysql_sql.y +++ b/pkg/sql/parsers/dialect/mysql/mysql_sql.y @@ -449,8 +449,8 @@ func sqlTaskInt64(v any) int64 { %token PROPERTIES // Secondary Index -%token PARSER VISIBLE INVISIBLE BTREE HASH RTREE BSI IVFFLAT MASTER HNSW CAGRA IVFPQ -%token ZONEMAP LEADING BOTH TRAILING UNKNOWN LISTS OP_TYPE REINDEX EF_SEARCH EF_CONSTRUCTION M ASYNC FORCE_SYNC AUTO_UPDATE INTERMEDIATE_GRAPH_DEGREE GRAPH_DEGREE QUANTIZATION BITS_PER_CODE DISTRIBUTION_MODE ITOPK_SIZE INCLUDE KMEANS_TRAIN_PERCENT KMEANS_MAX_ITERATION MAX_INDEX_CAPACITY QUANTIZER_TRAIN_LIMIT +%token PARSER VISIBLE INVISIBLE BTREE HASH RTREE BSI IVFFLAT MASTER HNSW CAGRA IVFPQ BM25 +%token ZONEMAP LEADING BOTH TRAILING UNKNOWN LISTS OP_TYPE REINDEX EF_SEARCH EF_CONSTRUCTION M ASYNC FORCE_SYNC AUTO_UPDATE INTERMEDIATE_GRAPH_DEGREE GRAPH_DEGREE QUANTIZATION BITS_PER_CODE DISTRIBUTION_MODE ITOPK_SIZE INCLUDE KMEANS_TRAIN_PERCENT KMEANS_MAX_ITERATION MAX_INDEX_CAPACITY MAX_POSTINGS_CAPACITY QUANTIZER_TRAIN_LIMIT FULLTEXT2 POSITION_FREE // Alter %token EXPIRE ACCOUNT ACCOUNTS UNLOCK DAY NEVER PUMP MYSQL_COMPATIBILITY_MODE UNIQUE_CHECK_ON_AUTOINCR @@ -4376,6 +4376,19 @@ alter_table_alter: var name = tree.Identifier($2.Compare()) $$ = tree.NewAlterOptionAlterReIndex(name, io) } +| REINDEX ident FULLTEXT2 index_option_list + { + var io *tree.IndexOption = nil + if $4 == nil { + io = tree.NewIndexOption() + io.IType = tree.INDEX_TYPE_FULLTEXT2 + } else { + io = $4 + io.IType = tree.INDEX_TYPE_FULLTEXT2 + } + var name = tree.Identifier($2.Compare()) + $$ = tree.NewAlterOptionAlterReIndex(name, io) + } | CHECK ident enforce { var checkType = $1 @@ -8863,6 +8876,10 @@ index_prefix: { $$ = tree.INDEX_CATEGORY_FULLTEXT } +| FULLTEXT2 + { + $$ = tree.INDEX_CATEGORY_FULLTEXT2 + } | SPATIAL { $$ = tree.INDEX_CATEGORY_SPATIAL @@ -8931,19 +8948,23 @@ index_option_list: } else if opt2.AlgoParamM > 0 { opt1.AlgoParamM = opt2.AlgoParamM } else if opt2.HnswEfConstruction > 0 { - opt1.HnswEfConstruction = opt2.HnswEfConstruction + opt1.HnswEfConstruction = opt2.HnswEfConstruction } else if opt2.HnswEfSearch > 0 { opt1.HnswEfSearch = opt2.HnswEfSearch - } else if opt2.Async { + } else if opt2.Async { opt1.Async = opt2.Async - } else if opt2.ForceSync { + } else if opt2.ForceSync { opt1.ForceSync = opt2.ForceSync - } else if opt2.AutoUpdate { + } else if opt2.Merge { + opt1.Merge = opt2.Merge + } else if opt2.AutoUpdate { opt1.AutoUpdate = opt2.AutoUpdate - } else if opt2.Day > 0 { + } else if opt2.Day > 0 { opt1.Day = opt2.Day - } else if opt2.Hour > 0 { + } else if opt2.Hour > 0 { opt1.Hour = opt2.Hour + } else if opt2.Second > 0 { + opt1.Second = opt2.Second } else if opt2.IntermediateGraphDegree > 0 { opt1.IntermediateGraphDegree = opt2.IntermediateGraphDegree } else if opt2.GraphDegree > 0 { @@ -8964,8 +8985,13 @@ index_option_list: opt1.KmeansMaxIteration = opt2.KmeansMaxIteration } else if opt2.MaxIndexCapacity > 0 { opt1.MaxIndexCapacity = opt2.MaxIndexCapacity + } else if opt2.MaxPostingsCapacity > 0 { + opt1.MaxPostingsCapacity = opt2.MaxPostingsCapacity } else if opt2.QuantizerTrainLimit > 0 { opt1.QuantizerTrainLimit = opt2.QuantizerTrainLimit + } else if opt2.PositionFreeSet { + opt1.PositionFree = opt2.PositionFree + opt1.PositionFreeSet = true } else if len(opt2.IncludeColumns) > 0 { opt1.IncludeColumns = opt2.IncludeColumns } @@ -9161,6 +9187,31 @@ index_option: io.MaxIndexCapacity = val $$ = io } +| MAX_POSTINGS_CAPACITY equal_opt INTEGRAL + { + val := int64($3.(int64)) + if val <= 0 { + yylex.Error("MAX_POSTINGS_CAPACITY should be greater than 0") + return 1 + } + io := tree.NewIndexOption() + io.MaxPostingsCapacity = val + $$ = io + } +| POSITION_FREE '=' TRUE + { + io := tree.NewIndexOption() + io.PositionFree = true + io.PositionFreeSet = true + $$ = io + } +| POSITION_FREE '=' FALSE + { + io := tree.NewIndexOption() + io.PositionFree = false + io.PositionFreeSet = true + $$ = io + } | ASYNC { io := tree.NewIndexOption() @@ -9170,7 +9221,13 @@ index_option: | FORCE_SYNC { io := tree.NewIndexOption() - io.ForceSync = true + io.ForceSync = true + $$ = io + } +| MERGE + { + io := tree.NewIndexOption() + io.Merge = true $$ = io } | AUTO_UPDATE '=' TRUE @@ -9207,6 +9264,17 @@ index_option: io.Hour = val $$ = io } +| SECOND equal_opt INTEGRAL + { + val := int64($3.(int64)) + if val < 0 { + yylex.Error("SECOND should be greater than or equal to 0") + return 1 + } + io := tree.NewIndexOption() + io.Second = val + $$ = io + } index_column_list: @@ -11029,6 +11097,21 @@ index_def: IndexOption, ) } +| FULLTEXT2 key_or_index_opt index_name '(' index_column_list ')' index_option_list + { + var KeyParts = $5 + var Name = $3 + var Empty = true + var IndexOption = $7 + fti := tree.NewFullTextIndex( + KeyParts, + Name, + Empty, + IndexOption, + ) + fti.IsV2 = true + $$ = fti + } | key_or_index not_exists_opt index_name_and_type_opt '(' index_column_list ')' index_option_list { keyTyp := tree.INDEX_TYPE_INVALID @@ -11267,6 +11350,7 @@ index_type: | HNSW | CAGRA | IVFPQ +| BM25 insert_method_options: NO @@ -11657,6 +11741,10 @@ fulltext_search_opt: { $$ = tree.FULLTEXT_QUERY_EXPANSION } +| IN BM25 MODE + { + $$ = tree.FULLTEXT_BM25 + } index_column_list_opt: { @@ -11944,9 +12032,8 @@ simple_expr: yylex.Error(err.Error()) goto ret1 } - $$ = val + $$ = val } - search_pattern: STRING { @@ -15058,6 +15145,7 @@ non_reserved_keyword: | HNSW | CAGRA | IVFPQ +| BM25 | PERSIST | GRANT | INCLUDE @@ -15086,7 +15174,9 @@ non_reserved_keyword: | KMEANS_TRAIN_PERCENT | KMEANS_MAX_ITERATION | MAX_INDEX_CAPACITY +| MAX_POSTINGS_CAPACITY | QUANTIZER_TRAIN_LIMIT +| POSITION_FREE | KEYS | LANGUAGE | LESS diff --git a/pkg/sql/parsers/tree/alter.go b/pkg/sql/parsers/tree/alter.go index dce2cf0fd9473..45654fd525d8c 100644 --- a/pkg/sql/parsers/tree/alter.go +++ b/pkg/sql/parsers/tree/alter.go @@ -915,6 +915,7 @@ type AlterOptionAlterReIndex struct { BitsPerCode int64 Async bool ForceSync bool + Merge bool AutoUpdate bool Day int64 Hour int64 @@ -926,6 +927,9 @@ type AlterOptionAlterReIndex struct { KmeansTrainPercent int64 KmeansMaxIteration int64 MaxIndexCapacity int64 + MaxPostingsCapacity int64 + PositionFree bool + PositionFreeSet bool QuantizerTrainLimit int64 IncludeColumns []*UnresolvedName } @@ -948,6 +952,7 @@ func NewAlterOptionAlterReIndex(name Identifier, option *IndexOption) *AlterOpti a.BitsPerCode = option.BitsPerCode a.Async = option.Async a.ForceSync = option.ForceSync + a.Merge = option.Merge a.AutoUpdate = option.AutoUpdate a.Day = option.Day a.Hour = option.Hour @@ -959,6 +964,9 @@ func NewAlterOptionAlterReIndex(name Identifier, option *IndexOption) *AlterOpti a.KmeansTrainPercent = option.KmeansTrainPercent a.KmeansMaxIteration = option.KmeansMaxIteration a.MaxIndexCapacity = option.MaxIndexCapacity + a.MaxPostingsCapacity = option.MaxPostingsCapacity + a.PositionFree = option.PositionFree + a.PositionFreeSet = option.PositionFreeSet a.QuantizerTrainLimit = option.QuantizerTrainLimit a.IncludeColumns = option.IncludeColumns return a @@ -1007,6 +1015,7 @@ func (node *AlterOptionAlterReIndex) Format(ctx *FmtCtx) { writeInt("kmeans_train_percent", node.KmeansTrainPercent) writeInt("kmeans_max_iteration", node.KmeansMaxIteration) writeInt("max_index_capacity", node.MaxIndexCapacity) + writeInt("max_postings_capacity", node.MaxPostingsCapacity) writeInt("quantizer_train_limit", node.QuantizerTrainLimit) if len(node.IncludeColumns) != 0 { ctx.WriteString(" include (") @@ -1021,6 +1030,9 @@ func (node *AlterOptionAlterReIndex) Format(ctx *FmtCtx) { if node.ForceSync { ctx.WriteString(" force_sync") } + if node.Merge { + ctx.WriteString(" merge") + } } func (node AlterOptionAlterReIndex) TypeName() string { return "tree.AlterOptionAlterReIndex" } diff --git a/pkg/sql/parsers/tree/create.go b/pkg/sql/parsers/tree/create.go index 61f2f7dadcb03..577569892c40b 100644 --- a/pkg/sql/parsers/tree/create.go +++ b/pkg/sql/parsers/tree/create.go @@ -1991,6 +1991,8 @@ func (it IndexType) ToString() string { return "cagra" case INDEX_TYPE_IVFPQ: return "ivfpq" + case INDEX_TYPE_FULLTEXT2: + return "fulltext2" case INDEX_TYPE_INVALID: return "" default: @@ -2011,6 +2013,7 @@ const ( INDEX_TYPE_HNSW INDEX_TYPE_CAGRA INDEX_TYPE_IVFPQ + INDEX_TYPE_FULLTEXT2 ) type VisibleType int @@ -2049,9 +2052,11 @@ type IndexOption struct { BitsPerCode int64 Async bool ForceSync bool + Merge bool AutoUpdate bool Day int64 Hour int64 + Second int64 IntermediateGraphDegree int64 GraphDegree int64 Quantization string @@ -2060,6 +2065,9 @@ type IndexOption struct { KmeansTrainPercent int64 KmeansMaxIteration int64 MaxIndexCapacity int64 + MaxPostingsCapacity int64 // fulltext2: max postings (term occurrences) per built segment + PositionFree bool // fulltext2: build a position-free (bag-of-words only) index + PositionFreeSet bool // whether POSITION_FREE was specified (tri-state for REINDEX: distinguishes =FALSE from unset) QuantizerTrainLimit int64 IncludeColumns []*UnresolvedName } @@ -2071,12 +2079,14 @@ func (node *IndexOption) Format(ctx *FmtCtx) { node.AlgoParamList != 0 || node.AlgoParamVectorOpType != "" || node.AlgoParamM != 0 || node.HnswEfConstruction != 0 || node.HnswEfSearch != 0 || node.AutoUpdate || node.Day != 0 || - node.Hour != 0 || + node.Hour != 0 || node.Second != 0 || node.IntermediateGraphDegree != 0 || node.GraphDegree != 0 || node.Quantization != "" || node.DistributionMode != "" || node.BitsPerCode != 0 || node.ITopkSize != 0 || node.KmeansTrainPercent != 0 || node.KmeansMaxIteration != 0 || - node.MaxIndexCapacity != 0 || node.QuantizerTrainLimit != 0 || + node.MaxIndexCapacity != 0 || node.MaxPostingsCapacity != 0 || + node.QuantizerTrainLimit != 0 || node.PositionFree || + node.Merge || node.ForceSync || len(node.IncludeColumns) != 0 { ctx.WriteByte(' ') } @@ -2129,6 +2139,9 @@ func (node *IndexOption) Format(ctx *FmtCtx) { if node.ForceSync { ctx.WriteString("FORCE_SYNC ") } + if node.Merge { + ctx.WriteString("MERGE ") + } if node.AutoUpdate { ctx.WriteString("AUTO_UPDATE=TRUE ") } @@ -2142,6 +2155,11 @@ func (node *IndexOption) Format(ctx *FmtCtx) { ctx.WriteString(strconv.FormatInt(node.Hour, 10)) ctx.WriteByte(' ') } + if node.Second != 0 { + ctx.WriteString("SECOND ") + ctx.WriteString(strconv.FormatInt(node.Second, 10)) + ctx.WriteByte(' ') + } if node.IntermediateGraphDegree != 0 { ctx.WriteString("INTERMEDIATE_GRAPH_DEGREE ") ctx.WriteString(strconv.FormatInt(node.IntermediateGraphDegree, 10)) @@ -2187,11 +2205,19 @@ func (node *IndexOption) Format(ctx *FmtCtx) { ctx.WriteString(strconv.FormatInt(node.MaxIndexCapacity, 10)) ctx.WriteByte(' ') } + if node.MaxPostingsCapacity != 0 { + ctx.WriteString("MAX_POSTINGS_CAPACITY ") + ctx.WriteString(strconv.FormatInt(node.MaxPostingsCapacity, 10)) + ctx.WriteByte(' ') + } if node.QuantizerTrainLimit != 0 { ctx.WriteString("QUANTIZER_TRAIN_LIMIT ") ctx.WriteString(strconv.FormatInt(node.QuantizerTrainLimit, 10)) ctx.WriteByte(' ') } + if node.PositionFree { + ctx.WriteString("POSITION_FREE=TRUE ") + } if len(node.IncludeColumns) != 0 { ctx.WriteString("INCLUDE (") for i, c := range node.IncludeColumns { @@ -2492,10 +2518,17 @@ type FullTextIndex struct { Name string Empty bool IndexOption *IndexOption + // IsV2 marks a CREATE FULLTEXT2 INDEX — the distinct WAND positional engine + // (algo="fulltext2"), routed to the fulltext2 plugin. false is classic v1. + IsV2 bool } func (node *FullTextIndex) Format(ctx *FmtCtx) { - ctx.WriteString("fulltext") + if node.IsV2 { + ctx.WriteString("fulltext2") + } else { + ctx.WriteString("fulltext") + } if node.Name != "" { ctx.WriteByte(' ') ctx.WriteString(node.Name) @@ -4159,6 +4192,8 @@ func (ic IndexCategory) ToString() string { return "unique" case INDEX_CATEGORY_FULLTEXT: return "fulltext" + case INDEX_CATEGORY_FULLTEXT2: + return "fulltext2" case INDEX_CATEGORY_SPATIAL: return "spatial" default: @@ -4171,6 +4206,7 @@ const ( INDEX_CATEGORY_UNIQUE INDEX_CATEGORY_FULLTEXT INDEX_CATEGORY_SPATIAL + INDEX_CATEGORY_FULLTEXT2 ) type CreateIndex struct { diff --git a/pkg/sql/parsers/tree/expr.go b/pkg/sql/parsers/tree/expr.go index 5201f8d3c6093..a5c51cf256698 100644 --- a/pkg/sql/parsers/tree/expr.go +++ b/pkg/sql/parsers/tree/expr.go @@ -1915,6 +1915,11 @@ const ( FULLTEXT_NL_QUERY_EXPANSION FULLTEXT_BOOLEAN FULLTEXT_QUERY_EXPANSION + // FULLTEXT_BM25 — IN BM25 MODE: ranked bag-of-words retrieval on a fulltext2 + // index (each token an OR term, no positional phrase), so it works on a + // POSITION_FREE index. Distinct from FullTextMatchExpr.IsBm25 (the BM25() verb + // of the standalone bm25 index). + FULLTEXT_BM25 ) type FullTextMatchExpr struct { @@ -1940,6 +1945,8 @@ func (node *FullTextSearchType) ToString() string { return "IN BOOLEAN MODE" case FULLTEXT_QUERY_EXPANSION: return "WITH QUERY EXPANSION" + case FULLTEXT_BM25: + return "IN BM25 MODE" default: return "Unknown FullSearchType" diff --git a/pkg/sql/plan/function/func_unary.go b/pkg/sql/plan/function/func_unary.go index f9c59f806cdbf..8a36d4469a5d4 100644 --- a/pkg/sql/plan/function/func_unary.go +++ b/pkg/sql/plan/function/func_unary.go @@ -4397,6 +4397,49 @@ func LoadFileDatalink(ivecs []*vector.Vector, result vector.FunctionResultWrappe return nil } +// LoadText reads a file via the datalink resolver and returns its EXTRACTED plain +// text: a PDF/DOCX is parsed to text (GetPlainText), any other file is returned as-is. +// Unlike load_file (raw bytes), this yields the same text the fulltext index build +// extracts, so it is the datalink resolver used by fulltext2 CDC maintenance to keep +// CDC parity with the synchronous build. +func LoadText(ivecs []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int, selectList *FunctionSelectList) error { + rs := vector.MustFunctionResult[types.Varlena](result) + filePathVec := vector.GenerateFunctionStrParameter(ivecs[0]) + + for i := uint64(0); i < uint64(length); i++ { + _filePath, null1 := filePathVec.GetStrValue(i) + if null1 { + if err := rs.AppendBytes(nil, true); err != nil { + return err + } + continue + } + filePath := util.UnsafeBytesToString(_filePath) + + dl, err := datalink.NewDatalink(filePath, proc) + if err != nil { + return err + } + fileBytes, err := dl.GetPlainText(proc) + if err != nil { + return err + } + if len(fileBytes) > types.MaxBlobLen { + return moerr.NewInternalError(proc.Ctx, "Data too long for blob") + } + if len(fileBytes) == 0 { + if err = rs.AppendBytes(nil, true); err != nil { + return err + } + continue + } + if err = rs.AppendBytes(fileBytes, false); err != nil { + return err + } + } + return nil +} + // WriteFileDatalink write content to file service and return number of byte written func WriteFileDatalink(ivecs []*vector.Vector, result vector.FunctionResultWrapper, proc *process.Process, length int, selectList *FunctionSelectList) error { rs := vector.MustFunctionResult[int64](result) diff --git a/pkg/sql/plan/function/function_id.go b/pkg/sql/plan/function/function_id.go index 1b9accbe54c8f..84967b885a300 100644 --- a/pkg/sql/plan/function/function_id.go +++ b/pkg/sql/plan/function/function_id.go @@ -804,9 +804,12 @@ const ( MAX_BY = 559 MAX_BY_NON_NULL = 560 + // LOAD_TEXT returns extracted datalink text for FULLTEXT2 maintenance. + LOAD_TEXT = 562 + // FUNCTION_END_NUMBER is not a function, just a flag to record the max number of function. // TODO: every one should put the new function id in front of this one if you want to make a new function. - FUNCTION_END_NUMBER = 562 + FUNCTION_END_NUMBER = 563 ) // functionIdRegister is what function we have registered already. @@ -1125,6 +1128,7 @@ var functionIdRegister = map[string]int32{ "uuid_to_bin": UUID_TO_BIN, "bin_to_uuid": BIN_TO_UUID, "load_file": LOAD_FILE, + "load_text": LOAD_TEXT, "save_file": SAVE_FILE, "hex": HEX, "unhex": UNHEX, diff --git a/pkg/sql/plan/function/list_builtIn.go b/pkg/sql/plan/function/list_builtIn.go index 3621e30a4ca3d..8e1148112612d 100644 --- a/pkg/sql/plan/function/list_builtIn.go +++ b/pkg/sql/plan/function/list_builtIn.go @@ -13335,6 +13335,53 @@ var supportedOthersBuiltIns = []FuncNew{ }, }, + // function `load_text` + // Like load_file but returns the datalink's EXTRACTED plain text (PDF/DOCX + // parsed via GetPlainText), not the raw bytes — so it yields the same text the + // fulltext index build indexes. Used by fulltext2 CDC datalink resolution. + { + functionId: LOAD_TEXT, + class: plan.Function_STRICT, + layout: STANDARD_FUNCTION, + checkFn: fixedTypeMatch, + + Overloads: []overload{ + { + overloadId: 0, + volatile: true, + args: []types.T{types.T_varchar}, + retType: func(parameters []types.Type) types.Type { + return types.T_text.ToType() + }, + newOp: func() executeLogicOfOverload { + return LoadText + }, + }, + { + overloadId: 1, + volatile: true, + args: []types.T{types.T_char}, + retType: func(parameters []types.Type) types.Type { + return types.T_text.ToType() + }, + newOp: func() executeLogicOfOverload { + return LoadText + }, + }, + { + overloadId: 2, + volatile: true, + args: []types.T{types.T_datalink}, + retType: func(parameters []types.Type) types.Type { + return types.T_text.ToType() + }, + newOp: func() executeLogicOfOverload { + return LoadText + }, + }, + }, + }, + // function `save_file` // confused function. { diff --git a/proto/plan.proto b/proto/plan.proto index 1bfee3a611cb2..05fb972f841e7 100644 --- a/proto/plan.proto +++ b/proto/plan.proto @@ -1616,6 +1616,7 @@ message AlterTableAlterReIndex { string index_name = 3; int64 index_algo_param_list = 4; bool force_sync = 5; + bool merge = 6; } message AlterTableAlterAutoUpdate { From 0eb9a921ece274c956447640b41ad2a255079e8d Mon Sep 17 00:00:00 2001 From: Eric Lam Date: Wed, 26 Aug 2026 19:49:19 +0800 Subject: [PATCH 02/17] feat(fulltext2): add engine plugin and CDC integration Backport-of: 1d7b6311ce91df0968da435bb405f3d68137c595 --- pkg/fulltext/plugin/compile/compile.go | 5 +- pkg/fulltext/plugin/runtime/runtime.go | 1 + pkg/fulltext2/README.md | 595 ++++++++++++ pkg/fulltext2/boolean.go | 590 ++++++++++++ pkg/fulltext2/build.go | 485 ++++++++++ pkg/fulltext2/cdc.go | 242 +++++ pkg/fulltext2/compact.go | 177 ++++ pkg/fulltext2/deletes.go | 145 +++ pkg/fulltext2/frames.go | 192 ++++ pkg/fulltext2/include_predicate.go | 457 +++++++++ pkg/fulltext2/index.go | 673 ++++++++++++++ pkg/fulltext2/membership.go | 160 ++++ pkg/fulltext2/mmap_other.go | 33 + pkg/fulltext2/mmap_unix.go | 47 + pkg/fulltext2/pkcodec.go | 236 +++++ pkg/fulltext2/plugin/compile/compile.go | 466 ++++++++++ pkg/fulltext2/plugin/idxcron/idxcron.go | 164 ++++ pkg/fulltext2/plugin/iscp/iscp.go | 46 + pkg/fulltext2/plugin/plan/plan.go | 50 + pkg/fulltext2/plugin/plan/schema.go | 299 ++++++ pkg/fulltext2/plugin/plan/tablefunc.go | 159 ++++ pkg/fulltext2/plugin/plugin.go | 66 ++ pkg/fulltext2/plugin/runtime/runtime.go | 145 +++ pkg/fulltext2/pool.go | 121 +++ pkg/fulltext2/query.go | 678 ++++++++++++++ pkg/fulltext2/search.go | 543 +++++++++++ pkg/fulltext2/search_cache.go | 364 ++++++++ pkg/fulltext2/segment.go | 733 +++++++++++++++ pkg/fulltext2/serialize.go | 721 ++++++++++++++ pkg/fulltext2/sink.go | 106 +++ pkg/fulltext2/storage.go | 877 ++++++++++++++++++ pkg/fulltext2/stream.go | 353 +++++++ pkg/fulltext2/tailbuild.go | 279 ++++++ pkg/fulltext2/termdict.go | 164 ++++ pkg/fulltext2/wand.go | 398 ++++++++ pkg/indexplugin/all/all.go | 1 + pkg/indexplugin/async.go | 58 ++ pkg/indexplugin/catalog/hooks.go | 16 + pkg/indexplugin/compile/hooks.go | 14 +- pkg/indexplugin/idxcron/hooks.go | 10 + pkg/indexplugin/iscp/import.go | 1 + pkg/indexplugin/plugin.go | 17 +- pkg/iscp/executor.go | 6 + pkg/iscp/fulltext2_consumer.go | 148 +++ pkg/iscp/fulltext2_sqlwriter.go | 396 ++++++++ pkg/iscp/index_consumer.go | 37 +- pkg/iscp/iteration.go | 42 +- pkg/iscp/mock_consumer.go | 6 +- pkg/iscp/types.go | 12 +- pkg/iscp/util.go | 72 +- pkg/monlp/tokenizer/jieba.go | 10 +- pkg/monlp/tokenizer/jieba_dict.go | 6 + pkg/monlp/tokenizer/word_id.go | 104 +++ pkg/sql/colexec/index_metadata.go | 2 +- .../table_function/fulltext2_compact.go | 143 +++ .../table_function/fulltext2_create.go | 364 ++++++++ .../table_function/fulltext2_search.go | 455 +++++++++ pkg/sql/colexec/table_function/hnsw_create.go | 63 +- .../colexec/table_function/table_function.go | 6 + pkg/sql/compile/alter.go | 14 +- pkg/sql/compile/ddl.go | 11 +- pkg/sql/compile/iscp_util.go | 8 +- pkg/sql/compile/util.go | 2 +- pkg/tools/checkpointtool/table_dump.go | 63 +- pkg/vectorindex/brute_force/brute_force.go | 20 +- pkg/vectorindex/brute_force/gpu.go | 20 +- pkg/vectorindex/cache/cache.go | 174 +++- pkg/vectorindex/cachegen/cachegen.go | 118 +++ .../cagra/plugin/compile/compile.go | 6 +- .../cagra/plugin/runtime/runtime.go | 5 + pkg/vectorindex/cagra/search_gpu.go | 54 +- pkg/vectorindex/columnbuffer.go | 193 ++++ pkg/vectorindex/cuvs/cdc.go | 26 + pkg/vectorindex/hnsw/generation.go | 127 +++ .../hnsw/plugin/compile/compile.go | 34 +- .../hnsw/plugin/runtime/runtime.go | 2 + pkg/vectorindex/hnsw/search.go | 54 +- pkg/vectorindex/hnsw/sync.go | 32 +- pkg/vectorindex/idxcron/executor.go | 151 ++- pkg/vectorindex/index.go | 20 + .../ivfflat/plugin/compile/compile.go | 6 +- .../ivfflat/plugin/runtime/runtime.go | 5 + pkg/vectorindex/ivfflat/search.go | 6 +- .../ivfpq/plugin/compile/compile.go | 6 +- .../ivfpq/plugin/runtime/runtime.go | 5 + pkg/vectorindex/ivfpq/search_gpu.go | 54 +- pkg/vectorindex/sqlexec/metadata.go | 82 ++ pkg/vectorindex/sqlexec/sqlexec.go | 43 + pkg/vectorindex/types.go | 43 + 89 files changed, 13983 insertions(+), 160 deletions(-) create mode 100644 pkg/fulltext2/README.md create mode 100644 pkg/fulltext2/boolean.go create mode 100644 pkg/fulltext2/build.go create mode 100644 pkg/fulltext2/cdc.go create mode 100644 pkg/fulltext2/compact.go create mode 100644 pkg/fulltext2/deletes.go create mode 100644 pkg/fulltext2/frames.go create mode 100644 pkg/fulltext2/include_predicate.go create mode 100644 pkg/fulltext2/index.go create mode 100644 pkg/fulltext2/membership.go create mode 100644 pkg/fulltext2/mmap_other.go create mode 100644 pkg/fulltext2/mmap_unix.go create mode 100644 pkg/fulltext2/pkcodec.go create mode 100644 pkg/fulltext2/plugin/compile/compile.go create mode 100644 pkg/fulltext2/plugin/idxcron/idxcron.go create mode 100644 pkg/fulltext2/plugin/iscp/iscp.go create mode 100644 pkg/fulltext2/plugin/plan/plan.go create mode 100644 pkg/fulltext2/plugin/plan/schema.go create mode 100644 pkg/fulltext2/plugin/plan/tablefunc.go create mode 100644 pkg/fulltext2/plugin/plugin.go create mode 100644 pkg/fulltext2/plugin/runtime/runtime.go create mode 100644 pkg/fulltext2/pool.go create mode 100644 pkg/fulltext2/query.go create mode 100644 pkg/fulltext2/search.go create mode 100644 pkg/fulltext2/search_cache.go create mode 100644 pkg/fulltext2/segment.go create mode 100644 pkg/fulltext2/serialize.go create mode 100644 pkg/fulltext2/sink.go create mode 100644 pkg/fulltext2/storage.go create mode 100644 pkg/fulltext2/stream.go create mode 100644 pkg/fulltext2/tailbuild.go create mode 100644 pkg/fulltext2/termdict.go create mode 100644 pkg/fulltext2/wand.go create mode 100644 pkg/indexplugin/async.go create mode 100644 pkg/iscp/fulltext2_consumer.go create mode 100644 pkg/iscp/fulltext2_sqlwriter.go create mode 100644 pkg/monlp/tokenizer/word_id.go create mode 100644 pkg/sql/colexec/table_function/fulltext2_compact.go create mode 100644 pkg/sql/colexec/table_function/fulltext2_create.go create mode 100644 pkg/sql/colexec/table_function/fulltext2_search.go create mode 100644 pkg/vectorindex/cachegen/cachegen.go create mode 100644 pkg/vectorindex/columnbuffer.go create mode 100644 pkg/vectorindex/hnsw/generation.go diff --git a/pkg/fulltext/plugin/compile/compile.go b/pkg/fulltext/plugin/compile/compile.go index 859db6676f692..f980fdcc497cd 100644 --- a/pkg/fulltext/plugin/compile/compile.go +++ b/pkg/fulltext/plugin/compile/compile.go @@ -27,6 +27,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/catalog" "github.com/matrixorigin/matrixone/pkg/common/moerr" + indexplugin "github.com/matrixorigin/matrixone/pkg/indexplugin" compileplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/compile" "github.com/matrixorigin/matrixone/pkg/logutil" "github.com/matrixorigin/matrixone/pkg/pb/plan" @@ -80,7 +81,7 @@ func (Hooks) HandleCreateIndex(ctx compileplugin.CompileContext, indexDefs map[s return nil } - async, err := catalog.IsIndexAsync(indexDef.IndexAlgoParams) + async, err := indexplugin.IsAsync(indexDef.IndexAlgo, indexDef.IndexAlgoParams) if err != nil { return err } @@ -108,7 +109,7 @@ func (Hooks) HandleCreateIndex(ctx compileplugin.CompileContext, indexDefs map[s } // HandleReindex — fulltext does not support ALTER … REINDEX. -func (Hooks) HandleReindex(_ compileplugin.CompileContext, _ map[string]*plan.IndexDef, _ bool) error { +func (Hooks) HandleReindex(_ compileplugin.CompileContext, _ map[string]*plan.IndexDef, _ bool, _ bool) error { return moerr.NewNotSupportedNoCtx("ALTER ... REINDEX is not supported for fulltext indexes") } diff --git a/pkg/fulltext/plugin/runtime/runtime.go b/pkg/fulltext/plugin/runtime/runtime.go index b83beb8c482aa..9bdaef3675bd7 100644 --- a/pkg/fulltext/plugin/runtime/runtime.go +++ b/pkg/fulltext/plugin/runtime/runtime.go @@ -84,6 +84,7 @@ func (CatalogHooks) ExperimentalFlag() string { return "" } // SupportedVectorTypes: fulltext has no vector column. func (CatalogHooks) SupportedVectorTypes() []types.T { return nil } +func (CatalogHooks) IsVectorIndex() bool { return false } // fulltext-family, not ANN // SupportedPrimaryKeyTypes: fulltext imposes no PK-type constraint. func (CatalogHooks) SupportedPrimaryKeyTypes() []types.T { return nil } diff --git a/pkg/fulltext2/README.md b/pkg/fulltext2/README.md new file mode 100644 index 0000000000000..2d39ec1dd2d9c --- /dev/null +++ b/pkg/fulltext2/README.md @@ -0,0 +1,595 @@ +# `pkg/fulltext2` — WAND-based positional fulltext engine + +`fulltext2` is a full-text index engine registered as the distinct index algorithm `"fulltext2"` +and created with `CREATE FULLTEXT2 INDEX` (experimental, gated by the +`experimental_fulltext2_index` session var). It is a **completely separate engine** from classic +v1 fulltext (generated-SQL + Go `Eval`): a from-scratch Block-Max WAND positional engine with its +own registered plugin (`pkg/fulltext2/plugin`), segment format, and query pipeline — **not** a +version of the `fulltext` plugin. + +> There is no `WITH (VERSION = 2)` syntax. fulltext2 is its own algorithm keyed by +> `catalog.MoIndexFullText2Algo`; you select it with `CREATE FULLTEXT2 INDEX`, not by versioning a +> classic `FULLTEXT` index. + +fulltext2 also **absorbs the position-free ranked-retrieval role** once served by the former +standalone `bm25` engine (now removed): a `POSITION_FREE` fulltext2 index queried `IN BM25 MODE` +is the bag-of-words path. + +It rides the shared **index-plugin framework** — the plugin hooks, the ISCP CDC consumer, +`VectorIndexCache`, idxcron `MERGE`/`REBUILD`, and the payload-agnostic CDC chunk framing — plus +common leaf serialization patterns (tar members, little-endian buffers, PK encode/decode). + +One engine serves two build modes, chosen at CREATE: + +| | positional (default) | `POSITION_FREE = TRUE` (bag-of-words) | +|---|---|---| +| **term dict** | **sorted** FST of the actual indexed terms (`word*`) | same sorted FST | +| **posting payload** | **+ positions** (phrase, ngram reassembly) | df / tf only (no positions, ~57% smaller) | +| **query model** | NL phrase · BOOLEAN tree · BM25 bag | `IN BM25 MODE` bag-of-words **only** | +| **scoring** | **TF-IDF *and* BM25** | **TF-IDF *and* BM25** | + +A `POSITION_FREE` index rejects NL / BOOLEAN phrase queries up front (it has no positions) — only +`MATCH(…) AGAINST(… IN BM25 MODE)` is valid against it. + +--- + +## Table of contents + +1. [Architecture at a glance](#1-architecture-at-a-glance) +2. [The segment format](#2-the-segment-format) +3. [Core in-memory structures](#3-core-in-memory-structures) +4. [The query model](#4-the-query-model) +5. [Algorithm: Block-Max WAND (top-k)](#5-algorithm-block-max-wand-top-k) +6. [Algorithm: phrase & boolean evaluation](#6-algorithm-phrase--boolean-evaluation) +7. [Scoring: TF-IDF and BM25](#7-scoring-tf-idf-and-bm25) +8. [Multi-segment: liveness, deletes, recency](#8-multi-segment-liveness-deletes-recency) +9. [INCLUDE columns: in-index prefilter & coverage](#9-include-columns-in-index-prefilter--coverage) +10. [Build & lifecycle: sync build, CDC, MERGE/REBUILD](#10-build--lifecycle-sync-build-cdc-mergerebuild) +11. [Memory model: mmap, lazy decode, bounded build](#11-memory-model-mmap-lazy-decode-bounded-build) +12. [File map](#12-file-map) + +--- + +## 1. Architecture at a glance + +An index is a set of **segments** plus a small overlay of deletes: + +``` +CREATE INDEX ──► sync build (from source rows) ──► tag=0 BASE segments + │ +INSERT/UPDATE/DELETE ──► CDC (ISCP) ──► tag=1 TAIL segments (+ delete frames) + │ +ALTER … REINDEX ──► MERGE (fold tail into base) / REBUILD (from source) + +query ──► Index{ segments:[base…, tail…], deletes } ──► per-segment WAND/phrase ──► global top-k +``` + +- **Base segments** (`tag=0`) are written by the synchronous build (`fulltext2_create` TVF) and + by REBUILD; they are stored as chunk rows and materialized on the LOCAL (SSD) fileservice under + `__fulltext2` and **mmap'd read-only**. +- **Tail segments** (`tag=1`) are CDC deltas appended incrementally; they are smaller and loaded + into the Go heap. +- A query builds an `Index` over *all* base + tail segments, runs the retrieval algorithm on each + segment against **global** corpus statistics, and merges into one top-k. Per-segment **liveness** + hides docs superseded by a newer copy or a tombstone. + +The engine answers three query shapes, all through one `Index`: + +| Mode | SQL | Semantics | +|---|---|---| +| **NL (natural language)** | `MATCH(col) AGAINST('quick brown')` | exact **ordered phrase** (positional) | +| **BOOLEAN** | `… AGAINST('+quick -fox "brown dog" quic*' IN BOOLEAN MODE)` | operator tree (`+`/`-`/`~`/`>`/`<`/`()`/`"…"`/`*`) | +| **BM25 / bag-of-words** | `… IN BM25 MODE` | pure disjunction of tokens (position-free) | + +--- + +## 2. The segment format + +A segment is the unit of storage and retrieval. Every segment — base or tail — has the same +logical shape (`doc.go`, `serialize.go`): + +``` +segment + ├─ term dict : term(string) → byte offset of its posting directory (SORTED, dictionary-free) + ├─ postings : per term, doc-sorted: docID/tf blocks + block-max meta + POSITIONS + ├─ docmap : ord → { pk, docLen [, INCLUDE values] }; N = doc count + └─ avgDocLen : Σ docLen / N (computed at LOAD across all loaded segments) +``` + +**On disk** it is a **tar archive** of five members (`serialize.go`): + +| Member | Contents | Residency at load | +|---|---|---| +| `docmap` | `pkType`, `N`, `ord→pk` (length-prefixed), `ord→docLen`, optional `ord→INCLUDE values` (§9) | viewed (mmap); pk / include decoded on demand | +| `termdict` | the **vellum FST**: `term → offset of its directory entry` | resident (compact, minimized) | +| `postings` | the **ranking directory**: per-term `df`, per-block max-TF / skip meta | resident (small) | +| `blocks` | per-term `docID/tf` blocks (delta + varint) | **mmap; block-decoded on demand** | +| `positions` | per-term compressed positions (phrase-only) | **mmap; decoded on demand** | + +Two deliberate properties: + +- **Dictionary-free.** The term dict holds exactly the terms that were indexed (gojieba words or + ngram bigrams), with *no jieba-dict dependency* in the backbone. One sorted structure gives both + exact term lookup **and** prefix enumeration (`word*`). On disk it is a **vellum FST** — a + compact minimized automaton; because it is byte-oriented and UTF-8 byte order equals Unicode + code-point order, CJK terms need no special handling (Go's string sort already produces the FST's + required ascending byte order). See `termdict.go`. +- **Scorer-agnostic block-max.** The block-max section stores the **raw** per-block max-TF and + min-doc-length (`deriveTermStats`), never a baked impact score. So *one* segment serves both + TF-IDF and BM25; the active scorer derives its max-impact bound at query time. + +Blocks are `BlockSize = 128` docs. Block-level bounds (`blockLastDoc`, `blockMaxTf`, +`blockMinDocLn`) let WAND locate and skip whole 128-doc regions without decoding them. + +--- + +## 3. Core in-memory structures + +### `Index` (`index.go`) + +The queryable handle over a set of segments. + +```go +type Index struct { + segments []*Segment + deletes map[any]int64 // normalizeKey(pk) → recency at/after which older copies are dead + globalN int64 // total live docs across segments + globalAvgDocLen float64 // global average doc length (for BM25 length normalization) + liveOrd [][]bool // liveOrd[si][ord] = is that doc live? nil ⇒ whole segment live +} +``` + +- `NewIndex(segments, deletes)` builds it; `resolve()` (lazy, once) computes `globalN`, + `globalAvgDocLen`, and the per-segment `liveOrd` bitmaps. +- **Global stats matter**: a term's IDF and the corpus `avgDocLen` are computed *across all + segments*, not per-segment — otherwise appending a CDC tail would shift every score. WAND uses + these global stats so a multi-segment index ranks identically to a single rebuilt one. +- `liveOrd` is the **sole resident liveness structure**. A fully-live (append-only) segment keeps + `liveOrd[si] == nil` — the "all live" fast path — so an append-only index costs *zero* resident + liveness heap. The transient `pk→loc` map that `resolve()` builds to derive it is local and + discarded (it was the dominant load-time heap floor). + +### `Segment` (`segment.go`) + +A segment has **two representations** that never coexist: + +- **Build-side** (in-memory, freshly built): `pks []any`, `terms map[string]*termPostings`, + `sortedTerms []string`. Postings hold docIDs/tfs/positions as plain Go slices. +- **Loaded-side** (deserialized for query): `pks == nil`. Instead: + - `dict *termDict` — the vellum FST (`term → directory-entry offset`). + - `ranking / blocks / positions []byte` — **views into the mmap/blob**; a base segment's are + backed by `mmapData` (page-cache, reclaimable, shared by all concurrent queries, no copy). + - `pkOffsets []int32` + `pkRaw []byte` — the docmap bytes; `pk(ord)` decodes a pk on demand + (instead of materializing `N` boxed `any` pks, ~24 B each). + +A loaded segment **expands nothing at load**: `LookupLoaded(term)` decodes only the touched term's +directory entry on demand; WAND then decodes only the *blocks* its walk lands on. Resident heap is +`O(current query)`, not `O(vocabulary)`. `Free()` releases the mmap under the cache's eviction +write-lock. + +`Recency` orders segments when the same pk lands in several (UPDATE / reinsert / stale base copy): +only the highest-`Recency` copy is live. + +### `termPostings` (`segment.go`) + +One term's posting list. + +```go +type termPostings struct { + // build-side (nil on a loaded segment): + docIDs []int64 // ascending doc ords + tfs []uint8 // capped term frequency (≤ MaxCappedTf) + positions [][]int32 // per-doc token positions (byte offsets) + + ndoc int // df (document frequency) + + // loaded-side: block-compressed views into the mmap + blockData []byte; blockOff []int64 // docID/tf blocks (delta+varint), one per 128 docs + posRaw []byte; blockPosOff []int64 // compressed positions, block-seekable + + // scorer-agnostic score-UB inputs (raw): + maxTf uint8; minDocLen int32 // whole-list bounds + blockLastDoc []int64; blockMaxTf []uint8; blockMinDocLn []int32 // per-block bounds +} +``` + +The docID/tf blocks are the largest section (~46 % at load) and positions ~48 %; **neither is +expanded at load** — WAND touches only the blocks its block-max walk visits; positions are read +only for phrase verification / MERGE. + +### `wandIter` (`wand.go`) + +A per-term posting **cursor** for the WAND walk, carrying its `maxImpact` bound and a small +per-cursor decoded-block cache (`bDocs`/`bTfs`, cap 128). Key methods: `doc()` (cached current +ord), `skipTo(d)` (locate block via resident `blockLastDoc`, binary-search within), `blockMax(d)` +(block-level score UB), `blockEndAt(d)`. See §5. + +### `Builder` / `TailBuilder` (`build.go`, `tailbuild.go`) + +- `Builder` accumulates `(word, position, pk)` occurrences via `Add`, then `FinishSegments` / + `Finish` assembles a `Segment`. It seals at `min(max_index_capacity docs, max_postings_capacity + postings)` so per-segment build memory is bounded (see §10). +- `TailBuilder` is the streaming CDC builder: it tokenizes insert rows into capacity-capped + segments, **spilling each sealed segment to a temp file** as it fills, so the sinker's peak + memory is one open segment, not the whole CDC stream. Deletes are spilled likewise. + +--- + +## 4. The query model + +`SearchQuery(pattern, boolean, parser, algo, k, filter)` (`query.go`) is the dispatch: + +- **BOOLEAN mode** → `buildBooleanQuery` parses the pattern into a `BoolQuery` (an operator tree) + and calls `SearchBoolean`. +- **NL mode** → `phraseSlots` tokenizes into positioned slots and calls `SearchPhrase` — an exact + **ordered phrase** (matching classic fulltext NL semantics, including CJK). +- **BM25 mode** → `SearchBagOfWords` tokenizes the whole pattern into a pure disjunction of tokens + (each a SHOULD term) and runs the position-free WAND — bag-of-words retrieval that works on a + `POSITION_FREE` index. + +The boolean operator tree is built from `clause` nodes (`boolean.go`): + +```go +type clause struct { + kind clauseKind // clauseTerm | clausePhrase | clausePrefix | clauseAnd | clauseOr | clauseNot | group + terms []string // leaf term(s) / prefix + phrase []phraseSlot // positional (byte-offset) slots for a "…" phrase + children []clause // group + weight float32 // impact multiplier: 1.0 default; +/-/~//() change it +} +``` + +Operators: `+term` (MUST), `-term` (MUST NOT), `~term` (weight down), `>`/`<` (weight up/down), +`(…)` (group), `"…"` (exact phrase), `word*` (prefix → OR of all terms with that prefix, enumerated +from the FST). A bare multi-word run in boolean mode is an implicit contiguous phrase. + +`phraseSlot{ term, off }` carries a term and its byte offset within the query, so phrase adjacency +in the query matches adjacency in the source text. + +--- + +## 5. Algorithm: Block-Max WAND (top-k) + +**WAND** (Weak-AND, Broder 2003) + **Block-Max WAND** (Ding & Suel 2011) answer disjunctive +top-k — a pure OR of terms — returning *the exact same top-k as a full scan* while skipping most +documents. Implemented in `searchWAND` (`wand.go`). + +**Idea.** Each term carries a `maxImpact` (its largest possible weighted contribution). The top-k +min-heap's k-th best score is a moving threshold **θ**. Any document whose summed term upper bounds +cannot reach θ can never make the top-k, so it is never scored. + +**Per-iteration loop** (`searchWAND`): + +``` +loop: + 1. insertion-sort cursors by current doc ascending (nearly-sorted between iters ⇒ O(n)) + 2. pivot = first cursor where Σ maxImpact[0..i] ≥ θ (term-level WAND) + extend pivot over all cursors also sitting on pivotDoc + 3. blockSum = Σ blockMax(pivotDoc)[0..pivot] (Block-Max refinement, tighter UB) + if blockSum ≤ θ: skipTo(block end) — skip the whole 128-doc region, score nothing + 4. elif iters[0].doc() == pivotDoc: score pivotDoc, push to heap, advance; update θ + 5. else: skipTo(pivotDoc) — align a lagging cursor +``` + +**Multi-term query: how the cursors combine.** One `wandIter` per SHOULD term +(`buildWandIters`); a document's score is the **sum of the contributions of whichever query +terms it contains** — disjunctive, so a doc need not hold every term. Only cursors *sitting on +the same doc* contribute to that doc's score (step 4 sums `it.tf()`-based contributions for +every `it` at `pivotDoc`). The pivot is what makes it fast: because cursors are sorted by +current doc and `maxImpact` is accumulated in that order, any doc below `pivotDoc` could only +contain the terms of cursors `0..pivot-1`, whose max-impacts sum to `< θ` — so it provably +can't make the top-k and is skipped without ever being read. + +Worked example — query `A B C`, want top-1, θ currently 3.0, per-term +`maxImpact{A:4, B:1, C:5}`, cursors at `A@5, B@2, C@8`: + +``` +1. sort by doc → [B@2, A@5, C@8] +2. pivot scan → acc=B(1)=1 < 3 ; +A(4)=5 ≥ 3 ⇒ pivot=A, pivotDoc=5 + ⇒ doc 2 is skipped: B alone (max 1) can never beat θ=3 +3. blockSum{B,A}@5 = 4.2 > 3 ⇒ don't block-skip +4. iters[0].doc()==5 ? lead is B@2 ≠ 5 ⇒ NOT aligned ⇒ skipTo(5) on B, re-loop + (once cursors align on a doc, it is scored = Σ contributions of the terms it has; + that raises θ, which prunes even more of the next iteration) +``` + +The heavier a term (larger `maxImpact`), the earlier the pivot lands on it and the more docs +below it are pruned; a rare, high-idf term therefore does most of the skipping. + +Design notes worth knowing: + +- **Insertion sort, not `sort.Slice`** (step 1): the cursor array is nearly sorted between + iterations (only the skipped cursor moved), so insertion sort is O(n); `sort.Slice` boxed the + slice into an `interface{}` and heap-allocated the `less` closure every call — that alloc churn + dominated query CPU. +- **Cached `doc()`**: `wandIter.cur` is recomputed only on cursor move (`refresh`), because it is + read many times per pivot iteration (sort, pivot scan, blockMax, alignment). +- **Block-skip is free**: it only removes docs whose score UB ≤ θ — which the `score > θ` + insertion rule would reject anyway. Same top-k **set** and **scores** as the full scan; only the + work differs. +- **WHERE prefilter**: `allow` (a `Membership`) admits a doc to the heap only if its ord passes, + so `LIMIT` bounds the *filtered* set. Block-skip stays valid — `blockSum` bounds every doc in the + region regardless of the filter. +- **`newTopKHeap` bounds k** to the segment's live-doc count, so an absurd pushed `LIMIT 5e8` + can't eagerly allocate GB-sized heap buffers and OOM the CN. + +**Tie caveat**: documents with an *exactly equal* score have unspecified order (WAND sums +contributions in cursor order, the full scan in clause-map order — they can differ in the last +float ULP). The top-k *set* above the boundary and the score *multiset* are identical. + +The no-LIMIT sibling `streamWAND` (`stream.go`) reuses the cursors but walks *every* matching doc +in ord order (no θ, no heap), emitting in bounded batches — used when the upstream `ORDER BY score` +does the ranking. + +--- + +## 6. Algorithm: phrase & boolean evaluation + +WAND (§5) is **only** the pure-OR path. `SearchBoolean` dispatches on query shape: + +``` +pure OR of single-term SHOULDs → searchWAND (§5: θ/pivot/block-skip) +any MUST / MUST-NOT / phrase clause → searchBooleanFull (dense accumulator, NO skip) +NL / "…" phrase → SearchPhrase (conjunctive block-cursor) +``` + +The three engines treat their per-term cursors in fundamentally different ways — desynchronized +(OR), no cursors at all (boolean), or forced-to-converge (phrase). One worked example each below; +all share a corpus where the postings are: + +``` +term "quick" → docs {2, 5, 8, 40} term "brown" → docs {5, 8, 9} +term "fox" → docs {8, 60} (doc 8 = "quick brown fox", doc 5 = "quick brown …") +``` + +### Exact phrase (NL mode) — `SearchPhrase` → `matchPhraseCursor` (`search.go`) + +Conjunctive, anchored on the **rarest** slot, then a byte-offset positional verify. Cursors are +forced to *converge* on the anchor's current doc — the opposite of WAND. + +`AGAINST('brown fox')` → slots `[{brown, off=0}, {fox, off=6}]`: + +``` +1. rarest slot = "fox" (df 2) ⇒ drive its docs {8, 60} +2. doc 8: skipTo(8) the "brown" cursor → present. Positional check: + fox @ pos p ⇒ phrase-start = p-6 ; is "brown" @ start+0 ? yes ⇒ MATCH + doc 60: skipTo(60) the "brown" cursor → absent ⇒ drop, no position decode +3. score only doc 8 (the one verified doc) +``` + +So `AGAINST('brown fox')` matches doc 8 ("brown fox") but **not** a doc containing "fox … brown" +out of order — the offset check (`phraseSlot.off`) is what makes it a phrase, not a bag. Positions +are decoded **only** for docs that survive the doc-level intersection; `boundedTopK` keeps the +top-k without materializing all matches. (This is also the only mode that reads positions.) + +### Boolean AND / MUST / MUST-NOT — `searchBooleanFull` (`boolean.go`) + +No impact cursors, no skipping: every clause's **whole** posting list is materialized into a dense +`O(N)` per-doc `score[]` array; AND is an intersection by **hit-count**, MUST-NOT an exclusion +bitset. + +`AGAINST('+quick +brown -fox' IN BOOLEAN MODE)` → must `{quick, brown}`, mustNot `{fox}`: + +``` +1. mustNot "fox" → set bits {8, 60} in the exclusion bitset +2. MUST pass, count hits per doc (need == 2): + quick {2,5,8,40}: mustHit[2,5,8,40]=1 ; score[..]+= + brown {5,8,9} : mustHit[5]=2, mustHit[8]=2, mustHit[9]=1 ; score[..]+= +3. admit docs with mustHit == 2 → {5, 8} + doc 8 excluded by mustNot("fox") ⇒ dropped + ⇒ candidate = {5}, pushed to the top-k heap by its summed score +``` + +Every MUST/SHOULD/MUST-NOT term is read in full (`materializeDocIDs` decodes all its blocks) — +cost `O(Σ df)` to decode + `O(N)` dense memory, no block-skip. WAND's impact bound is useless here +because MUST/MUST-NOT need *membership* of every doc, which an impact upper bound cannot tell you. +`~term` (ADJUST) adds a (typically negative) contribution but does **not** exclude — a doc matching +only a `~`-term still ranks, just low (MySQL parity). + +### Boolean OR — routes to WAND + +`AGAINST('quick brown fox' IN BOOLEAN MODE)` with no `+`/`-`/`"…"` is a pure disjunction, so +`disjunctiveTerms` returns true and it runs the §5 WAND engine — identical to the OR example there. + +`word*` prefix clauses enumerate matching terms from the FST and union their posting lists; a group +`(…)` recurses. Both fall back to the dense `searchBooleanFull` path (they need a per-doc MAX over +their expansion). + +### Mode comparison + +| Mode | SQL | Engine | Cursor behavior | Skips docs? | Reads positions? | +|---|---|---|---|---|---| +| **OR** | `AGAINST('a b' IN BM25 MODE)` / pure-OR `IN BOOLEAN MODE` | `searchWAND` | desynchronized, impact-driven `skipTo` | **yes** (θ + block-max) | no | +| **AND / NOT** | `+a +b -c IN BOOLEAN MODE` | `searchBooleanFull` | none — full `materializeDocIDs` + dense arrays | no (full read) | no | +| **phrase** | `AGAINST('a b')` (default NL) / `"a b"` | `matchPhraseCursor` | conjunctive, converge on rarest-slot doc | yes (skip to anchor) | **yes** (offset check) | + +All three top-k paths use `vectorindex.FastMaxHeap` (SoA, keyed by ord, distance = −score) — zero +per-candidate allocation, ties unspecified (the top-k *set* and score *multiset* are stable; the +order among exactly-equal scores is not). + +--- + +## 7. Scoring: TF-IDF and BM25 + +`ScoreAlgo` selects the scorer; both are supported from the *same* segment because block-max is +raw (§2). + +- **TF-IDF**: `weight · tf · idf`. +- **BM25**: `weight · idf · bm25Factor(tf, docLen, avgDocLen)` — the saturating term-frequency + factor with document-length normalization against the **global** `avgDocLen`. + +Key quantities: + +- `idf` is computed from **global** df/N (across all segments), so a CDC tail doesn't shift scores. +- `termMaxImpact` / `blockMax` derive the WAND upper bounds in the *same* scorer, so the block-skip + bound is always ≥ the real score — WAND returns the identical ranking to a full scan. +- `tf` is capped (`MaxCappedTf`) to one byte; fulltext2 keeps *real* tf (unlike a position-free + impact-only index), so phrase/NL scoring is faithful. + +--- + +## 8. Multi-segment: liveness, deletes, recency + +An index is append-mostly: base segments plus CDC tail segments plus delete frames. Correctness +comes from three overlays computed in `Index.resolve()`: + +- **Recency**: when the same pk appears in several segments (UPDATE, reinsert, a stale base copy), + only the **highest-Recency** copy is live. Tail chunk_id > base recency, so a later append always + wins. +- **Deletes**: `deletes[pk] = recency` — any copy of `pk` older than that recency is dead + (tombstone). CDC delete frames carry pk-only. +- **`liveOrd[si]`**: a per-segment ord-indexed liveness bitmap derived once; `nil` means "whole + segment live" (append-only fast path). Every liveness check — phrase `isLive`, boolean/stream + `livenessMembership`, and compaction `ReconstructLiveDocs` — is an O(1) bitmap index, allocation + free. + +A skipped/dead doc is never scored and never emitted; `globalN` counts only live docs. + +--- + +## 9. INCLUDE columns: in-index prefilter & coverage + +A fulltext2 index can carry **INCLUDE columns** — the actual per-document values of chosen scalar +columns, stored *inside* the segments (and the CDC tail) alongside the pk. They let a filtered +top-k query run entirely inside `fulltext2_search`, dropping the two JOINs a plain fulltext query +needs (a prefilter second-scan for the WHERE, and a base-table join-back for the SELECT): + +```sql +CREATE FULLTEXT2 INDEX ftidx ON docs (body) + WITH PARSER json + INCLUDE (status, priority); + +-- filter on an include column is evaluated INSIDE the WAND walk; status/priority are +-- served straight from the index — no base-table JOIN, no prefilter second-scan: +SELECT id, status, priority +FROM docs +WHERE status = 'active' AND priority > 3 + AND MATCH(body) AGAINST('search terms'); +``` + +**Supported types.** Exactly what the segment pk codec already round-trips +(`CatalogHooks.SupportedIncludeColumnTypes`, `pkcodec.go`): the integer family +`int8/16/32/64`, `uint8/16/32/64` (+ `bit`), and `varchar`/`char`. The value is stored as its +**actual value, not a hash** — so both prefiltering *and* covering projection work, including +range and prefix/`LIKE 'x%'` predicates that a hash could not answer. `float`/`date`/`decimal`/ +`text` are deferred (the codec has no float case; widening the set is a codec extension, not a +plan change). The set + column names are pinned into `IndexAlgoParams` at CREATE and read back on +every build / CDC / query path. + +**Storage.** Include values live in the segment **docmap**, ord-aligned next to `pk` and `docLen` +(`serialize.go` `encodeDocmap`/`decodeDocmap`): a `[nCols][per-col type]` header, then per-doc, +per-col values encoded with the same codec as pks — integers dense fixed-width, strings as +`[u32 len][content]` varlena. A NULL include value carries a per-element null flag plus a +well-formed placeholder so the byte cursor stays aligned. On load the section is an mmap view +(`Segment.includeRaw` + per-varlena-col offset tables); lazy accessors decode on demand: +`Segment.includeVal(ord, colIdx)` (for the predicate evaluator) and the box-free +`Segment.appendIncludeTo(buf, colIdx, ord)` (for covering TVF output — the mirror of `appendPkTo`). + +**Prefilter** (`include_predicate.go`). A WHERE predicate on include columns is pushed into the +TVF as JSON and parsed into an `includePredMembership` implementing the existing +`Membership.Contains(ord)`: it decodes `seg.includeVal(ord, colIdx)` and tests the op +(`= < > BETWEEN IN`, string comparison, and **prefix** / `LIKE 'x%'`) with 3-valued NULL logic. +It is `andAllow`'d with the liveness/docfilter memberships, so it runs **inside** the Block-Max +WAND walk *before* top-k admission — a pushed `LIMIT` therefore bounds the already-filtered set +(no over-fetch). Block-skip stays valid: `blockSum` bounds every doc in a region regardless of the +filter. A pk predicate is peeled only when the pk type is one the evaluator can compare +(`fulltext2PeelablePkColName`); otherwise it stays a residual on the base scan and the JOIN is +retained — correct, just unoptimized. + +**Coverage** (`buildFulltext2SearchNodeCovered`, `search_cache.go` `SearchInto`/streaming). When +the index's INCLUDE columns cover the query's SELECT, the `fulltext2_search` TVF's output coldefs +are `[__mo_ft_doc_id, __mo_ft_score, ]`, so the projection +reads pk/score/include directly from the TVF with **no base-table JOIN**. The pk/score outputs use +the reserved names `catalog.FullText2Search_OutCol_DocId` / `_Score` (`__mo_ft_doc_id` / +`__mo_ft_score`) precisely so an INCLUDE column named `doc_id` or `score` cannot collide with them +(the runtime classifies the output batch by name). Results carry include values box-free through +`vectorindex.ColumnBuffer` / `SearchOutput` on both the LIMIT (`SearchInto`) and no-LIMIT +(streaming `Emit`) paths. + +**Value source.** Build (CREATE/REBUILD) selects the INCLUDE columns after the text columns and +passes them as trailing `fulltext2_create` args (the tokenizer stops *before* them so they are not +indexed as text); CDC (ISCP `Fulltext2SqlWriter`) resolves each include column's position from +`Name2ColIndex` and threads its value through `cdc.Insert/Upsert` into `SetDoc`, which replaces +include values in lock-step with terms so an UPSERT supersedes them. + +Cost tradeoff: the docmap grows by the include values per doc (full string values bloat the +segment — the same tradeoff fulltext2 already accepts for varchar pks; prefer small scalar +include columns). + +--- + +## 10. Build & lifecycle: sync build, CDC, MERGE/REBUILD + +Index CREATE → sync build → CDC maintenance → periodic compaction. All bases/tails are chunk rows +in two hidden tables (storage + metadata), framed with the shared CDC chunk format. + +- **Sync build** (`fulltext2_create` TVF): CROSS APPLY over the source table, tokenize each row + (datalink → plain text, json → values, ngram/gojieba parser), `Add` to a streaming `Builder`, + seal + persist tag=0 base sub-segments as they fill. `CREATE FULLTEXT2 INDEX` registers this + + an always-async CDC task. +- **CDC** (`cdc.go`, `tailbuild.go`, `sink.go`, ISCP consumer): INSERT/UPSERT/DELETE flow into the + tag=1 tail. `TailBuilder` tokenizes inserts into capped, spilled tail segments; deletes into + spilled tombstone frames; on flush it appends them (delete-frames first, then insert segments) at + the next chunk_id in one txn, advancing the watermark. +- **MERGE** (`compact.go`, `fulltext2_compact` TVF): fold the tag=1 tail into the tag=0 base and + reclaim dead space — load base+tail+deletes, `ReconstructLiveDocs` (from postings, no re-tokenize) + into a fresh capacity-bounded base, atomically replace all prior bases + the whole tail. +- **REBUILD**: discard the tail and rebuild the base from source (`buildFromSource`). +- idxcron schedules MERGE (or REBUILD once dead-doc % is high) when the tail grows past a chunk + threshold. + +--- + +## 11. Memory model: mmap, lazy decode, bounded build + +Full-text indexes are large; the engine is careful on both the query and build sides. + +**Query side:** + +- Base segments are **mmap'd read-only** on the fast LOCAL (SSD) `__fulltext2` fileservice dir — + page-cache-backed (reclaimable, *not* Go heap), shared by all concurrent queries, no copy. +- A loaded segment expands **nothing** at load: FST resident (compact), directory entries decoded + per touched term, docID/tf blocks decoded per block the WAND walk visits, positions decoded only + for phrase verification. Resident heap is `O(current query)`, not `O(vocabulary)`. +- pks are decoded on demand from the docmap bytes (no `N` boxed `any`). +- `liveOrd == nil` for an append-only segment ⇒ zero resident liveness heap. +- A base-load budget guard fails fast (clear error) rather than letting a huge base OOM-kill the CN. + +**Build side** — per-segment build memory ≈ **Σ postings** (term occurrences held in +`Builder.docs`), *not* doc count (a doc can hold 1 token or thousands). So every streaming build +path (create TVF, CDC `TailBuilder`, MERGE/REBUILD `CompactSegments`) seals a segment on +`ReachedSegmentCap(b, docCap, postingCap)` — **whichever of `max_index_capacity` (docs) or +`max_postings_capacity` (postings, default 8M ≈ ~512 MB build peak) fires first**. This bounds +build memory regardless of document shape. + +--- + +## 12. File map + +| File | Responsibility | +|---|---| +| `doc.go` | package overview + segment-format summary | +| `index.go` | `Index` type, `resolve()`, `SearchPhrase`/`SearchBoolean`/`SearchText` entry points, liveness | +| `segment.go` | `Segment` (build-side vs loaded-side), `termPostings`, `deriveTermStats`, `BlockSize`, INCLUDE accessors (`includeVal`, `appendIncludeTo`) | +| `wand.go` | `wandIter`, Block-Max WAND (`searchWAND`), `buildWandIters`, top-k heap | +| `stream.go` | no-LIMIT streaming WAND (`streamWAND`, `StreamQuery`, `StreamBagOfWords`) | +| `query.go` | `SearchQuery` dispatch, `SearchBagOfWords`, boolean-query build, phrase slots | +| `boolean.go` | boolean operator tree (`clause`, `clauseKind`), boolean evaluation | +| `search.go` | phrase evaluation, per-segment scoring glue, `boundedTopK` | +| `membership.go` | WHERE-prefilter + liveness `Membership` adapters | +| `include_predicate.go` | in-index INCLUDE-column predicate → `Membership` (`= < > BETWEEN IN`, prefix/`LIKE`, 3-valued NULL) | +| `pkcodec.go` | typed pk / INCLUDE value encode/decode (integer family dense fixed-width; varchar/char varlena `[len][value]`) | +| `termdict.go` | vellum FST term dict: build, load, exact `get`, `prefixIter`, `forEachTerm` | +| `build.go` | `Builder` (Add/Finish/FinishSegments), `TokenizedDoc`, `ReachedSegmentCap`, seal caps | +| `tailbuild.go` | streaming CDC `TailBuilder` (spill-as-you-fill) | +| `cdc.go` | CDC event decode (`Cdc`), pk typing | +| `deletes.go` | delete-record framing / tombstones | +| `frames.go` | CDC chunk framing (`FrameSegment`, `UnframeTail`) | +| `sink.go` | ISCP sink adapter glue | +| `compact.go` | `CompactSegments` (MERGE): reconstruct live docs → fresh bounded base | +| `serialize.go` | tar-archive segment encode/decode (docmap incl. INCLUDE section / termdict/postings/blocks/positions) | +| `storage.go` | chunk-row persistence, `LoadAllBases`/tail, `__fulltext2` spill, load budgets, `TableConfig` | +| `search_cache.go` | `VectorIndexCache` integration (per-CN cached loaded index) | +| `mmap_unix.go` / `mmap_other.go` | read-only mmap + munmap (platform) | + +For the plugin wiring (hooks, grammar, CDC registration, idxcron), see `pkg/fulltext2/plugin/…`. diff --git a/pkg/fulltext2/boolean.go b/pkg/fulltext2/boolean.go new file mode 100644 index 0000000000000..f87853df18c74 --- /dev/null +++ b/pkg/fulltext2/boolean.go @@ -0,0 +1,590 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "math/bits" + + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" +) + +// docBitset is a dense per-doc-ord bitset (doc ords are dense in [0, N) per segment), +// used by the boolean evaluator instead of map[int64]struct{} — no rehash, ~1/64 the +// memory, and forEach yields ords ascending. +type docBitset []uint64 + +func newDocBitset(n int) docBitset { return make(docBitset, (n+63)/64) } +func (b docBitset) set(i int) { b[i>>6] |= uint64(1) << (uint(i) & 63) } +func (b docBitset) get(i int) bool { return b[i>>6]&(uint64(1)<<(uint(i)&63)) != 0 } + +// forEach calls f with each set bit index, ascending. +func (b docBitset) forEach(f func(int)) { + for w, word := range b { + base := w << 6 + for word != 0 { + f(base + bits.TrailingZeros64(word)) + word &= word - 1 + } + } +} + +// Boolean-mode search (MATCH … AGAINST('…' IN BOOLEAN MODE)). +// +// MatrixOne/MySQL boolean semantics, mapped onto the positional segment: +// - +clause → MUST (AND): the doc must contain it. +// - -clause → MUST NOT (the doc must not contain it). +// - >clause → SHOULD, impact ×1.1 (boost). +// - < ~ weight operators (§6). +const ( + weightGreater = 1.1 + weightLess = 0.9 + weightTilde = -1.0 +) + +type clause struct { + kind clauseKind + terms []string // leaf (term / prefix) + phrase []phraseSlot // clausePhrase: positional (byte-offset) slots + children []clause // group + weight float32 // impact multiplier (1.0 default; > < ~ change it) +} + +// BoolQuery is a parsed boolean-mode query. adjust holds the ~ clauses, which +// adjust score without driving inclusion. +type BoolQuery struct { + must []clause // + + mustNot []clause // - + should []clause // bare / > / < + adjust []clause // ~ +} + +// SearchBoolean evaluates a boolean query and returns up to k hits, score desc +// (ties by ascending doc ord). A doc matches when every MUST clause is present, +// no MUST-NOT clause is present, and — with no MUST — at least one SHOULD clause +// is present. Score sums the MUST + SHOULD contributions, then applies ~ ADJUST +// penalties to the matched docs. +// +// A pure disjunction of single terms (bare/>/< SHOULD terms, no MUST/MUST-NOT/~, +// no phrase/prefix/group) is routed to the WAND top-k (searchWAND), which +// early-terminates via term max-impact bounds and returns the identical ranking +// as the full scan. Everything else uses the full evaluator. +func (s *Segment) SearchBoolean(q BoolQuery, algo ScoreAlgo, k int, allow Membership, gs *globalStats) ([]Result, error) { + // A query with no clauses at all returns nothing. A ~-only query (adjust, no MUST/ + // SHOULD) still returns its matching rows ranked low — classic/MySQL "~ rates lower + // but does not exclude" — so it must NOT early-out here (searchBooleanFull's no-MUST + // candidate set unions the adjust terms). + if k <= 0 || s.N == 0 || (len(q.must) == 0 && len(q.should) == 0 && len(q.adjust) == 0) { + return nil, nil + } + if terms, ok := disjunctiveTerms(q); ok { + return s.searchWAND(terms, algo, k, allow, gs), nil + } + return s.searchBooleanFull(q, algo, k, allow, gs) +} + +// disjunctiveTerms reports whether q is a pure OR of single-term SHOULD clauses +// (the WAND-eligible shape) and returns those clauses. +func disjunctiveTerms(q BoolQuery) ([]clause, bool) { + if len(q.must) != 0 || len(q.mustNot) != 0 || len(q.adjust) != 0 || len(q.should) == 0 { + return nil, false + } + for _, c := range q.should { + if c.kind != clauseTerm { + return nil, false + } + } + return q.should, true +} + +// evalBoolean runs ONE segment's boolean evaluation against a DENSE per-doc-ord accumulator +// (score []float32 + doc bitsets) rather than a map[int64]float64 per clause plus a +// candidate map, and calls emit(ord, score) for every doc that satisfies the query (all +// MUST present, no MUST-NOT, and — with no MUST — at least one SHOULD/ADJUST) AND passes the +// WHERE/liveness prefilter. Doc ords are dense in [0, N) per segment, so the arrays are O(N) +// with no rehash — a low-selectivity 2-char CJK term matches most of the corpus, and the old +// maps then rehashed themselves to death and held ~30 bytes/doc × (#clauses+1). Peak Go heap +// is O(segment N) (the dense score array + candidate/must bitsets), bounded by the segment +// capacity and freed when the call returns — INDEPENDENT of k. Both the top-k +// searchBooleanFull and the no-LIMIT streaming path (Index.streamBoolean) build on this; +// they differ only in what emit does (bounded heap push vs. stream to the sink). +func (s *Segment) evalBoolean(q BoolQuery, algo ScoreAlgo, allow Membership, gs *globalStats, emit func(ord int, score float32)) error { + n := int(s.N) + if n == 0 { + return nil + } + avgDocLen := gs.avgdl(s) + score := make([]float32, n) + + // MUST-NOT: presence only, into an exclusion bitset. + mustNot := newDocBitset(n) + for _, c := range q.mustNot { + if err := s.evalClauseInto(c, algo, avgDocLen, gs, false, func(ord int64, _ float32) { + mustNot.set(int(ord)) + }); err != nil { + return err + } + } + + admit := func(ord int) { + if mustNot.get(ord) || !allowed(allow, int64(ord)) { // WHERE prefilter (nil = allow all) + return + } + emit(ord, score[ord]) + } + + if len(q.must) > 0 { + // A candidate must satisfy EVERY MUST clause: count hits per doc (intersection). + mustHit := make([]int32, n) + touched := newDocBitset(n) + for _, c := range q.must { + if err := s.evalClauseInto(c, algo, avgDocLen, gs, true, func(ord int64, sc float32) { + score[ord] += sc + mustHit[ord]++ + touched.set(int(ord)) + }); err != nil { + return err + } + } + // SHOULD/ADJUST add to the score of whatever docs they touch; only MUST-satisfying + // docs are collected, so contributions to non-candidates are simply never read. + for _, cs := range [][]clause{q.should, q.adjust} { + for _, c := range cs { + if err := s.evalClauseInto(c, algo, avgDocLen, gs, true, func(ord int64, sc float32) { + score[ord] += sc + }); err != nil { + return err + } + } + } + need := int32(len(q.must)) + touched.forEach(func(ord int) { + if mustHit[ord] == need { + admit(ord) + } + }) + return nil + } + + // No MUST: the candidate set is the union of SHOULD *and* ADJUST. A ~ (ADJUST) term + // lowers a row's score but does NOT exclude it (MySQL: "rated lower ... but not + // excluded, as with -"), so a row matching only a ~term is still a candidate, ranked + // low by its negative contribution — matching classic fulltext. + cand := newDocBitset(n) + for _, cs := range [][]clause{q.should, q.adjust} { + for _, c := range cs { + if err := s.evalClauseInto(c, algo, avgDocLen, gs, true, func(ord int64, sc float32) { + score[ord] += sc + cand.set(int(ord)) + }); err != nil { + return err + } + } + } + cand.forEach(admit) + return nil +} + +// searchBooleanFull returns the segment's top-k boolean hits by feeding evalBoolean's +// admitted candidates into a bounded top-K heap (score desc; equal scores by ascending ord). +func (s *Segment) searchBooleanFull(q BoolQuery, algo ScoreAlgo, k int, allow Membership, gs *globalStats) ([]Result, error) { + if s.N == 0 || k <= 0 { + return nil, nil + } + h := newTopKHeap(k, s.N) + if err := s.evalBoolean(q, algo, allow, gs, func(ord int, score float32) { + h.Push(int64(ord), -score) + }); err != nil { + return nil, err + } + return heapToResults(s, h), nil +} + +// evalClauseInto streams one clause's weighted (doc, score) contributions to emit, WITHOUT +// building a per-clause map. Term and phrase clauses touch each doc once, so they emit +// directly (weight folded in); prefix and group clauses need a per-doc MAX over their +// expansion/children, so they fall back to evalClause's map (rare, and bounded by the +// prefix's terms). scored=false emits presence only (score 0), for MUST-NOT. +func (s *Segment) evalClauseInto(c clause, algo ScoreAlgo, avgDocLen float64, gs *globalStats, scored bool, emit func(int64, float32)) error { + w := c.weight + switch c.kind { + case clauseTerm: + pl, ok := s.lookup(c.terms[0]) + if !ok { + return nil + } + docs := pl.materializeDocIDs() + if !scored { + for _, ord := range docs { + emit(ord, 0) + } + return nil + } + idf2 := gs.idfFor(s, c.terms[0], pl) + tfs := pl.materializeTfs() + for i, ord := range docs { + emit(ord, w*s.scoreTerm(algo, float64(tfs[i]), idf2, ord, avgDocLen)) + } + case clausePhrase: + var hits []docTf + if gs != nil { + hits = gs.phraseHits(s, c.phrase) // memoized; shared with gs.phraseDf + } else { + hits = s.matchPhrase(c.phrase) + } + if !scored { + for _, hh := range hits { + emit(hh.ord, 0) + } + return nil + } + idf2 := gs.phraseIdfFor(s, c.phrase, len(hits)) + for _, hh := range hits { + emit(hh.ord, w*s.scoreTerm(algo, float64(hh.tf), idf2, hh.ord, avgDocLen)) + } + default: // clausePrefix, clauseGroup: per-doc MAX needs a temp (evalClause applies weight) + m, err := s.evalClause(c, algo, avgDocLen, gs, scored) + if err != nil { + return err + } + for ord, sc := range m { + emit(ord, sc) + } + } + return nil +} + +// evalClause returns the weighted (doc ord → score) contribution of one clause. +// A present doc is always a key (even at score 0, so MUST/presence is exact); an +// absent term yields an empty map. The clause weight (> < ~) scales the result. +// Term / prefix / phrase clauses all score with the global corpus idf (gs) — a phrase +// clause via the cross-segment phrase df (gs.phraseDf), so ranking is consistent +// across a base + CDC tail. +// +// scored=false skips the per-doc score computation (idf + scoreTerm) and returns a +// presence-only map (all 0 values). MUST-NOT clauses use this: only the ord set is read +// there, so scoring every doc of a common negated term (e.g. `-the`) was wasted work. +func (s *Segment) evalClause(c clause, algo ScoreAlgo, avgDocLen float64, gs *globalStats, scored bool) (map[int64]float32, error) { + raw := make(map[int64]float32) + switch c.kind { + case clauseTerm: + if pl, ok := s.lookup(c.terms[0]); ok { + docs := pl.materializeDocIDs() + if !scored { + for _, ord := range docs { + raw[ord] = 0 + } + break + } + idf2 := gs.idfFor(s, c.terms[0], pl) + tfs := pl.materializeTfs() + for i, ord := range docs { + // tf-aware BM25 (real within-doc occurrence count). Classic fulltext scores + // boolean matches with tf≡1, but ONLY because its SQL retrieval collapses + // GROUP BY doc_id to avoid a JOIN row blow-up (sql.go:493-496) — a + // restriction fulltext2's in-memory scorer does not have. Keeping tf gives + // better ranking among matched docs (BM25 saturation, k1, tames repetition) + // and tighter WAND max-impact bounds. This is an intentional divergence. + raw[ord] = s.scoreTerm(algo, float64(tfs[i]), idf2, ord, avgDocLen) + } + } + case clausePhrase: + var hits []docTf + if gs != nil { + hits = gs.phraseHits(s, c.phrase) // memoized; shared with gs.phraseDf + } else { + hits = s.matchPhrase(c.phrase) + } + if !scored { + for _, h := range hits { + raw[h.ord] = 0 + } + break + } + idf2 := gs.phraseIdfFor(s, c.phrase, len(hits)) + for _, h := range hits { + // tf-aware: real phrase-occurrence count (see clauseTerm for why fulltext2 keeps + // tf where classic's SQL forced tf≡1). + raw[h.ord] = s.scoreTerm(algo, float64(h.tf), idf2, h.ord, avgDocLen) + } + case clausePrefix: + terms, err := s.prefixTerms(c.terms[0]) + if err != nil { + return nil, err + } + for _, t := range terms { // combined impact = MAX over expanded terms (§6) + pl, ok := s.lookup(t) + if !ok { + continue + } + docs := pl.materializeDocIDs() + if !scored { + for _, ord := range docs { + raw[ord] = 0 + } + continue + } + idf2 := gs.idfFor(s, t, pl) + tfs := pl.materializeTfs() + for i, ord := range docs { + sc := s.scoreTerm(algo, float64(tfs[i]), idf2, ord, avgDocLen) // tf-aware (see clauseTerm) + if cur, seen := raw[ord]; !seen || sc > cur { + raw[ord] = sc + } + } + } + case clauseGroup: + for _, ch := range c.children { // OR of children, score = MAX (§6) + m, err := s.evalClause(ch, algo, avgDocLen, gs, scored) + if err != nil { + return nil, err + } + for ord, sc := range m { + if cur, seen := raw[ord]; !seen || sc > cur { + raw[ord] = sc + } + } + } + } + if c.weight != 1.0 { + for ord := range raw { + raw[ord] *= c.weight + } + } + return raw, nil +} + +// prefixTerms expands a word* prefix to its matching terms, over the loaded FST +// or the build-side sorted key list. +func (s *Segment) prefixTerms(prefix string) ([]string, error) { + if s.dict != nil { + return s.dict.prefixTerms(prefix) + } + return s.PrefixRange(prefix), nil +} + +// SearchBooleanText parses query in boolean mode (tokenizing with tok, the index's +// tokenizer) and evaluates it — the convenience entry mirroring +// MATCH(col) AGAINST('query' IN BOOLEAN MODE). +func (s *Segment) SearchBooleanText(query []byte, tok tokenizer.Tokenizer, algo ScoreAlgo, k int) ([]Result, error) { + q, err := ParseBoolean(query, tok) + if err != nil { + return nil, err + } + return s.SearchBoolean(q, algo, k, nil, nil) // convenience entry: no prefilter, local stats +} + +// ---- parser ---- + +// bucket classifies a clause by its prefix operator. +type bucket int + +const ( + bShould bucket = iota + bMust + bMustNot + bAdjust +) + +func bucketOf(prefix byte) bucket { + switch prefix { + case '+': + return bMust + case '-': + return bMustNot + case '~': + return bAdjust + default: // '>' '<' 0 + return bShould + } +} + +func weightOf(prefix byte) float32 { + switch prefix { + case '>': + return weightGreater + case '<': + return weightLess + case '~': + return weightTilde + default: + return 1.0 + } +} + +// ParseBoolean parses a boolean-mode query string into clauses, tokenizing each +// leaf's text with tok. Surface: leading +/-/>/ < ~ or 0 + quoted bool + star bool + group bool + text string + children []rawClause +} + +func isPrefixChar(c byte) bool { + return c == '+' || c == '-' || c == '>' || c == '<' || c == '~' +} + +// scanBoolean splits a boolean query into raw clauses at the top level. +func scanBoolean(q string) []rawClause { + cs, _ := scanRange(q, 0) + return cs +} + +// scanRange scans clauses from index i until end-of-string or an unmatched ')' +// (a group close), returning the clauses and the index just past the stop point. +func scanRange(q string, i int) ([]rawClause, int) { + var out []rawClause + n := len(q) + for i < n { + for i < n && q[i] == ' ' { + i++ + } + if i >= n { + break + } + if q[i] == ')' { + return out, i + 1 // end of this group + } + var rc rawClause + if isPrefixChar(q[i]) { + rc.prefix = q[i] + i++ + } + switch { + case i < n && q[i] == '(': + rc.group = true + i++ + rc.children, i = scanRange(q, i) + case i < n && q[i] == '"': + rc.quoted = true + i++ + start := i + for i < n && q[i] != '"' { + i++ + } + rc.text = q[start:i] + if i < n { + i++ // closing quote + } + default: + start := i + for i < n && q[i] != ' ' && q[i] != ')' { + i++ + } + word := q[start:i] + if len(word) > 0 && word[len(word)-1] == '*' { + rc.star = true + word = word[:len(word)-1] + } + rc.text = word + } + if rc.group || rc.text != "" { + out = append(out, rc) + } + } + return out, i +} diff --git a/pkg/fulltext2/build.go b/pkg/fulltext2/build.go new file mode 100644 index 0000000000000..ad4ba0a4d6564 --- /dev/null +++ b/pkg/fulltext2/build.go @@ -0,0 +1,485 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" +) + +// Doc is one source row to index: its primary key and the text of the indexed +// column(s). +type Doc struct { + Pk any + Text []byte +} + +// tokenWord decodes the length-prefixed word out of a tokenizer.Token +// (TokenBytes[0] is the byte length; the word follows). The tokenizer already +// lowercases and truncates to MAX_TOKEN_SIZE. +func tokenWord(tk tokenizer.Token) string { + n := int(tk.TokenBytes[0]) + return string(tk.TokenBytes[1 : 1+n]) +} + +// buildOpts carries optional build settings shared by the build entry points. +type buildOpts struct { + positionFree bool // drop the positional payload (keep the FST + docID/tf postings) + includeTypes []int32 // types.T of each INCLUDE column (nil = no INCLUDE columns) +} + +// BuildOpt configures a segment build (functional-options, so existing callers are +// unaffected). +type BuildOpt func(*buildOpts) + +// WithPositionFree builds a POSITION-FREE segment: term frequencies are still derived +// (so BM25 ranking is unchanged), but the per-token positional payload — the largest +// section (~half the segment) — is omitted and the FST term dictionary is kept. Phrase +// / NL exact-match is therefore unavailable on such a segment (bag-of-words retrieval +// only). Mirrors the footprint of a position-free bm25 index while keeping the FST. +func WithPositionFree() BuildOpt { return func(o *buildOpts) { o.positionFree = true } } + +// WithIncludeTypes builds a segment that carries per-doc INCLUDE column values (the actual +// typed values, stored in the docmap for in-index prefilter + covering). types holds each +// INCLUDE column's types.T in column order; a doc's values are supplied via Builder.SetInclude +// / SetDoc (or TokenizedDoc.Include on the batch build path). Nil/empty ⇒ no INCLUDE columns. +func WithIncludeTypes(types []int32) BuildOpt { + return func(o *buildOpts) { o.includeTypes = types } +} + +func applyBuildOpts(opts []BuildOpt) buildOpts { + var bo buildOpts + for _, o := range opts { + o(&bo) + } + return bo +} + +// buildEntry is one (doc ord, positions-in-that-doc) occurrence of a term, collected +// in ascending doc order (SearchPhrase relies on the ordering). +type buildEntry struct { + ord int64 + positions []int32 +} + +// addPosting records one (term, byte-position) occurrence for document ord into global, +// keeping each term's buildEntry list ascending by ord. The caller MUST feed documents in +// ascending ord and a document's tokens contiguously (both build paths do), so the last +// entry for a term is always the current document's when the term repeats within it — a +// repeat then extends that entry's positions in place. This groups a document's repeated +// occurrences of a term into ONE buildEntry (tf = len(positions), df = len(entries)) without +// a per-document map, which profiled as ~21% of a large CREATE FULLTEXT2 build (the old +// per-doc `local := make(map[string][]int32)` + per-token map assign). +func addPosting(global map[string][]buildEntry, w string, ord int64, pos int32) { + entries := global[w] + if n := len(entries); n > 0 && entries[n-1].ord == ord { + // Same document, repeated term: extend the open entry in place. entries aliases the + // map's slice, so mutating entries[n-1] updates global[w][n-1] — no map write needed. + entries[n-1].positions = append(entries[n-1].positions, pos) + return + } + global[w] = append(entries, buildEntry{ord: ord, positions: []int32{pos}}) +} + +// assembleTerms turns the collected per-term (ord, positions) lists into termPostings. +// When positionFree, tf is still derived from the occurrence count but the positional +// payload is dropped (tp.positions left nil) — Serialize then writes an empty positions +// section while the FST/docID/tf sections are unchanged. +func assembleTerms(global map[string][]buildEntry, docLen []int32, positionFree bool) map[string]*termPostings { + terms := make(map[string]*termPostings, len(global)) + for w, entries := range global { + tp := &termPostings{ + docIDs: make([]int64, len(entries)), + tfs: make([]uint8, len(entries)), + } + if !positionFree { + tp.positions = make([][]int32, len(entries)) + } + for i, e := range entries { + tp.docIDs[i] = e.ord + tf := len(e.positions) + if tf > MaxCappedTf { + tf = MaxCappedTf + } + tp.tfs[i] = uint8(tf) + if !positionFree { + tp.positions[i] = e.positions + } + } + // Derive the raw term-level + Block-Max score-UB fields (mirrors the load + // path), so a build-side segment gets the same WAND block-skip bounds. + deriveTermStats(tp, docLen) + terms[w] = tp + } + return terms +} + +// BuildSegmentFromDocs tokenizes each doc with tok and builds an in-memory +// (build-side) segment: per-term posting lists with per-doc token positions, +// the docmap (pk + token-count length), and AvgDocLen. Docs are indexed by their +// slice position (the doc ord), so pks/docLen are ord-aligned. +// +// It is the minimal build path used to produce queryable segments (tests, and +// the seed for the eventual CDC/sync build sink). Positions are the tokenizer's +// TokenPos, so phrase adjacency in a query matches adjacency in the source text. +func BuildSegmentFromDocs(id string, pkType int32, docs []Doc, tok tokenizer.Tokenizer, opts ...BuildOpt) (*Segment, error) { + bo := applyBuildOpts(opts) + s := NewSegment(id, pkType) + s.pks = make([]any, len(docs)) + s.docLen = make([]int32, len(docs)) + + // term -> ascending list of (doc ord, positions-in-that-doc). Built in doc + // order, so each term's list is ascending by ord (SearchPhrase relies on it). + global := make(map[string][]buildEntry) + + for ord, d := range docs { + s.pks[ord] = d.Pk + var ntok int32 + for tk, err := range tok.Tokenize(d.Text) { + if err != nil { + return nil, err + } + addPosting(global, tokenWord(tk), int64(ord), tk.BytePos) + ntok++ + } + s.docLen[ord] = ntok + } + + terms := assembleTerms(global, s.docLen, bo.positionFree) + + s.N = int64(len(docs)) + s.setTerms(terms) + s.AvgDocLen = meanDocLen(s.docLen) + return s, nil +} + +// SearchText tokenizes query with tok (the same tokenizer used to build the +// segment) and runs an NL exact-phrase search — the convenience entry point that +// mirrors MATCH(col) AGAINST('query') in natural-language mode. +func (s *Segment) SearchText(query []byte, tok tokenizer.Tokenizer, algo ScoreAlgo, k int) ([]Result, error) { + return s.SearchPhrase(tokenizePhraseSlots(tok, query), algo, k), nil +} + +// TokenizedDoc is a document already tokenized into an ordered term slice (the +// term at index i has token position i). Used by the build path that tokenizes +// via the execution-side fulltext2_tokenize TVF (datalink/json/parsers resolved +// there) and only assembles the segment here. +type TokenizedDoc struct { + Pk any + Terms []string + Positions []int32 // byte position of Terms[i] (parallel to Terms) + // Include holds this doc's INCLUDE column values in column order (nil element = SQL + // NULL; a nil slice = all NULL). Only meaningful when the segment is built with + // WithIncludeTypes; ignored otherwise. + Include []any +} + +// BuildSegmentFromTokenized builds a segment from pre-tokenized docs (positions +// are term indices). It is the assembly half of BuildSegmentFromDocs without the +// tokenizer — the terms already came from fulltext2_tokenize at execution. +func BuildSegmentFromTokenized(id string, pkType int32, docs []TokenizedDoc, opts ...BuildOpt) (*Segment, error) { + bo := applyBuildOpts(opts) + s := NewSegment(id, pkType) + s.pks = make([]any, len(docs)) + s.docLen = make([]int32, len(docs)) + + global := make(map[string][]buildEntry) + + for ord, d := range docs { + s.pks[ord] = d.Pk + for i, w := range d.Terms { + pos := int32(i) // fall back to token index when Positions is unset + if i < len(d.Positions) { + pos = d.Positions[i] // byte position + } + addPosting(global, w, int64(ord), pos) + } + s.docLen[ord] = int32(len(d.Terms)) + } + + terms := assembleTerms(global, s.docLen, bo.positionFree) + + // INCLUDE column values: carried on each TokenizedDoc.Include, packed ord-aligned into + // the segment so encodeDocmap writes the docmap include section. A nil Include ⇒ all-NULL + // row (encodeDocmap fills NULLs for missing columns). + if len(bo.includeTypes) > 0 { + s.includeTypes = bo.includeTypes + s.includeVals = make([][]any, len(docs)) + for ord := range docs { + s.includeVals[ord] = docs[ord].Include + } + } + + s.N = int64(len(docs)) + s.setTerms(terms) + s.AvgDocLen = meanDocLen(s.docLen) + return s, nil +} + +// DefaultBuildCapacity floors a non-positive segment DOC capacity for the streaming +// build paths (base CREATE build and the CDC tail), so a segment is sealed and +// spilled every ~1M docs and peak build memory stays bounded to one segment even +// when max_index_capacity is unset. Splitting a logically "unbounded" base into +// 1M-doc sub-indexes is transparent to queries (the Index already spans segments). +const DefaultBuildCapacity int64 = 1000000 + +// DefaultPostingCapacity floors a non-positive segment POSTING capacity +// (max_postings_capacity). Per-segment build memory tracks the number of +// accumulated postings (one term occurrence ≈ a term-string ref + an int32 +// position ≈ 30 B of Go heap, ~2× at assembly), NOT the doc count — a doc can hold +// one token or tens of thousands, so a doc-only seal lets a long-document corpus +// (e.g. wiki articles ≈ 1k tokens each) blow past any memory bound. 8M postings ≈ +// a ~512 MB peak build heap per segment; a segment seals on whichever cap +// (docs OR postings) is reached first, so build memory is bounded regardless of +// doc shape. See ReachedSegmentCap. +const DefaultPostingCapacity int64 = 8_000_000 + +// DefaultDataBytesCapacity bounds the retained variable-width PAYLOAD heap — the copied +// varlena primary-key and INCLUDE-column bytes held in Builder.docs — which neither the doc +// cap nor the posting cap covers. A corpus of one-token rows (postings ≈ docs) with wide +// VARCHAR/BLOB pk or INCLUDE values reaches neither of those caps yet can retain up to +// DefaultBuildCapacity docs' worth of near-max varlena data (tens of GiB) before sealing. +// 512 MiB keeps that payload heap in the same ~512 MB per-segment envelope as the posting +// cap, so total build peak stays bounded regardless of doc shape. See ReachedSegmentCap. +const DefaultDataBytesCapacity int64 = 512 << 20 // 512 MiB + +// ReachedSegmentCap reports whether an open streaming Builder has hit any of its three +// per-segment build-memory bounds: the doc cap (max_index_capacity, bounds the docmap/liveOrd +// heap for a many-tiny-docs index), the posting cap (max_postings_capacity, bounds the +// positional posting heap for a few-long-docs index), or the payload-bytes cap (bounds the +// retained varlena pk + INCLUDE heap for a wide-varlena corpus). Non-positive doc/posting caps +// fall back to their defaults. Every streaming build path (CREATE TVF, CDC TailBuilder, +// MERGE/REBUILD compaction) seals on this single predicate so peak build memory is one bounded +// segment. +// +// The cap is checked AFTER a row is added, so a SINGLE row larger than a cap seals into its own +// segment (peak bounded to that one oversized row) rather than being rejected — a covering CREATE +// must still ingest a legitimately huge row. +func ReachedSegmentCap(b *Builder, docCap, postingCap int64) bool { + if docCap <= 0 { + docCap = DefaultBuildCapacity + } + if postingCap <= 0 { + postingCap = DefaultPostingCapacity + } + // Compare the int64 counters directly (not via the int-returning getters) so a cap above + // 2^31 can never wrap on a 32-bit build. + return int64(b.NumDocs()) >= docCap || b.postings >= postingCap || b.dataBytes >= DefaultDataBytesCapacity +} + +// Builder accumulates a token stream fed in (word, pk) order — the positional +// analogue of bm25's wand.Builder, with the SAME API (NewBuilder / Add / Finish / +// FinishSegments) so the two can share a core later. Each Add appends one token +// occurrence to its document (position = the doc's running token count), so the +// caller feeds a document's tokens contiguously and in order. +type Builder struct { + id string + pkType int32 + ordMap map[any]int64 // normalized pk -> ord + docs []TokenizedDoc + postings int64 // total term occurrences Add'd (one Add == one posting) + // dataBytes is the retained variable-width payload heap: the copied varlena pk bytes plus + // the retained INCLUDE value bytes across all open docs. It is the memory dimension the doc + // and posting caps miss (a one-token row with a huge VARCHAR pk/INCLUDE), so ReachedSegmentCap + // also seals on it. Kept in lock-step: docOrd adds a new doc's pk bytes; SetDoc/SetInclude + // add the new INCLUDE bytes and subtract the superseded ones on replace. + dataBytes int64 + opts []BuildOpt // carried into FinishSegments (e.g. WithPositionFree) +} + +// anyDataBytes is the retained-heap size of one boxed pk / INCLUDE value: the content length +// for a varlena value (the dimension that blows up), a small fixed estimate for a scalar, 0 for +// SQL NULL. Used to keep Builder.dataBytes tracking the varlena payload the caps must bound. +func anyDataBytes(v any) int64 { + switch x := v.(type) { + case nil: + return 0 + case string: + return int64(len(x)) + case []byte: + return int64(len(x)) + default: + return 8 // fixed-width scalar (int/uint/temporal/decimal) — bounded, small + } +} + +// includeDataBytes sums the retained payload of a doc's INCLUDE value slice. +func includeDataBytes(include []any) int64 { + var n int64 + for _, v := range include { + n += anyDataBytes(v) + } + return n +} + +// NewBuilder creates a Builder for an index id and source pk type (types.T). Pass +// WithPositionFree() to build position-free segments. +func NewBuilder(id string, pkType int32, opts ...BuildOpt) *Builder { + return &Builder{id: id, pkType: pkType, ordMap: make(map[any]int64), opts: opts} +} + +// docOrd returns the dense ord for a pk, assigning one on first sight (mirrors +// wand.Builder.docOrd). +func (b *Builder) docOrd(pk any) int64 { + key := builderKey(pk) + if o, ok := b.ordMap[key]; ok { + return o + } + o := int64(len(b.docs)) + b.ordMap[key] = o + b.docs = append(b.docs, TokenizedDoc{Pk: builderCopyPk(pk)}) + b.dataBytes += anyDataBytes(pk) // retained copied pk bytes (varlena payload the caps must bound) + return o +} + +// Add records one (word, byte-position, doc) token occurrence. Tokens of a document +// must be fed contiguously and in order; pos is the token's BYTE position in the doc +// (so a positional phrase query can match by byte offset, consistent with the base +// build's tk.BytePos and classic fulltext's byte-position phrase JOIN). +func (b *Builder) Add(word string, pos int32, pk any) error { + if word == "" { + return moerr.NewInternalErrorNoCtx("fulltext2 builder: empty word") + } + ord := b.docOrd(pk) + b.docs[ord].Terms = append(b.docs[ord].Terms, word) + b.docs[ord].Positions = append(b.docs[ord].Positions, pos) + b.postings++ + return nil +} + +// SetDoc REPLACES a pk's document terms with words (assigning a fresh ord on first +// sight), rather than appending like Add. It is the CDC upsert primitive: a later +// upsert of a pk already present in this open segment must supersede — not merge with — +// its earlier terms (Add would leave both the old and new terms searchable under the +// same live doc). A zero-word upsert produces a live 0-term doc: it carries the pk + +// this segment's Recency, so it SHADOWS any lower-Recency copy (base or an earlier tail +// segment) at query time while itself matching nothing — the way an upsert to empty / +// NULL text removes the old version from search. Positions/Terms are reset in place so +// repeated SetDoc of one pk does not grow the backing arrays unboundedly. +func (b *Builder) SetDoc(pk any, words []WordPos, include []any) { + ord := b.docOrd(pk) + d := &b.docs[ord] + b.postings -= int64(len(d.Terms)) // drop the prior version's postings before rewriting + d.Terms = d.Terms[:0] + d.Positions = d.Positions[:0] + for _, w := range words { + if w.Word == "" { + continue // never index an empty token (mirrors Add's guard) + } + d.Terms = append(d.Terms, w.Word) + d.Positions = append(d.Positions, w.Pos) + } + b.postings += int64(len(d.Terms)) + // An upsert supersedes the prior version's INCLUDE values in lock-step with its terms; + // track the payload-byte delta so dataBytes reflects only the live version. + b.dataBytes += includeDataBytes(include) - includeDataBytes(d.Include) + d.Include = include +} + +// SetInclude sets a doc's INCLUDE column values (assigning a fresh ord on first sight, +// like Add). Used by the Add-based build paths (base CREATE, MERGE reconstruction) which +// feed a doc's tokens via Add and then attach its per-row INCLUDE values once. include is +// retained as-is (column order; a nil element = SQL NULL). +func (b *Builder) SetInclude(pk any, include []any) { + ord := b.docOrd(pk) + b.dataBytes += includeDataBytes(include) - includeDataBytes(b.docs[ord].Include) + b.docs[ord].Include = include +} + +// NumDocs returns the number of distinct documents added so far. +func (b *Builder) NumDocs() int { return len(b.docs) } + +// NumDataBytes returns the retained variable-width payload heap (copied varlena pk + INCLUDE +// bytes) — the dimension DefaultDataBytesCapacity bounds in ReachedSegmentCap. +func (b *Builder) NumDataBytes() int64 { return b.dataBytes } + +// NumPostings returns the number of term occurrences Add'd so far (one Add == one +// posting). It is the memory-correlated dimension the streaming build paths seal +// on (max_postings_capacity) — see ReachedSegmentCap. +func (b *Builder) NumPostings() int { return int(b.postings) } + +// Finish produces a single-segment index (no capacity limit). +func (b *Builder) Finish() (*Segment, error) { + segs, err := b.FinishSegments(0) + if err != nil { + return nil, err + } + return segs[0], nil +} + +// FinishSegments finalizes the build into one or more segments, each holding at +// most capacity documents (capacity <= 0 => a single segment). Segment i is built +// under SubIndexId(b.id, i); the caller may override Segment.Id. Mirrors +// wand.Builder.FinishSegments (contiguous doc-ord range split). +func (b *Builder) FinishSegments(capacity int64) ([]*Segment, error) { + n := int64(len(b.docs)) + if n == 0 { + return nil, nil + } + if capacity <= 0 || n <= capacity { + seg, err := BuildSegmentFromTokenized(SubIndexId(b.id, 0), b.pkType, b.docs, b.opts...) + if err != nil { + return nil, err + } + return []*Segment{seg}, nil + } + var segs []*Segment + i := 0 + for lo := int64(0); lo < n; lo += capacity { + hi := lo + capacity + if hi > n { + hi = n + } + seg, err := BuildSegmentFromTokenized(SubIndexId(b.id, i), b.pkType, b.docs[lo:hi], b.opts...) + if err != nil { + return nil, err + } + segs = append(segs, seg) + i++ + } + return segs, nil +} + +// builderKey / builderCopyPk mirror wand.go's normalizeKey / copyPk so a []byte +// pk is a stable map key and safely retained. +func builderKey(pk any) any { + if b, ok := pk.([]byte); ok { + return string(b) + } + return pk +} + +func builderCopyPk(pk any) any { + if b, ok := pk.([]byte); ok { + c := make([]byte, len(b)) + copy(c, b) + return c + } + return pk +} + +// tokenizeToTerms flattens a tokenizer stream into the ordered term slice a +// phrase query needs. +func tokenizeToTerms(text []byte, tok tokenizer.Tokenizer) ([]string, error) { + // token count is unknown up front; estimate from input length to avoid regrowth. + terms := make([]string, 0, len(text)/4) + for tk, err := range tok.Tokenize(text) { + if err != nil { + return nil, err + } + terms = append(terms, tokenWord(tk)) + } + return terms, nil +} diff --git a/pkg/fulltext2/cdc.go b/pkg/fulltext2/cdc.go new file mode 100644 index 0000000000000..e080a3558e6f9 --- /dev/null +++ b/pkg/fulltext2/cdc.go @@ -0,0 +1,242 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "encoding/binary" + "hash/crc32" + "io" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" +) + +// The CDC-event codec: the ISCP consumer's Fulltext2SqlWriter accumulates a +// flush's source-row mutations into a Cdc, ships it as one channel blob (Encode), +// and RunFulltext2 decodes it (DecodeCdc) and STREAMS it through a TailBuilder +// into capacity-capped tag=1 CdcTail segments. The blob is BINARY (typed pk) — +// like bm25's WandCdc — because a fulltext2 pk is `any` (int64 OR varchar) and a +// JSON round-trip would corrupt a non-integer pk. + +type cdcOp byte + +const ( + cdcInsert cdcOp = 'I' + cdcUpsert cdcOp = 'U' + cdcDelete cdcOp = 'D' +) + +// CdcEvent is one source-row mutation: an INSERT/UPSERT carries the row's text +// (tokenized at build) and its INCLUDE column values; a DELETE carries only the pk. +type CdcEvent struct { + Op cdcOp + Pk any + Text string + Include []any // INCLUDE column values in column order (nil element = NULL); nil for DELETE +} + +// Cdc is the per-flush CDC batch the sinker accumulates and ships as one blob. +// IncludeTypes is the index's INCLUDE column schema (types.T per column, column order), +// needed to typed-encode/decode each event's Include values; nil ⇒ no INCLUDE columns. +type Cdc struct { + PkType int32 + IncludeTypes []int32 + Events []CdcEvent +} + +func NewCdc(pkType int32) *Cdc { return &Cdc{PkType: pkType} } + +func (c *Cdc) Insert(pk any, text string, include []any) { + c.Events = append(c.Events, CdcEvent{cdcInsert, pk, text, include}) +} +func (c *Cdc) Upsert(pk any, text string, include []any) { + c.Events = append(c.Events, CdcEvent{cdcUpsert, pk, text, include}) +} +func (c *Cdc) Delete(pk any) { c.Events = append(c.Events, CdcEvent{cdcDelete, pk, "", nil}) } +func (c *Cdc) Len() int { return len(c.Events) } + +const cdcMagic uint32 = 0x46540200 // 'F' 'T' 02 00 — fulltext2 cdc events + +// Encode serializes the batch: magic | pkType | count | +// [op:1 pkLen:u32 pk textLen:u32 text]* | crc32. Self-describing + CRC-checked. +func (c *Cdc) Encode() ([]byte, error) { + var b bytes.Buffer + _ = binary.Write(&b, binary.LittleEndian, cdcMagic) + _ = binary.Write(&b, binary.LittleEndian, c.PkType) + // INCLUDE column schema: count + per-col type (0 ⇒ no INCLUDE columns). Lets the + // decoder typed-decode each event's Include values without an external schema. + _ = binary.Write(&b, binary.LittleEndian, uint32(len(c.IncludeTypes))) + for _, t := range c.IncludeTypes { + _ = binary.Write(&b, binary.LittleEndian, t) + } + _ = binary.Write(&b, binary.LittleEndian, int64(len(c.Events))) + for _, e := range c.Events { + pkb, err := encodePk(c.PkType, e.Pk) + if err != nil { + return nil, err + } + b.WriteByte(byte(e.Op)) + _ = binary.Write(&b, binary.LittleEndian, uint32(len(pkb))) + b.Write(pkb) + _ = binary.Write(&b, binary.LittleEndian, uint32(len(e.Text))) + b.WriteString(e.Text) + // INCLUDE values (column order); a missing/nil value is SQL NULL. Same + // [nullFlag][u32 len][content] shape as the docmap include section. + for ci, t := range c.IncludeTypes { + var v any + if ci < len(e.Include) { + v = e.Include[ci] + } + if v == nil { + b.WriteByte(includeNull) + continue + } + vb, err := encodePk(t, v) + if err != nil { + return nil, err + } + b.WriteByte(includeNotNull) + _ = binary.Write(&b, binary.LittleEndian, uint32(len(vb))) + b.Write(vb) + } + } + sum := crc32.ChecksumIEEE(b.Bytes()) + _ = binary.Write(&b, binary.LittleEndian, sum) + return b.Bytes(), nil +} + +// DecodeCdc reverses Encode, validating magic + CRC. +func DecodeCdc(buf []byte) (*Cdc, error) { + if len(buf) < 4+4+8+4 { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 cdc: truncated") + } + body := buf[:len(buf)-4] + if crc32.ChecksumIEEE(body) != binary.LittleEndian.Uint32(buf[len(buf)-4:]) { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 cdc: checksum mismatch") + } + r := bytes.NewReader(body) + var magic uint32 + _ = binary.Read(r, binary.LittleEndian, &magic) + if magic != cdcMagic { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 cdc: bad magic") + } + c := &Cdc{} + if err := binary.Read(r, binary.LittleEndian, &c.PkType); err != nil { + return nil, err + } + var nIncl uint32 + if err := binary.Read(r, binary.LittleEndian, &nIncl); err != nil { + return nil, err + } + if nIncl > maxIncludeCols { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 cdc: bad include col count") + } + if nIncl > 0 { + c.IncludeTypes = make([]int32, nIncl) + for i := range c.IncludeTypes { + if err := binary.Read(r, binary.LittleEndian, &c.IncludeTypes[i]); err != nil { + return nil, err + } + } + } + var n int64 + if err := binary.Read(r, binary.LittleEndian, &n); err != nil { + return nil, err + } + if n < 0 { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 cdc: bad count") + } + // n is read straight off a (possibly corrupt) blob; a bogus huge count would make() + // gigabytes up front. Cap the pre-alloc hint — append grows it for a genuinely large + // frame, and a truncated stream errors out per-event in the loop below. + capHint := n + if capHint > 4096 { + capHint = 4096 + } + c.Events = make([]CdcEvent, 0, capHint) + for i := int64(0); i < n; i++ { + op, err := r.ReadByte() + if err != nil { + return nil, err + } + pk, err := cdcReadLenBytes(r, c.PkType) + if err != nil { + return nil, err + } + text, err := cdcReadLenString(r) + if err != nil { + return nil, err + } + var include []any + if nIncl > 0 { + include = make([]any, nIncl) + for ci := 0; ci < int(nIncl); ci++ { + v, ierr := cdcReadInclude(r, c.IncludeTypes[ci]) + if ierr != nil { + return nil, ierr + } + include[ci] = v + } + } + c.Events = append(c.Events, CdcEvent{Op: cdcOp(op), Pk: pk, Text: text, Include: include}) + } + return c, nil +} + +// cdcReadInclude reads one INCLUDE value: a null flag, then (if not null) a u32 length + +// typed content decoded via decodePk. Returns nil for a NULL value. +func cdcReadInclude(r *bytes.Reader, t int32) (any, error) { + flag, err := r.ReadByte() + if err != nil { + return nil, err + } + if flag == includeNull { + return nil, nil + } + if flag != includeNotNull { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 cdc: bad include null-flag") + } + return cdcReadLenBytes(r, t) +} + +func cdcReadLenBytes(r *bytes.Reader, pkType int32) (any, error) { + var l uint32 + if err := binary.Read(r, binary.LittleEndian, &l); err != nil { + return nil, err + } + if int64(l) > int64(r.Len()) { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 cdc: truncated pk") + } + pkb := make([]byte, l) + if _, err := io.ReadFull(r, pkb); err != nil { + return nil, err + } + return decodePk(pkType, pkb) +} + +func cdcReadLenString(r *bytes.Reader) (string, error) { + var l uint32 + if err := binary.Read(r, binary.LittleEndian, &l); err != nil { + return "", err + } + if int64(l) > int64(r.Len()) { + return "", moerr.NewInternalErrorNoCtx("fulltext2 cdc: truncated text") + } + sb := make([]byte, l) + if _, err := io.ReadFull(r, sb); err != nil { + return "", err + } + return string(sb), nil +} diff --git a/pkg/fulltext2/compact.go b/pkg/fulltext2/compact.go new file mode 100644 index 0000000000000..6deb66416e800 --- /dev/null +++ b/pkg/fulltext2/compact.go @@ -0,0 +1,177 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "fmt" + "time" + + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" +) + +// CompactSegments folds the tag=1 CdcTail into the tag=0 base and reclaims dead +// space: it loads base + tail + deletes, reconstructs the LIVE docs from their +// positional postings (no re-tokenize), rebuilds capacity-bounded fresh tag=0 +// bases, and atomically replaces ALL prior bases + the whole tail — within the +// caller's (TVF statement) transaction. This is a FULL compaction, simpler than +// bm25's incremental tail-fold; an incremental variant is a later optimization. +// Returns the live-doc count. No tail delta → nothing to reclaim (returns 0). +func CompactSegments(sqlproc *sqlexec.SqlProcess, cfg TableConfig, capacity, postingCap int64) (int, error) { + bases, err := LoadAllBases(sqlproc, cfg) + if err != nil { + return 0, err + } + tails, deletes, err := LoadTailSegments(sqlproc, cfg) + if err != nil { + freeSegs(bases) + return 0, err + } + // Free the loaded (off-heap) base + tail buffers on every return path; the fresh + // rebuilt segments below are build-side (Go heap, no deallocators — Free no-op). + defer freeSegs(bases) + defer freeSegs(tails) + if len(tails) == 0 && len(deletes) == 0 { + return 0, nil // no CDC delta since the last build/compaction + } + + // Recency for the fresh base: above every existing base + tail chunk_id, so a + // later tail append (NextTailChunkId = base+1) stays monotonic and newer. + recency, err := NextTailChunkId(sqlproc, cfg) + if err != nil { + return 0, err + } + + segs := append(bases, tails...) + pkType := int32(0) + if len(segs) > 0 { + pkType = segs[0].PkType + } + idx := NewIndex(segs, deletes) + if capacity <= 0 { + capacity = DefaultBuildCapacity // floor so the fresh base is sealed+spilled per ~1M docs + } + if postingCap <= 0 { + postingCap = DefaultPostingCapacity // floor so a long-doc corpus's rebuild stays memory-bounded + } + + ts := time.Now().UnixMicro() + uid := fmt.Sprintf("%s:%d", cfg.IndexTable, ts) + var bopts []BuildOpt + if cfg.PositionFree { + bopts = append(bopts, WithPositionFree()) + } + // Carry the INCLUDE schema into the rebuilt base so the fresh segments keep the docmap + // include section (the source segments all share one schema; read the first that has it). + var incTypes []int32 + for _, s := range segs { + if s.nIncludeCols() > 0 { + incTypes = s.includeTypes + break + } + } + if len(incTypes) > 0 { + bopts = append(bopts, WithIncludeTypes(incTypes)) + } + + // Atomic replace, all in the caller's (TVF statement) txn: drop every prior base + + // the whole tail FIRST, then STREAM the fresh dead-doc-free base(s). Deleting first + // is required because DeleteAllBases is indiscriminate over tag=0 — writing the fresh + // bases first would let it wipe them. It is safe because ReconstructLiveDocs reads the + // already-loaded in-memory idx (not the storage rows), and a mid-stream failure rolls + // the whole statement back. + for _, s := range DeleteAllBasesSqls(cfg) { + if e := runCompactSql(sqlproc, s); e != nil { + return 0, e + } + } + for _, s := range DeleteTailSqls(cfg) { + if e := runCompactSql(sqlproc, s); e != nil { + return 0, e + } + } + + // Stream the reconstructed live docs into a capacity-bounded builder, sealing + + // persisting + freeing each fresh base segment as it fills — so peak build memory is + // one open segment, not the whole rebuilt corpus. + var cur *Builder + segIdx := 0 + nlive := 0 + sealFresh := func() error { + if cur == nil || cur.NumDocs() == 0 { + cur = nil + return nil + } + seg, err := cur.Finish() + cur = nil + if err != nil { + return err + } + seg.Id = SubIndexId(uid, segIdx) + seg.Recency = recency + segIdx++ + sqls, cleanup, e := seg.ToInsertSqls(sqlproc, cfg, ts, 0 /* tag=0 base */) + if e != nil { + return e + } + defer cleanup() + return runCompactSqls(sqlproc, sqls) + } + for d, derr := range idx.ReconstructLiveDocs(cfg.PositionFree) { + if derr != nil { + return 0, derr + } + if cur == nil { + cur = NewBuilder(uid, pkType, bopts...) + } + for i, w := range d.Terms { + if e := cur.Add(w, d.Positions[i], d.Pk); e != nil { + return 0, e + } + } + // Attach the doc's INCLUDE values (only for docs the Add loop created; a 0-term + // shadow doc is intentionally dropped by MERGE — nothing left to shadow, never matches). + if len(incTypes) > 0 && len(d.Terms) > 0 { + cur.SetInclude(d.Pk, d.Include) + } + nlive++ + if ReachedSegmentCap(cur, capacity, postingCap) { + if e := sealFresh(); e != nil { + return 0, e + } + } + } + if e := sealFresh(); e != nil { + return 0, e + } + return nlive, nil +} + +func runCompactSql(sqlproc *sqlexec.SqlProcess, sql string) error { + res, err := runSql(sqlproc, sql) + if err != nil { + return err + } + res.Close() + return nil +} + +func runCompactSqls(sqlproc *sqlexec.SqlProcess, sqls []string) error { + for _, s := range sqls { + if err := runCompactSql(sqlproc, s); err != nil { + return err + } + } + return nil +} diff --git a/pkg/fulltext2/deletes.go b/pkg/fulltext2/deletes.go new file mode 100644 index 0000000000000..6217297223210 --- /dev/null +++ b/pkg/fulltext2/deletes.go @@ -0,0 +1,145 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "encoding/binary" + "hash/crc32" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/types" +) + +// DeleteRecord is one tombstone: a pk deleted by a CDC batch. Its order is the +// containing frame's chunk_id (assigned at load), not a stored field. Mirrors +// bm25's DeleteRecord. +type DeleteRecord struct { + Pk any +} + +const deleteLogMagic uint32 = 0x46540100 // 'F' 'T' 01 00 — fulltext2 delete log + +// pkFixedWidth reports the fixed byte width of an integer pk type (so the delete +// log can skip the per-pk length prefix); varlena/other types are length-prefixed. +func pkFixedWidth(pkType int32) (int, bool) { + switch t := types.T(pkType); t { + case types.T_int64, types.T_uint64, types.T_int32, types.T_uint32: + return t.FixedLength(), true + default: + return -1, false + } +} + +// EncodeDeleteLog serializes delete records into one self-describing, CRC32-checked +// chunk: magic | pkType | count | pks | crc. A fixed-width int pk is stored bare +// (width implied by pkType); a varlena pk is length-prefixed. Mirrors bm25's +// EncodeDeleteLog. +func EncodeDeleteLog(pkType int32, recs []DeleteRecord) ([]byte, error) { + var w leBuf + w.u32(deleteLogMagic) + w.i32(pkType) + w.i64(int64(len(recs))) + width, fixed := pkFixedWidth(pkType) + for _, r := range recs { + pkb, err := encodePk(pkType, r.Pk) + if err != nil { + return nil, err + } + if fixed { + if len(pkb) != width { + return nil, moerr.NewInternalErrorNoCtxf("fulltext2 delete log: pk width %d != %d for fixed type %d", len(pkb), width, pkType) + } + } else { + w.u32(uint32(len(pkb))) + } + w.b.Write(pkb) + } + sum := crc32.ChecksumIEEE(w.b.Bytes()) + w.u32(sum) + return w.b.Bytes(), nil +} + +// DecodeDeleteLog reverses EncodeDeleteLog, validating magic + CRC. +func DecodeDeleteLog(buf []byte) ([]DeleteRecord, error) { + if len(buf) < 4+4+8+4 { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 delete log: truncated") + } + body := buf[:len(buf)-4] + if crc32.ChecksumIEEE(body) != binary.LittleEndian.Uint32(buf[len(buf)-4:]) { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 delete log: checksum mismatch") + } + if binary.LittleEndian.Uint32(body[0:4]) != deleteLogMagic { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 delete log: bad magic") + } + pkType := int32(binary.LittleEndian.Uint32(body[4:8])) + n := int64(binary.LittleEndian.Uint64(body[8:16])) + if n < 0 { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 delete log: bad count") + } + width, fixed := pkFixedWidth(pkType) + // n is off the blob; each record is >= minRec bytes (a fixed pk, or the 4-byte length + // prefix of a varlen pk), so a count that can't fit in the remaining buffer is corrupt. + // Reject it before make() / the loop so a bogus count can't drive a huge allocation. + minRec := 4 + if fixed { + minRec = width + } + if minRec > 0 && n > int64((len(body)-16)/minRec) { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 delete log: count exceeds buffer") + } + pos := 16 + out := make([]DeleteRecord, 0, n) + for i := int64(0); i < n; i++ { + l := width + if !fixed { + if pos+4 > len(body) { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 delete log: truncated pk length") + } + l = int(binary.LittleEndian.Uint32(body[pos:])) + pos += 4 + } + if l < 0 || pos+l > len(body) { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 delete log: truncated pk") + } + pk, err := decodePk(pkType, body[pos:pos+l]) + if err != nil { + return nil, err + } + pos += l + out = append(out, DeleteRecord{Pk: pk}) + } + return out, nil +} + +// foldDeleteFrame folds one decoded delete frame (all records share the frame's +// chunk_id) into the pk-key -> max-delete-chunk_id map that Index liveness +// consumes: a copy with Recency < the delete chunk_id is shadowed. Keyed by +// normalizeKey(pk) (matching resolve()); the max chunk_id wins, so re-delivered deletes +// only raise the bound. Mirrors bm25's FoldDeleteFrame (string-keyed here). +func foldDeleteFrame(m map[any]int64, recs []DeleteRecord, chunkId int64) map[any]int64 { + if len(recs) == 0 { + return m + } + if m == nil { + m = make(map[any]int64) + } + for _, r := range recs { + k := normalizeKey(r.Pk) + if cur, ok := m[k]; !ok || chunkId > cur { + m[k] = chunkId + } + } + return m +} diff --git a/pkg/fulltext2/frames.go b/pkg/fulltext2/frames.go new file mode 100644 index 0000000000000..0a5f4b1a23c5f --- /dev/null +++ b/pkg/fulltext2/frames.go @@ -0,0 +1,192 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "fmt" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + cuvscdc "github.com/matrixorigin/matrixone/pkg/vectorindex/cuvs" +) + +// CDC framing for the tag=1 tail. Fulltext v2 reuses cuVS's payload-agnostic +// FrameCdcChunk envelope (magic + version + op-counts + crc32) — exactly what +// bm25 does — carrying a serialized positional Segment as the insert payload. A +// frame larger than the storage data column is split across consecutive chunks +// at ascending chunk_ids; the load path reassembles them by each frame's +// self-describing header length. The chunk_id is the recency key, assigned by the +// storage layer, never stored inside the frame. + +// FrameSegment serializes seg and wraps it as one insert-segment frame. nInserts +// carries the doc count so a header-only scan can sum delta docs without decoding. +func FrameSegment(seg *Segment) ([]byte, error) { + blob, err := seg.Serialize() + if err != nil { + return nil, err + } + return cuvscdc.FrameCdcChunk(blob, nil, uint32(seg.N), 0, 0), nil +} + +// FrameDeletes encodes a delete batch as one delete-log frame (nDeletes carries +// the tombstone count so a header scan can distinguish it from an insert frame). +// Mirrors bm25's FrameDeletes. +func FrameDeletes(pkType int32, recs []DeleteRecord) ([]byte, error) { + blob, err := EncodeDeleteLog(pkType, recs) + if err != nil { + return nil, err + } + return cuvscdc.FrameCdcChunk(blob, nil, 0, uint32(len(recs)), 0), nil +} + +// UnframeSegment decodes an insert-segment frame back into a loaded Segment +// (id set to id). +func UnframeSegment(id string, framed []byte) (*Segment, error) { + records, _, nInserts, _, _, err := cuvscdc.UnframeCdcChunk(framed) + if err != nil { + return nil, err + } + if nInserts == 0 { + return nil, moerr.NewInternalErrorNoCtx("fulltext2: frame carries no insert segment") + } + return Deserialize(id, bytes.NewReader(records)) +} + +// UnframeTail decodes a tag=1 tail frame into EITHER an insert Segment (id set to +// id, exactly one non-nil) or a delete batch — the same per-frame dispatch +// LoadTailSegments does, exposed for callers/tests that already hold the framed +// bytes (e.g. TailBuilder's spilled files). +func UnframeTail(id string, framed []byte) (*Segment, []DeleteRecord, error) { + records, _, nInserts, nDeletes, _, err := cuvscdc.UnframeCdcChunk(framed) + if err != nil { + return nil, nil, err + } + switch { + case nInserts > 0: + seg, derr := Deserialize(id, bytes.NewReader(records)) + return seg, nil, derr + case nDeletes > 0: + recs, derr := DecodeDeleteLog(records) + return nil, recs, derr + } + return nil, nil, nil +} + +// TailChunk is one raw tag=1 storage row: a MaxChunkSize-bounded piece of a frame +// at its chunk_id. +type TailChunk struct { + ChunkId int64 + Data []byte +} + +// TailFrame is a reassembled complete frame plus the chunk_id of its first chunk +// (its recency key). +type TailFrame struct { + ChunkId int64 + Data []byte +} + +// splitFrameChunks splits a complete frame into MaxChunkSize-bounded chunks at +// consecutive chunk_ids from startChunkId. A frame <= MaxChunkSize yields a single +// chunk. +func splitFrameChunks(startChunkId int64, framed []byte) []TailChunk { + out := make([]TailChunk, 0, (len(framed)+vectorindex.MaxChunkSize-1)/vectorindex.MaxChunkSize) + cid := startChunkId + for off := 0; off < len(framed); off += vectorindex.MaxChunkSize { + end := off + vectorindex.MaxChunkSize + if end > len(framed) { + end = len(framed) + } + out = append(out, TailChunk{ChunkId: cid, Data: framed[off:end]}) + cid++ + } + return out +} + +// orderTailChunks returns the chunks ordered by chunk_id WITHOUT a comparison +// sort: it places each at index (chunk_id - min) in a preallocated slice (O(n)). +// tag=1 chunk_ids are a gapless run (the writer appends consecutive ids, and +// compaction deletes a whole low prefix — never a hole), so [min..max] must span +// exactly len(chunks) ids; a mismatch means a missing or duplicate chunk +// (corruption), reported rather than silently mis-assembled. +func orderTailChunks(chunks []TailChunk) ([]TailChunk, error) { + if len(chunks) == 0 { + return nil, nil + } + minC, maxC := chunks[0].ChunkId, chunks[0].ChunkId + for _, c := range chunks[1:] { + if c.ChunkId < minC { + minC = c.ChunkId + } + if c.ChunkId > maxC { + maxC = c.ChunkId + } + } + span := maxC - minC + 1 + if span != int64(len(chunks)) { + return nil, moerr.NewInternalErrorNoCtx(fmt.Sprintf( + "fulltext2 tail: chunk_id range [%d..%d] spans %d but got %d rows (gap or duplicate)", + minC, maxC, span, len(chunks))) + } + ordered := make([]TailChunk, span) + for _, c := range chunks { + ordered[c.ChunkId-minC] = c + } + return ordered, nil +} + +// reassembleFrames groups chunk_id-ordered rows back into complete frames using +// each frame's self-describing header length (CdcFrameLen): a frame occupies +// consecutive chunks whose bytes sum to that length, and its ordering key is the +// first chunk's chunk_id. Chunks MUST be pre-sorted by chunk_id ascending. +func reassembleFrames(chunks []TailChunk) ([]TailFrame, error) { + // Running count of bytes still available from the current chunk onward. A frame's bytes + // come from CONSECUTIVE chunks, so its self-declared length (CdcFrameLen, read from two + // untrusted uint32s in the first chunk) can never exceed this. Validate against it BEFORE + // make() so a corrupt chunk — start magic intact but garbage length fields declaring a + // near-4GiB frame — is rejected instead of OOM-killing the CN on the allocation. + // checkTailLoadBudget can't catch this: it measures the REAL stored bytes, not the length + // a corrupt header claims. + remaining := 0 + for _, c := range chunks { + remaining += len(c.Data) + } + var frames []TailFrame + i := 0 + for i < len(chunks) { + total, err := cuvscdc.CdcFrameLen(chunks[i].Data) + if err != nil { + return nil, err + } + if total <= 0 || total > remaining { + return nil, moerr.NewInternalErrorNoCtx(fmt.Sprintf( + "fulltext2 tail: frame at chunk %d declares %d bytes but only %d remain across %d chunks (corrupt frame header)", + chunks[i].ChunkId, total, remaining, len(chunks)-i)) + } + firstChunkId := chunks[i].ChunkId + buf := make([]byte, 0, total) + for len(buf) < total && i < len(chunks) { + remaining -= len(chunks[i].Data) + buf = append(buf, chunks[i].Data...) + i++ + } + if len(buf) < total { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 tail: truncated frame (missing chunk rows)") + } + frames = append(frames, TailFrame{ChunkId: firstChunkId, Data: buf[:total]}) + } + return frames, nil +} diff --git a/pkg/fulltext2/include_predicate.go b/pkg/fulltext2/include_predicate.go new file mode 100644 index 0000000000000..b3b7401b57f0b --- /dev/null +++ b/pkg/fulltext2/include_predicate.go @@ -0,0 +1,457 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "encoding/json" + "strconv" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/types" +) + +// includeFilterPred is the pushed-down predicate wire form — the SAME JSON shape the vector +// plugins use (pkg/sql/plan filterJSONPred): identical field names (col/op/val/lo/hi/vals), +// the same symbolic op strings (=,!=,<,<=,>,>=,between,in,is_null,is_not_null), the same +// bare-array serialization, and the same pk sentinel col (-1). One shape means a single +// planner predicate-peeler can target both engines. fulltext2 extends it in two ways its Go +// evaluator supports (and the numeric C++ FilterStore cannot): Val/Vals may be JSON STRINGS +// (varchar INCLUDE/pk columns store the actual value), and a "prefix" op (LIKE 'x%'). The +// predicates form a conjunction (WHERE a AND b): a row is admitted iff ALL pass. +type includeFilterPred struct { + Col int `json:"col"` + Op string `json:"op"` + Val any `json:"val,omitempty"` // scalar comparison (=,!=,<,<=,>,>=,prefix) + Lo any `json:"lo,omitempty"` // between + Hi any `json:"hi,omitempty"` // between + Vals []any `json:"vals,omitempty"` // in +} + +// IncludePredPkCol is the "col" sentinel for a predicate on the PRIMARY KEY (doc_id) — +// identical to the vector plugins' pkHostIdSentinelCol (-1). The pk is stored per-doc +// (seg.pk) and typed by the index's PkType, so a pk predicate is evaluated inline exactly +// like an INCLUDE column: an EXACT, JOIN-free alternative to the docfilter second-scan +// pushdown for the comparable pk types (integer family + varchar/char). +const IncludePredPkCol = -1 + +type includeCmpKind uint8 + +const ( + incEq includeCmpKind = iota + incNe + incLt + incLe + incGt + incGe + incBetween + incIn + incPrefix + incIsNull + incIsNotNull +) + +// includeOpKinds maps the wire op strings (matching the vector plugins' symbolic ops, plus +// fulltext2's "prefix") to compiled kinds. +var includeOpKinds = map[string]includeCmpKind{ + "=": incEq, "!=": incNe, "<": incLt, "<=": incLe, ">": incGt, ">=": incGe, + "between": incBetween, "in": incIn, + "is_null": incIsNull, "is_not_null": incIsNotNull, + "prefix": incPrefix, +} + +// compiledIncludePred is a parsed, type-bound predicate: operands are decoded to the +// column's comparable form (int64 for the integer family, []byte for varchar/char) once at +// compile, so per-candidate evaluation is allocation-free. It is segment-independent (the +// INCLUDE column types are the same across an index's segments); includePredMembership binds +// it to a segment's includeVal. +type compiledIncludePred struct { + col int + kind includeCmpKind + isStr bool + isUnsigned bool // uint64/bit column: compare as uint64 (int64 wraps values above MaxInt64) + ints []int64 // signed integer-column operands (all int types + uint8/16/32, which fit int64) + uints []uint64 // unsigned-64 column operands + strs [][]byte // varchar/char-column operands (also the prefix) +} + +// compileIncludePredicates parses the JSON spec and type-binds each predicate against the +// index's INCLUDE column types (from a loaded segment) — or against pkType for a predicate +// on the primary key (Col == IncludePredPkCol). Returns nil for an empty spec. +func compileIncludePredicates(specJSON []byte, includeTypes []int32, pkType int32) ([]compiledIncludePred, error) { + if len(specJSON) == 0 { + return nil, nil + } + // UseNumber so a JSON integer decodes to json.Number (exact int64), not float64 (which + // loses precision past 2^53 — real for large pks). + dec := json.NewDecoder(bytes.NewReader(specJSON)) + dec.UseNumber() + var preds []includeFilterPred + if err := dec.Decode(&preds); err != nil { + return nil, err + } + out := make([]compiledIncludePred, 0, len(preds)) + for _, p := range preds { + kind, ok := includeOpKinds[p.Op] + if !ok { + return nil, moerr.NewInternalErrorNoCtxf("fulltext2 include predicate: unknown op %q", p.Op) + } + // Resolve the operand type: the pk (Col == IncludePredPkCol) uses pkType; an INCLUDE + // column uses includeTypes[Col]. + var colType int32 + if p.Col == IncludePredPkCol { + colType = pkType + } else if p.Col >= 0 && p.Col < len(includeTypes) { + colType = includeTypes[p.Col] + } else { + return nil, moerr.NewInternalErrorNoCtxf("fulltext2 include predicate: col %d out of range", p.Col) + } + if !isComparableIncludeType(colType) { + return nil, moerr.NewInternalErrorNoCtxf("fulltext2 include predicate: col %d type not comparable", p.Col) + } + // uint64/bit columns store values that overflow int64 (> MaxInt64), so they need an + // unsigned comparison path — int64 casting would wrap a large value to negative and + // silently exclude a matching row. All other integer types (int8..64, uint8..32) fit + // in int64. isStr takes precedence (varchar/char). + unsigned := colType == int32(types.T_uint64) || colType == int32(types.T_bit) + cp := compiledIncludePred{col: p.Col, kind: kind, isStr: isVarlenaIncludeType(colType), isUnsigned: unsigned} + // Gather the op's operands from the shape-appropriate field(s). + var operands []any + switch kind { + case incIsNull, incIsNotNull: + // no operands + case incBetween: + operands = []any{p.Lo, p.Hi} + case incIn: + operands = p.Vals + default: + operands = []any{p.Val} + } + for _, o := range operands { + switch { + case cp.isStr: + s, ok := includeOperandString(o) + if !ok { + return nil, moerr.NewInternalErrorNoCtxf("fulltext2 include predicate: col %d expects a string operand", p.Col) + } + cp.strs = append(cp.strs, []byte(s)) + case cp.isUnsigned: + u, ok := includeOperandUint64(o) + if !ok { + return nil, moerr.NewInternalErrorNoCtxf("fulltext2 include predicate: col %d expects an unsigned-integer operand", p.Col) + } + cp.uints = append(cp.uints, u) + default: + n, ok := includeOperandInt64(o) + if !ok { + return nil, moerr.NewInternalErrorNoCtxf("fulltext2 include predicate: col %d expects an integer operand", p.Col) + } + cp.ints = append(cp.ints, n) + } + } + out = append(out, cp) + } + return out, nil +} + +// includeOperandString extracts a string operand from a decoded JSON value. +func includeOperandString(v any) (string, bool) { + s, ok := v.(string) + return s, ok +} + +// includeOperandInt64 extracts an int64 operand from a decoded JSON value (json.Number with +// UseNumber, or float64/string defensively). +func includeOperandInt64(v any) (int64, bool) { + switch x := v.(type) { + case json.Number: + if n, err := x.Int64(); err == nil { + return n, true + } + if f, err := x.Float64(); err == nil { // e.g. "1.0" + return int64(f), true + } + return 0, false + case float64: + return int64(x), true + case string: + n, err := strconv.ParseInt(x, 10, 64) + return n, err == nil + default: + return 0, false + } +} + +// isVarlenaIncludeType reports whether an INCLUDE column type is compared as bytes (varchar/ +// char) rather than as an integer. +func isVarlenaIncludeType(t int32) bool { + switch types.T(t) { + case types.T_varchar, types.T_char: + return true + default: + return false + } +} + +// includeOperandUint64 extracts a uint64 operand from a decoded JSON value, for uint64/bit +// columns whose values can exceed MaxInt64 (where includeOperandInt64 would fail or saturate). +func includeOperandUint64(v any) (uint64, bool) { + switch x := v.(type) { + case json.Number: + if u, err := strconv.ParseUint(x.String(), 10, 64); err == nil { + return u, true + } + if f, err := x.Float64(); err == nil && f >= 0 { // e.g. "1.0" + return uint64(f), true + } + return 0, false + case float64: + if x < 0 { + return 0, false + } + return uint64(x), true + case uint64: + return x, true + case int64: + if x < 0 { + return 0, false + } + return uint64(x), true + case string: + u, err := strconv.ParseUint(x, 10, 64) + return u, err == nil + default: + return 0, false + } +} + +// includeUint64 coerces a stored unsigned INCLUDE/pk value to uint64 (decodePk yields uint64 +// for T_uint64/T_bit; the smaller unsigned/signed types are widened losslessly). +func includeUint64(v any) (uint64, bool) { + switch x := v.(type) { + case uint64: + return x, true + case uint32: + return uint64(x), true + case uint16: + return uint64(x), true + case uint8: + return uint64(x), true + case int64: + if x < 0 { + return 0, false + } + return uint64(x), true + case int32: + if x < 0 { + return 0, false + } + return uint64(x), true + default: + return 0, false + } +} + +// isComparableIncludeType reports whether the inline evaluator can compare a value of this +// type — the integer family (normalized to int64) or varchar/char (byte-compare). A pk of an +// unsupported type (uuid/decimal/...) must fall back to the docfilter pushdown, so the +// planner never emits an inline pk predicate on it. +func isComparableIncludeType(t int32) bool { + if isVarlenaIncludeType(t) { + return true + } + switch types.T(t) { + case types.T_int8, types.T_int16, types.T_int32, types.T_int64, + types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, + types.T_bit: // BIT is uint64-backed; compiledIncludePred handles it on the unsigned path + return true + default: + return false + } +} + +// test evaluates the predicate against one candidate's stored INCLUDE value with SQL +// 3-valued logic: a value comparison against NULL is UNKNOWN → the row is NOT admitted; +// only isnull/isnotnull inspect the NULL flag directly. +func (p *compiledIncludePred) test(v any, isNull bool) bool { + switch p.kind { + case incIsNull: + return isNull + case incIsNotNull: + return !isNull + } + if isNull { + return false // NULL vs value ⇒ UNKNOWN ⇒ excluded + } + if p.isStr { + b := includeBytes(v) + switch p.kind { + case incEq: + return bytes.Equal(b, p.strs[0]) + case incNe: + return !bytes.Equal(b, p.strs[0]) + case incLt: + return bytes.Compare(b, p.strs[0]) < 0 + case incLe: + return bytes.Compare(b, p.strs[0]) <= 0 + case incGt: + return bytes.Compare(b, p.strs[0]) > 0 + case incGe: + return bytes.Compare(b, p.strs[0]) >= 0 + case incBetween: + return bytes.Compare(b, p.strs[0]) >= 0 && bytes.Compare(b, p.strs[1]) <= 0 + case incPrefix: + return bytes.HasPrefix(b, p.strs[0]) + case incIn: + for _, s := range p.strs { + if bytes.Equal(b, s) { + return true + } + } + return false + } + return false + } + if p.isUnsigned { + u, ok := includeUint64(v) + if !ok { + return false + } + switch p.kind { + case incEq: + return u == p.uints[0] + case incNe: + return u != p.uints[0] + case incLt: + return u < p.uints[0] + case incLe: + return u <= p.uints[0] + case incGt: + return u > p.uints[0] + case incGe: + return u >= p.uints[0] + case incBetween: + return u >= p.uints[0] && u <= p.uints[1] + case incIn: + for _, x := range p.uints { + if u == x { + return true + } + } + return false + case incPrefix: + return false // prefix is string-only + } + return false + } + n, ok := includeInt64(v) + if !ok { + return false + } + switch p.kind { + case incEq: + return n == p.ints[0] + case incNe: + return n != p.ints[0] + case incLt: + return n < p.ints[0] + case incLe: + return n <= p.ints[0] + case incGt: + return n > p.ints[0] + case incGe: + return n >= p.ints[0] + case incBetween: + return n >= p.ints[0] && n <= p.ints[1] + case incIn: + for _, x := range p.ints { + if n == x { + return true + } + } + return false + case incPrefix: + return false // prefix is a string-only op; the planner never emits it on an int column + } + return false +} + +// includeBytes coerces a stored varchar/char INCLUDE value to bytes (decodePk yields []byte). +func includeBytes(v any) []byte { + switch x := v.(type) { + case []byte: + return x + case string: + return []byte(x) + default: + return nil + } +} + +// includeInt64 normalizes a stored integer INCLUDE value (any int/uint width, as decodePk +// returns it) to int64 for comparison. A uint64 above math.MaxInt64 wraps — a documented +// v1 limitation; such huge unsigned filter values are not expected. +func includeInt64(v any) (int64, bool) { + switch x := v.(type) { + case int64: + return x, true + case int32: + return int64(x), true + case int16: + return int64(x), true + case int8: + return int64(x), true + case uint64: + return int64(x), true + case uint32: + return int64(x), true + case uint16: + return int64(x), true + case uint8: + return int64(x), true + default: + return 0, false + } +} + +// includePredMembership admits an ord iff every compiled INCLUDE predicate passes against +// its stored value in seg — the in-index prefilter, ANDed into the search's allow-set +// (mkAllow) alongside the docfilter/liveness memberships. +type includePredMembership struct { + seg *Segment + preds []compiledIncludePred +} + +func (m *includePredMembership) Contains(ord int64) bool { + for i := range m.preds { + p := &m.preds[i] + var v any + var isNull bool + if p.col == IncludePredPkCol { + // The primary key is stored per-doc and is never NULL. + v = m.seg.pk(ord) + } else { + var err error + v, isNull, err = m.seg.includeVal(ord, p.col) + if err != nil { + return false // corrupt value ⇒ exclude (safe: a covering fallback JOIN re-checks) + } + } + if !p.test(v, isNull) { + return false + } + } + return true +} diff --git a/pkg/fulltext2/index.go b/pkg/fulltext2/index.go new file mode 100644 index 0000000000000..7cd6d79192650 --- /dev/null +++ b/pkg/fulltext2/index.go @@ -0,0 +1,673 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "fmt" + "iter" + "sort" + "strconv" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" + "github.com/matrixorigin/matrixone/pkg/vectorindex" +) + +// Index is a queryable fulltext v2 index made of several loaded segments — a +// tag=0 base plus tag=1 CDC tail deltas (§4) — with per-pk liveness and a delete +// set. It resolves the two things a lone Segment cannot: +// +// - GLOBAL stats: N and avgDocLen span all segments (a term's idf and BM25's +// length norm must be global, not per-segment), computed once at construction. +// - Liveness: the same pk may land in several segments (UPDATE / reinsert / a +// stale base copy); only the highest-Recency copy is live, and a delete at +// recency d kills copies with recency < d. Dead copies never appear in +// results and never contribute to a phrase's document frequency. +type Index struct { + segments []*Segment + deletes map[any]int64 // normalizeKey(pk) -> recency at/after which older copies are dead + + globalN int64 + globalAvgDocLen float64 + // liveOrd[si] is a per-segment ord-indexed liveness bitmap (built once in resolve), + // so every liveness check — isLive (phrase), livenessMembership (boolean/stream), and + // ReconstructLiveDocs (compact) — is an O(1) bitmap index, allocation-free, with no + // per-candidate keyOf + map lookup. A fully-live segment keeps liveOrd[si]==nil ("all + // live" fast path), so an append-only index costs ZERO resident liveness heap. It is + // the SOLE resident liveness structure: the pk->loc map that resolve() builds to + // derive it is local and discarded (it was O(live docs) of Go heap — the dominant + // load-time floor). Mirrors bm25's ComputeLiveness []Membership. + liveOrd [][]bool +} + +type docLoc struct { + si int + ord int64 +} + +// NewIndex builds an index over the given loaded segments (each carrying its +// Recency) and delete set, computing global stats and the liveness map. deletes +// may be nil. +func NewIndex(segments []*Segment, deletes map[any]int64) *Index { + idx := &Index{segments: segments, deletes: deletes} + idx.resolve() + return idx +} + +// resolve computes the live copy of each pk (highest Recency, not delete-shadowed), +// then the global doc count + average document length; each segment's AvgDocLen is +// set to the global value (§4) so BM25 length-normalizes consistently. +func (idx *Index) resolve() { + type best struct { + rec int64 + loc docLoc + } + top := make(map[any]best) + for si, seg := range idx.segments { + nd := seg.numDocs() + for ord := 0; ord < nd; ord++ { + key := normalizeKey(seg.pk(int64(ord))) + cand := best{seg.Recency, docLoc{si, int64(ord)}} + if cur, ok := top[key]; !ok || cand.rec > cur.rec { + top[key] = cand + } + } + } + + // liveLoc (pk key -> live loc) is LOCAL: it drives the liveOrd bitmap + global stats + // below and is then discarded, so it never becomes part of the resident cache + // footprint (it was O(live docs) of Go-heap map — the biggest load-time allocation). + liveLoc := make(map[any]docLoc, len(top)) + var sumDocLen int64 + for key, b := range top { + if d, ok := idx.deletes[key]; ok && b.rec < d { + continue // shadowed by a later delete + } + liveLoc[key] = b.loc + sumDocLen += int64(idx.segments[b.loc.si].docLen[b.loc.ord]) + } + idx.globalN = int64(len(liveLoc)) + if idx.globalN > 0 { + idx.globalAvgDocLen = float64(sumDocLen) / float64(idx.globalN) + } + for _, seg := range idx.segments { + seg.AvgDocLen = idx.globalAvgDocLen + } + + // Per-segment liveness bitmap (mirrors bm25's ComputeLiveness): a fully-live + // segment keeps liveOrd[si]==nil (the common single-base / append-only case) so it + // costs no O(doc-count) allocation; otherwise mark each live ord true. Every query + // and compact liveness check then indexes this O(1) instead of keyOf+map per candidate. + idx.liveOrd = make([][]bool, len(idx.segments)) + liveCount := make([]int, len(idx.segments)) + for _, loc := range liveLoc { + liveCount[loc.si]++ + } + for si, seg := range idx.segments { + if liveCount[si] < seg.numDocs() { // some dead ord in this segment → need the bitmap + idx.liveOrd[si] = make([]bool, seg.numDocs()) + } + } + for _, loc := range liveLoc { + if b := idx.liveOrd[loc.si]; b != nil { + b[loc.ord] = true + } + } +} + +// includeTypes returns the index's INCLUDE column types (types.T per column), read from the +// first segment that carries them — all segments of an index share one INCLUDE schema. +// nil when the index has no INCLUDE columns. +func (idx *Index) includeTypes() []int32 { + for _, s := range idx.segments { + if len(s.includeTypes) > 0 { + return s.includeTypes + } + } + return nil +} + +// pkType returns the index's primary-key type (types.T), used to type-bind an inline pk +// predicate. All segments share the pk type; 0 when the index has no segments. +func (idx *Index) pkType() int32 { + if len(idx.segments) > 0 { + return idx.segments[0].PkType + } + return 0 +} + +// isLive reports whether (si, ord) is the live copy of its pk, via the per-segment +// liveness bitmap (nil => the segment is fully live). Same semantics as +// livenessMembership; no pk key or map lookup needed. +func (idx *Index) isLive(si int, ord int64) bool { + b := idx.liveOrd[si] + return b == nil || b[ord] +} + +// SearchPhrase runs an NL exact-phrase query across all segments and returns the +// global top-k (score desc; equal scores are order-unspecified). A single term is the +// degenerate one-term phrase. Scoring uses global N + global avgDocLen, and the +// phrase's document frequency is the number of LIVE documents that match (dead +// and delete-shadowed copies are excluded), so idf is exact. +func (idx *Index) SearchPhrase(slots []phraseSlot, algo ScoreAlgo, k int, filter *prefilter) []Result { + if k <= 0 || idx.globalN == 0 || len(slots) == 0 { + return nil + } + // A phrase can match at most globalN live docs, so cap k to it: a huge pushed LIMIT + // (clamped to MaxInt32 upstream) must never size the top-k beyond the live corpus. + if int64(k) > idx.globalN { + k = int(idx.globalN) + } + // Single pass, O(min(k, #matches)) memory (was O(matches): a matched map + a full results slice, + // which OOMs on a low-selectivity phrase that hits most of the corpus). A lone + // phrase has ONE idf² (idfSquared over its live df), and scoreTerm multiplies the + // per-doc factor by that constant, so idf² does not change the top-k order — rank on + // the idf²-free partial score in a bounded top-k, count the (filter-independent) live + // df alongside, and scale the k winners by idf² at the end. isLive is true for exactly + // one (si,ord) per pk, so counting live hits IS the distinct-live df (no dedup set). + tk := newBoundedTopK(k) + df := 0 + for si, seg := range idx.segments { + allow := mkAllow(seg, filter) // WHERE prefilter over this segment's ords (nil = none) + for _, h := range seg.matchPhrase(slots) { + if !idx.isLive(si, h.ord) { + continue + } + df++ // distinct live phrase match (filter-independent, matches the old dfSet) + if allowed(allow, h.ord) { + partial := seg.scoreTerm(algo, float64(h.tf), 1.0, h.ord, idx.globalAvgDocLen) + tk.push(seg.pk(h.ord), partial, seg.decodeInclude(h.ord)) + } + } + } + if tk.len() == 0 { + return nil + } + idf2 := idfSquared(idx.globalN, df) // df = live phrase-matched docs, filter-independent + return tk.resultsDescScaled(float32(idf2)) +} + +// boundedTopK keeps only the highest-scoring k (pk, score) pairs seen via push in O(k) +// memory — a binary min-heap by score, so the current k-th best sits at the root and is +// evicted in O(log k) when a better candidate arrives. It replaces "materialize every +// match, then top-k", which was O(matches). Ties at the k-th place are unspecified (as +// before — no arbitrary pk tie-break; see topKResults). +// +// It is deliberately keyed by the resolved `pk any` (NOT the segment `ord`), unlike the +// ord-keyed FastMaxHeap in wand.go: SearchPhrase accumulates across ALL segments into +// ONE heap, and each segment has its own ord space (ord 5 in seg0 != ord 5 in seg1), so +// only the globally-unique pk is a safe key here. It also defers the single idf² scale to +// resultsDescScaled. Those two properties are why it is not the FastMaxHeap used by the +// per-segment WAND paths — do not "unify" them; keying this by ord reintroduces cross- +// segment ord collisions. +type topKItem struct { + pk any + score float32 + include []any // INCLUDE values carried for a covering query (nil when no INCLUDE columns) +} + +type boundedTopK struct { + k int + h []topKItem +} + +// newBoundedTopK does NOT pre-allocate a cap-k backing array: push grows h lazily and +// never past k items, so the heap is bounded by min(k, #matches) — an absurd pushed +// LIMIT (e.g. 5e8) on a query matching a handful of docs costs a handful of slots, not +// a k-sized (multi-GB) allocation. +func newBoundedTopK(k int) *boundedTopK { return &boundedTopK{k: k} } + +func (b *boundedTopK) len() int { return len(b.h) } + +func (b *boundedTopK) push(pk any, score float32, include []any) { + if b.k <= 0 { + return + } + if len(b.h) < b.k { + b.h = append(b.h, topKItem{pk, score, include}) + for i := len(b.h) - 1; i > 0; { + p := (i - 1) / 2 + if b.h[p].score <= b.h[i].score { + break + } + b.h[p], b.h[i] = b.h[i], b.h[p] + i = p + } + return + } + if score <= b.h[0].score { + return // not better than the current k-th best + } + b.h[0] = topKItem{pk, score, include} + n := len(b.h) + for i := 0; ; { + l, r, s := 2*i+1, 2*i+2, i + if l < n && b.h[l].score < b.h[s].score { + s = l + } + if r < n && b.h[r].score < b.h[s].score { + s = r + } + if s == i { + break + } + b.h[s], b.h[i] = b.h[i], b.h[s] + i = s + } +} + +// resultsDescScaled returns the retained items as Results in score-descending order, +// each partial score multiplied by scale (the idf² deferred out of the ranking). +func (b *boundedTopK) resultsDescScaled(scale float32) []Result { + out := make([]Result, len(b.h)) + for i := range b.h { + out[i] = Result{Pk: b.h[i].pk, Score: b.h[i].score * scale, Include: b.h[i].include} + } + sort.Slice(out, func(i, j int) bool { return out[i].Score > out[j].Score }) + return out +} + +// topKResults returns the top-k results ordered by score descending. Equal scores are +// equally relevant, so their relative order is unspecified (SQL LIMIT over a tied ORDER +// BY key is itself unspecified) — we do NOT impose an arbitrary pk tie-break, which would +// force a sortKey string alloc for every matched doc (the alloc storm that dominated the +// CPU/GC profile of a query matching far more than k docs, e.g. a common single-term NL +// phrase or a boolean union). +// +// Selection is a bounded SoA FastMaxHeap keyed by result index with distance = -score: +// keeping the k smallest distances yields the k highest scores in O(n) with ZERO +// per-candidate allocation. Pop returns largest-distance (lowest-score) first, so +// filling the output back-to-front lands it in score-descending order. +func topKResults(results []Result, k int) []Result { + if k <= 0 || len(results) == 0 { + return nil + } + if k > len(results) { + k = len(results) + } + keysBuf := make([]int64, k) + distsBuf := make([]float32, k) + h := vectorindex.NewFastMaxHeap[float32, int64](k, keysBuf, distsBuf) + for i := range results { + h.Push(int64(i), -results[i].Score) + } + out := make([]Result, k) + for i := k - 1; i >= 0; i-- { + idx, _, ok := h.Pop() + if !ok { // fewer than k live entries (e.g. duplicate pushes were bounded out) + return out[i+1:] + } + out[i] = results[idx] + } + return out +} + +// SearchText tokenizes query with tok and runs an NL exact-phrase search across +// the index. +func (idx *Index) SearchText(query []byte, tok tokenizer.Tokenizer, algo ScoreAlgo, k int, filter *prefilter) ([]Result, error) { + return idx.SearchPhrase(tokenizePhraseSlots(tok, query), algo, k, filter), nil +} + +// globalStats carries the corpus-global scoring inputs (N, average doc length, and +// per-term document frequency) so a boolean query scores every segment on the SAME +// scale. Without it each segment used its LOCAL N/df, so appending a CDC tail shifted +// a doc's score and could drop a globally-top-k doc at the merge. df is summed across +// segments (like bm25's gdf) and memoized per query; it is built fresh per query and +// used sequentially across segments, so the cache needs no lock. +type globalStats struct { + n int64 + avgDocLen float64 + idx *Index + dfCache map[string]int + // phraseDfCache memoizes only the corpus-global phrase COUNT per phrase (an int), + // never the per-segment hit slices — see phraseDf. Retaining the slices would defeat + // the no-LIMIT streaming O(one segment) bound: a low-selectivity phrase over many + // capacity-sized segments would hold O(global matching corpus) before the first + // segment could finish emitting. + phraseDfCache map[string]int +} + +func (idx *Index) newGlobalStats() *globalStats { + return &globalStats{n: idx.globalN, avgDocLen: idx.globalAvgDocLen, idx: idx, + dfCache: make(map[string]int), phraseDfCache: make(map[string]int)} +} + +// phraseHits returns seg's matchPhrase hits WITHOUT retaining them across segments: the +// caller (evalClause) uses the slice transiently to score one segment and drops it, so +// only O(one segment) of phrase hits is live at a time. phraseDf recomputes-and-discards +// its own cross-segment scan for the same reason; the two intentionally do NOT share a +// cache (that sharing is what broke the streaming memory bound). The doubled matchPhrase +// work is the accepted cost of bounded memory on the no-LIMIT streaming path. +func (gs *globalStats) phraseHits(seg *Segment, slots []phraseSlot) []docTf { + return seg.matchPhrase(slots) +} + +// df returns term's corpus-global LIVE document frequency: only ords not superseded by a +// newer copy (recency) or a tombstone are counted. df feeds idf against the LIVE globalN, so +// counting dead PHYSICAL copies (an UPDATE leaves the old copy in a lower-recency segment +// until MERGE reclaims it) would inflate df past N and make a dirty base+tail rank a query +// DIFFERENTLY from the identical corpus after compaction. Enumerating the term's ords and +// testing isLive costs O(df) per query term, memoized so it is paid once — and WAND walks the +// same posting list anyway. +func (gs *globalStats) df(term string) int { + if d, ok := gs.dfCache[term]; ok { + return d + } + d := 0 + for si, seg := range gs.idx.segments { + if pl, ok := seg.lookup(term); ok { + for _, ord := range pl.materializeDocIDs() { + if gs.idx.isLive(si, ord) { + d++ + } + } + } + } + gs.dfCache[term] = d + return d +} + +// idfFor is the term's idf² under the global corpus stats, or the segment-local stats +// when gs is nil (a direct Segment query / single-segment test, where local == global). +func (gs *globalStats) idfFor(seg *Segment, term string, pl *termPostings) float64 { + if gs == nil { + return idfSquared(seg.N, pl.df()) + } + return idfSquared(gs.n, gs.df(term)) +} + +// phraseDf returns the corpus-global LIVE document frequency of a phrase (the number of LIVE +// docs matching the contiguous phrase, summed over segments), memoizing only the COUNT. Each +// segment's matchPhrase slice is counted and immediately discarded (count-and-discard), so the +// df scan holds at most one segment's hits at a time — never the whole corpus. Only live ords +// are counted (mirrors the live term df above and streamPhrase's pass 1), so a dirty tail does +// not inflate the phrase idf relative to the compacted corpus. +func (gs *globalStats) phraseDf(slots []phraseSlot) int { + key := slotsKey(slots) + if d, ok := gs.phraseDfCache[key]; ok { + return d + } + d := 0 + for si, seg := range gs.idx.segments { + for _, h := range seg.matchPhrase(slots) { // counted, then the slice is released this iteration + if gs.idx.isLive(si, h.ord) { + d++ + } + } + } + gs.phraseDfCache[key] = d + return d +} + +// phraseIdfFor is a phrase clause's idf² under the global corpus stats (global N + +// cross-segment phrase df), or the segment-local stats when gs is nil — so a phrase +// clause ranks consistently across a base + CDC tail, like the term/prefix clauses. +func (gs *globalStats) phraseIdfFor(seg *Segment, slots []phraseSlot, localHits int) float64 { + if gs == nil { + return idfSquared(seg.N, localHits) + } + return idfSquared(gs.n, gs.phraseDf(slots)) +} + +// avgdl is the global average doc length, or the segment's own when gs is nil. +func (gs *globalStats) avgdl(seg *Segment) float64 { + if gs == nil { + return seg.avgDocLenOrMean() + } + return gs.avgDocLen +} + +// SearchBoolean runs a parsed boolean-mode query across the index and returns the +// global top-k (score desc; equal scores are order-unspecified). Each segment is scored on the +// GLOBAL corpus stats (globalStats), and its walk admits only the LIVE copy of a pk +// (liveness ANDed into the WHERE prefilter), so a segment's top-k is k live docs on +// the global scale — the merge below is then the exact global top-k even across a +// base + CDC tail. All clause types (term, prefix, phrase, and the disjunctive WAND +// path) score on global stats — a phrase clause uses the cross-segment phrase df. +func (idx *Index) SearchBoolean(q BoolQuery, algo ScoreAlgo, k int, filter *prefilter) ([]Result, error) { + if k <= 0 || idx.globalN == 0 { + return nil, nil + } + gs := idx.newGlobalStats() + matched := make(map[any]Result) + for si, seg := range idx.segments { + allow := andAllow(mkAllow(seg, filter), &livenessMembership{idx: idx, si: si}) + res, err := seg.SearchBoolean(q, algo, k, allow, gs) + if err != nil { + return nil, err + } + for _, r := range res { + matched[normalizeKey(r.Pk)] = r // liveness inside the walk → one live copy per pk + } + } + if len(matched) == 0 { + return nil, nil + } + results := make([]Result, 0, len(matched)) + for _, r := range matched { + results = append(results, r) + } + return topKResults(results, k), nil +} + +// streamBoolean emits every LIVE, WHERE-admitted boolean match (pk, score) through sink +// WITHOUT a global top-K heap or an O(corpus) matched map — the streaming analogue of +// SearchBoolean for the no-LIMIT MUST / MUST-NOT / mixed-phrase path. Liveness is ANDed into +// each segment's prefilter exactly as SearchBoolean does, so a pk that survives in exactly +// one live segment is emitted once. Each segment's evalBoolean is O(segment N) (bounded by +// the segment capacity) and freed before the next, so peak Go heap is O(one segment), NOT +// O(globalN) — this is what makes the streamed boolean shape genuinely bounded rather than +// the old SearchQuery(globalN) fallback that materialized the whole live corpus. Ranking is +// the upstream ORDER BY score node's job (same contract as the other streaming paths). +func (idx *Index) streamBoolean(q BoolQuery, algo ScoreAlgo, filter *prefilter, sink *streamSink) error { + if idx.globalN == 0 { + return nil + } + gs := idx.newGlobalStats() + for si, seg := range idx.segments { + allow := andAllow(mkAllow(seg, filter), &livenessMembership{idx: idx, si: si}) + err := seg.evalBoolean(q, algo, allow, gs, func(ord int, score float32) { + sink.pushPk(seg, int64(ord), score) // pushPk no-ops once the sink has stopped + }) + if err != nil { + return err + } + // Bail before the NEXT segment on an emit error (the expensive per-segment work is + // bounded to one segment). Unlike streamWAND's `for !sink.stopped`, evalBoolean is + // NOT short-circuited mid-segment on abort: a boolean query must fully score every + // clause into the dense array before it can admit any candidate, so that work is + // already done by the time an emit can fail — the only remaining cost is the tail of + // the candidate forEach running as no-op pushes (a cheap bitset walk, no scoring). + // Threading a stop signal through the sink-agnostic evalBoolean to save that tail + // isn't worth complicating the helper it shares with searchBooleanFull. + if sink.err != nil { + return sink.err + } + } + sink.flush() + return sink.err +} + +// SearchBooleanText tokenizes+parses query in boolean mode with tok, then +// evaluates it across the index — the convenience entry mirroring +// MATCH(col) AGAINST('query' IN BOOLEAN MODE) with a fixed tokenizer. +func (idx *Index) SearchBooleanText(query []byte, tok tokenizer.Tokenizer, algo ScoreAlgo, k int) ([]Result, error) { + q, err := ParseBoolean(query, tok) + if err != nil { + return nil, err + } + return idx.SearchBoolean(q, algo, k, nil) // convenience entry: no WHERE prefilter +} + +// ReconstructLiveDocs yields each LIVE document as (pk, ordered terms) from the +// positional postings across all loaded segments — the input to a MERGE compaction +// that folds base + tail into a fresh dead-doc-free base WITHOUT re-tokenizing the +// source. Per-doc term order is recovered from token positions. +// +// Reconstruction is done ONE SEGMENT AT A TIME: a live doc's postings live entirely +// within its own (live) segment, so `buckets` only ever holds the CURRENT segment's +// live docs and is freed before the next — peak Go heap is O(one segment's postings) +// (≤ max_index_capacity), NOT O(the whole live corpus). This matters because MERGE +// (CompactSegments) is deliberately exempt from checkBaseLoadBudget, so an all-at-once +// reconstruction would OOM-kill the CN on exactly the operation meant to reclaim it. +// Docs are yielded in load-order across segments, pk-sorted WITHIN each segment (so the +// output is still deterministic; the fresh base's per-segment doc grouping differs but +// its contents and the resulting query answers do not). On a posting-decode error the +// iterator yields once with a non-nil error and stops. +func (idx *Index) ReconstructLiveDocs(positionFree bool) iter.Seq2[TokenizedDoc, error] { + return func(yield func(TokenizedDoc, error) bool) { + type posTerm struct { + pos int32 + term string + } + for si, seg := range idx.segments { + live := idx.liveOrd[si] + nd := seg.numDocs() + // Bucket ONLY this segment's live ords (keyed by ord, unique within the segment). + buckets := make(map[int64][]posTerm) + for ord := 0; ord < nd; ord++ { + if live == nil || live[ord] { + buckets[int64(ord)] = nil + } + } + if len(buckets) == 0 { + continue + } + err := seg.forEachPosting(func(term string, tp *termPostings) { + docs := tp.materializeDocIDs() // docIDs (block-compressed on load) + if positionFree { + // A position-free segment has no positions: reconstruct each doc's terms + // from tf — emit the term tf times with a synthetic ascending position. + // Order is irrelevant (the position-free rebuild drops positions again), + // but tf is preserved so BM25 ranking is unchanged. + tfs := tp.materializeTfs() + for i, ord := range docs { + if _, ok := buckets[ord]; !ok { + continue + } + for t := int32(0); t < int32(tfs[i]); t++ { + buckets[ord] = append(buckets[ord], posTerm{t, term}) + } + } + return + } + mat := tp.materializePositions() // decode this term's positions once + for i, ord := range docs { + if _, ok := buckets[ord]; !ok { + continue + } + for _, pos := range mat[i] { + buckets[ord] = append(buckets[ord], posTerm{pos, term}) + } + } + }) + if err != nil { + yield(TokenizedDoc{}, err) + return + } + + // Yield this segment's live docs pk-sorted (sortKey computed once each). + type keyed struct { + sk string + ord int64 + } + keys := make([]keyed, 0, len(buckets)) + for ord := range buckets { + keys = append(keys, keyed{sortKey(normalizeKey(seg.pk(ord))), ord}) + } + sort.Slice(keys, func(i, j int) bool { return keys[i].sk < keys[j].sk }) + for _, kd := range keys { + pl := buckets[kd.ord] + sort.SliceStable(pl, func(i, j int) bool { return pl[i].pos < pl[j].pos }) + terms := make([]string, len(pl)) + positions := make([]int32, len(pl)) + for i, pt := range pl { + terms[i] = pt.term + positions[i] = pt.pos // byte position, carried through MERGE + } + buckets[kd.ord] = nil // copied out — let GC reclaim it + // Carry the source doc's INCLUDE values through MERGE so the rebuilt base + // keeps them (a compaction that dropped them would break covering/prefilter). + var include []any + if nIncl := seg.nIncludeCols(); nIncl > 0 { + include = make([]any, nIncl) + for c := 0; c < nIncl; c++ { + v, isNull, ierr := seg.includeVal(kd.ord, c) + if ierr != nil { + yield(TokenizedDoc{}, ierr) + return + } + if !isNull { + include[c] = v + } + } + } + if !yield(TokenizedDoc{Pk: seg.pk(kd.ord), Terms: terms, Positions: positions, Include: include}, nil) { + return + } + } + // buckets goes out of scope → freed before the next segment. + } + } +} + +// NumDocs returns the number of live documents in the index. +func (idx *Index) NumDocs() int64 { return idx.globalN } + +// Free releases the off-heap posting buffers of every segment the index owns. +// Called on cache eviction (Fulltext2Search.Destroy) and by single-shot loaders +// (compact) once done. Build-side segments free nothing (no-op). +func (idx *Index) Free() { + if idx != nil { + freeSegs(idx.segments) + } +} + +// normalizeKey converts a pk to a comparable map key ([]byte -> string) and is the +// key for every pk-keyed map (liveness, deletes, dedup). Keying by the pk VALUE (not +// its String()) makes it INJECTIVE for every comparable pk type via Go == : a +// microsecond-precision DATETIME/TIME/TIMESTAMP (whose String() truncates to whole +// seconds), a raw uuid ([16]byte), a decimal128 struct — all key by their exact value +// and can never collide, so no distinct live document is dropped. Mirrors bm25's +// normalizeKey exactly (the complete port; a %v/String() key was lossy). +func normalizeKey(pk any) any { + if b, ok := pk.([]byte); ok { + return string(b) + } + return pk +} + +// sortKey is a deterministic INJECTIVE string projection of a pk, used to order +// equal-score results (and MERGE output) reproducibly — never as a map key. The +// temporal types are keyed by their underlying int64 because %v/String() truncates +// them to whole seconds, which would make two equal-score DATETIME(6) rows sharing a +// second sort nondeterministically at a LIMIT boundary; every other scalar pk has an +// injective %v (int/decimal exact, uuid canonical, date a distinct day). +func sortKey(pk any) string { + switch v := pk.(type) { + case []byte: + return string(v) + case string: + return v + case types.Datetime: + return strconv.FormatInt(int64(v), 10) + case types.Time: + return strconv.FormatInt(int64(v), 10) + case types.Timestamp: + return strconv.FormatInt(int64(v), 10) + default: + return fmt.Sprintf("%v", v) + } +} diff --git a/pkg/fulltext2/membership.go b/pkg/fulltext2/membership.go new file mode 100644 index 0000000000000..1ee249d14addc --- /dev/null +++ b/pkg/fulltext2/membership.go @@ -0,0 +1,160 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "encoding/binary" + + "github.com/matrixorigin/matrixone/pkg/common/docfilter" + "github.com/matrixorigin/matrixone/pkg/container/types" +) + +// Membership is a doc-ord allow-set consulted during search for WHERE-clause +// prefiltering — the fulltext2 analogue of bm25/wand.Membership and cuVS filtered +// search. A nil Membership means "allow all" (no prefilter). It is built PER QUERY +// (the loaded Index is shared/cached across concurrent queries, so per-query filter +// state must never live on the Index) and threaded as a parameter into the segment +// searches, where it prunes candidates BEFORE they enter the top-k — so a pushed +// LIMIT bounds the FILTERED result, not the raw match set. +type Membership interface { + Contains(ord int64) bool +} + +// docFilterMembership resolves a pk-based docfilter.MembershipFilter (built in C from +// the pushed-down WHERE pks) against ONE segment's ord→pk dictionary: doc ord is +// allowed iff its pk bytes pass the filter. Mirrors bm25's docFilterMembership; +// fulltext2 shares the byte-identical pk codec, so the encoded probe bytes match what +// docfilter.Build hashed on the source side. For integer PKs this is the C int64 +// cbitmap (exact); for other PKs a bloom (its false positives are removed by the +// downstream join to the filtered source). +type docFilterMembership struct { + seg *Segment + f docfilter.MembershipFilter + scratch [8]byte // reused encode buffer for the hot integer-PK path (Test copies out) +} + +func (d *docFilterMembership) Contains(ord int64) bool { + if ord < 0 || ord >= int64(d.seg.numDocs()) { + return false + } + // pk(ord) decodes on demand on a loaded segment (a small box on this WHERE-prefilter + // hot path); the docmap pk encoding differs from the docfilter probe encoding for some + // types (uuid raw vs canonical), so the decoded value must be re-encoded below. + v := d.seg.pk(ord) + // Contains runs once per WAND candidate on the block-max walk hot path. For the + // common integer PKs, encode straight into the reused scratch buffer (byte-identical + // to packUint*) rather than allocating via encodePk per call; Test reads the bytes + // synchronously and does not retain them, so reuse is safe. + var raw []byte + switch types.T(d.seg.PkType) { + case types.T_int64: + binary.LittleEndian.PutUint64(d.scratch[:], uint64(v.(int64))) + raw = d.scratch[:8] + case types.T_uint64: + binary.LittleEndian.PutUint64(d.scratch[:], v.(uint64)) + raw = d.scratch[:8] + case types.T_int32: + binary.LittleEndian.PutUint32(d.scratch[:4], uint32(v.(int32))) + raw = d.scratch[:4] + case types.T_uint32: + binary.LittleEndian.PutUint32(d.scratch[:4], v.(uint32)) + raw = d.scratch[:4] + case types.T_uuid: + // The membership filter hashes each source uuid as its RAW 16 bytes; probe with + // the same raw bytes, NOT encodePk (the 36-char canonical string), which would + // never hit the same bloom cell and reject every candidate. + u := v.(types.Uuid) + raw = u[:] + default: + var err error + if raw, err = encodePk(d.seg.PkType, v); err != nil { + return false + } + } + return d.f.Test(raw) +} + +// prefilter bundles the per-query WHERE prefilters threaded into every segment search: the +// pk-based docfilter (WHERE pushdown resolved in C) AND compiled INCLUDE-column predicates +// (evaluated in Go against the stored per-doc value). Either may be empty; a nil *prefilter +// means "allow all". Built once per query in Fulltext2Search.Search and bound to each +// segment by mkAllow. It must never live on the shared Index (per-query state). +type prefilter struct { + docFilter docfilter.MembershipFilter + include []compiledIncludePred +} + +// mkAllow returns a per-segment WHERE-prefilter Membership over seg's ords (the docfilter +// AND the INCLUDE predicates), or nil when there is no filter (the "allow all" fast path). +func mkAllow(seg *Segment, p *prefilter) Membership { + if p == nil { + return nil + } + var m Membership + if p.docFilter != nil { + m = &docFilterMembership{seg: seg, f: p.docFilter} + } + if len(p.include) > 0 { + m = andAllow(m, &includePredMembership{seg: seg, preds: p.include}) + } + return m +} + +// allowed reports whether ord passes the membership (nil = allow all) — the one-liner +// the segment searches use at each admit point. +func allowed(m Membership, ord int64) bool { return m == nil || m.Contains(ord) } + +// livenessMembership admits ord in segment si iff it is the LIVE copy of its pk +// (highest-Recency, not delete-shadowed). Threaded into the per-segment boolean walk +// so a segment's top-k is k LIVE docs — a dead copy (superseded by a later CDC +// segment, or deleted) never enters the heap, so the cross-segment merge can't +// under-fill by later dropping it. Mirrors bm25's per-segment liveness Membership. +type livenessMembership struct { + idx *Index + si int +} + +func (l *livenessMembership) Contains(ord int64) bool { + // Bounds-check on BOTH paths: never admit an out-of-range ord (the scoring that + // follows indexes seg.pks[ord] and would panic). + if ord < 0 || ord >= int64(l.idx.segments[l.si].numDocs()) { + return false + } + b := l.idx.liveOrd[l.si] + if b == nil { + return true // fully-live segment (no dead copies) — the fast path + } + return b[ord] +} + +// andMembership is the conjunction of two memberships (either may be nil = allow all). +type andMembership struct{ a, b Membership } + +func (m andMembership) Contains(ord int64) bool { + return (m.a == nil || m.a.Contains(ord)) && (m.b == nil || m.b.Contains(ord)) +} + +// andAllow ANDs two memberships, collapsing nils (used to AND the WHERE prefilter with +// per-segment liveness). nil AND x = x. +func andAllow(a, b Membership) Membership { + switch { + case a == nil: + return b + case b == nil: + return a + default: + return andMembership{a, b} + } +} diff --git a/pkg/fulltext2/mmap_other.go b/pkg/fulltext2/mmap_other.go new file mode 100644 index 0000000000000..4b246603b7ca2 --- /dev/null +++ b/pkg/fulltext2/mmap_other.go @@ -0,0 +1,33 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !darwin && !linux + +package fulltext2 + +import ( + "io" + "os" +) + +// mmapReadOnly fallback (non-unix): read the whole file into a Go slice. No page +// cache benefit, but the load/query API is identical; munmap is a no-op. +func mmapReadOnly(f *os.File) ([]byte, error) { + if _, err := f.Seek(0, io.SeekStart); err != nil { + return nil, err + } + return io.ReadAll(f) +} + +func munmap(b []byte) error { return nil } diff --git a/pkg/fulltext2/mmap_unix.go b/pkg/fulltext2/mmap_unix.go new file mode 100644 index 0000000000000..92be18430a04f --- /dev/null +++ b/pkg/fulltext2/mmap_unix.go @@ -0,0 +1,47 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build darwin || linux + +package fulltext2 + +import ( + "os" + "syscall" +) + +// mmapReadOnly maps the whole file into a shared, read-only region. One mapping is +// created per cached base segment at load and shared by all concurrent queries — +// reads are plain memory loads (no lock), the kernel serializes page faults, and +// the OS page cache (reclaimable, unlike our off-heap C-malloc) is the residency +// manager. Unmapped by munmap under the cache's eviction write-lock (no reader in +// flight). Returns nil for a zero-length file. +func mmapReadOnly(f *os.File) ([]byte, error) { + fi, err := f.Stat() + if err != nil { + return nil, err + } + if fi.Size() == 0 { + return nil, nil + } + return syscall.Mmap(int(f.Fd()), 0, int(fi.Size()), syscall.PROT_READ, syscall.MAP_SHARED) +} + +// munmap releases a mapping from mmapReadOnly (no-op on nil/empty). +func munmap(b []byte) error { + if len(b) == 0 { + return nil + } + return syscall.Munmap(b) +} diff --git a/pkg/fulltext2/pkcodec.go b/pkg/fulltext2/pkcodec.go new file mode 100644 index 0000000000000..9ee0607b003e6 --- /dev/null +++ b/pkg/fulltext2/pkcodec.go @@ -0,0 +1,236 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "encoding/binary" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/vectorindex" +) + +// pk codec — the SINGLE SOURCE OF TRUTH for how a primary key of a given types.T is +// stored and moved through fulltext2. It is little-endian and used by the docmap, +// CDC log, delete frames, and the streaming ColumnBuffer. +// +// ADDING A PK TYPE: every switch in this file must gain the new case together — +// fixedPkByteWidth (width, or false for varlena) +// encodePk (concrete value → content bytes) +// decodePk (content bytes → boxed value) +// encodePkLen (has fast cases; falls back to encodePk otherwise) +// AppendColumnBuffer (content bytes → typed vector, box-free) +// TestPkCodecAllTypesRoundTrip loops every supported type through the codec so a +// missing case fails loudly rather than shipping (a missing T_bit case once stalled +// CDC — see the BIT-pk fix). +// +// NOTE: byte-for-byte identical to pkg/bm25/wand's pk codec — both engines store +// any-typed source PKs the same way. Extract to a shared pk-codec package once bm25 +// and fulltext2 live in the same tree (rule of three). + +// fixedPkByteWidth returns the exact stored byte width of a fixed-width pk type, and +// false for variable-width types (varchar/blob/uuid/json…). Used to validate a docmap's +// per-pk length prefix at load and to guard decodePk's fixed-width reads. +func fixedPkByteWidth(pkType int32) (int, bool) { + switch types.T(pkType) { + case types.T_int8, types.T_uint8: + return 1, true + case types.T_int16, types.T_uint16: + return 2, true + case types.T_int32, types.T_uint32, types.T_date: + return 4, true + case types.T_int64, types.T_uint64, types.T_bit, + types.T_datetime, types.T_time, types.T_timestamp, types.T_decimal64: + return 8, true + case types.T_decimal128: + return 16, true + default: + return 0, false + } +} + +func encodePk(pkType int32, v any) ([]byte, error) { + switch types.T(pkType) { + case types.T_int64: + return packUint64(uint64(v.(int64))), nil + case types.T_uint64: + return packUint64(v.(uint64)), nil + case types.T_bit: + // BIT is backed by uint64 (types.MustFixedColWithTypeCheck[uint64]); encode + // like T_uint64. build_ddl accepts a BIT primary key, so the codec must too — + // otherwise the first CDC flush errors and stalls index maintenance. + return packUint64(v.(uint64)), nil + case types.T_int32: + return packUint32(uint32(v.(int32))), nil + case types.T_uint32: + return packUint32(v.(uint32)), nil + case types.T_int16: + return packUint16(uint16(v.(int16))), nil + case types.T_uint16: + return packUint16(v.(uint16)), nil + case types.T_int8: + return []byte{byte(v.(int8))}, nil + case types.T_uint8: + return []byte{v.(uint8)}, nil + case types.T_varchar, types.T_char, types.T_text, types.T_datalink, + types.T_binary, types.T_varbinary, types.T_blob, types.T_json: + return asBytes(v), nil + case types.T_uuid: + switch x := v.(type) { + case string: + return []byte(x), nil + case types.Uuid: + return []byte(x.String()), nil + case []byte: + return append([]byte(nil), x...), nil + default: + return nil, moerr.NewInternalErrorNoCtxf("fulltext2: uuid pk unexpected go type %T", v) + } + case types.T_date: + return packUint32(uint32(int32(v.(types.Date)))), nil + case types.T_datetime: + return packUint64(uint64(int64(v.(types.Datetime)))), nil + case types.T_time: + return packUint64(uint64(int64(v.(types.Time)))), nil + case types.T_timestamp: + return packUint64(uint64(int64(v.(types.Timestamp)))), nil + case types.T_decimal64: + return packUint64(uint64(v.(types.Decimal64))), nil + case types.T_decimal128: + d := v.(types.Decimal128) + b := make([]byte, 16) + binary.LittleEndian.PutUint64(b[0:8], d.B0_63) + binary.LittleEndian.PutUint64(b[8:16], d.B64_127) + return b, nil + default: + return nil, moerr.NewInternalErrorNoCtxf("fulltext2: unsupported pk type %d", pkType) + } +} + +func decodePk(pkType int32, b []byte) (any, error) { + // Fixed-width types index into b with LittleEndian.Uint{16,32,64}, which panic on + // a short slice. A corrupt docmap can store a wrong length prefix, so verify the + // slice is wide enough and return a clean error instead of crashing the query + // goroutine. (decodeDocmap also width-validates at load; this is belt-and-suspenders.) + if w, fixed := fixedPkByteWidth(pkType); fixed && len(b) < w { + return nil, moerr.NewInternalErrorNoCtxf("fulltext2: corrupt pk: type %d needs %d bytes, got %d", pkType, w, len(b)) + } + switch types.T(pkType) { + case types.T_int64: + return int64(binary.LittleEndian.Uint64(b)), nil + case types.T_uint64: + return binary.LittleEndian.Uint64(b), nil + case types.T_bit: + return binary.LittleEndian.Uint64(b), nil + case types.T_int32: + return int32(binary.LittleEndian.Uint32(b)), nil + case types.T_uint32: + return binary.LittleEndian.Uint32(b), nil + case types.T_int16: + return int16(binary.LittleEndian.Uint16(b)), nil + case types.T_uint16: + return binary.LittleEndian.Uint16(b), nil + case types.T_int8: + return int8(b[0]), nil + case types.T_uint8: + return b[0], nil + case types.T_varchar, types.T_char, types.T_text, types.T_datalink, + types.T_binary, types.T_varbinary, types.T_blob, types.T_json: + return append([]byte(nil), b...), nil + case types.T_uuid: + u, err := types.ParseUuid(string(b)) + if err != nil { + return nil, err + } + return u, nil + case types.T_date: + return types.Date(int32(binary.LittleEndian.Uint32(b))), nil + case types.T_datetime: + return types.Datetime(int64(binary.LittleEndian.Uint64(b))), nil + case types.T_time: + return types.Time(int64(binary.LittleEndian.Uint64(b))), nil + case types.T_timestamp: + return types.Timestamp(int64(binary.LittleEndian.Uint64(b))), nil + case types.T_decimal64: + return types.Decimal64(binary.LittleEndian.Uint64(b)), nil + case types.T_decimal128: + return types.Decimal128{ + B0_63: binary.LittleEndian.Uint64(b[0:8]), + B64_127: binary.LittleEndian.Uint64(b[8:16]), + }, nil + default: + return nil, moerr.NewInternalErrorNoCtxf("fulltext2: unsupported pk type %d", pkType) + } +} + +// encodePkLen writes a length-prefixed pk ([u32 len][content]) into the docmap buffer, +// deriving the content from encodePk (one codec switch, not a duplicate here). +func (w *leBuf) encodePkLen(pkType int32, v any) error { + pkb, err := encodePk(pkType, v) + if err != nil { + return err + } + w.u32(uint32(len(pkb))) + w.b.Write(pkb) + return nil +} + +// appendAnyPk re-encodes a boxed pk value into a ColumnBuffer's Data (used by the +// build-side / materialized-fallback streaming paths): fixed-width types contribute +// their width bytes, varlena types a [u32 len][content] entry. No NEW box. +func appendAnyPk(k *vectorindex.ColumnBuffer, fixedW int, v any) { + content, err := encodePk(int32(k.Type), v) + if err != nil { + return // unsupported pk type never reaches streaming (validated at build) + } + if fixedW > 0 { + k.Data = append(k.Data, content...) // exactly fixedW bytes + } else { + var lp [4]byte + binary.LittleEndian.PutUint32(lp[:], uint32(len(content))) + k.Data = append(k.Data, lp[:]...) + k.Data = append(k.Data, content...) + } + k.N++ +} + +func packUint64(v uint64) []byte { + b := make([]byte, 8) + binary.LittleEndian.PutUint64(b, v) + return b +} + +func packUint32(v uint32) []byte { + b := make([]byte, 4) + binary.LittleEndian.PutUint32(b, v) + return b +} + +func packUint16(v uint16) []byte { + b := make([]byte, 2) + binary.LittleEndian.PutUint16(b, v) + return b +} + +func asBytes(v any) []byte { + switch x := v.(type) { + case []byte: + return x + case string: + return []byte(x) + default: + return nil + } +} diff --git a/pkg/fulltext2/plugin/compile/compile.go b/pkg/fulltext2/plugin/compile/compile.go new file mode 100644 index 0000000000000..f14e49a83b710 --- /dev/null +++ b/pkg/fulltext2/plugin/compile/compile.go @@ -0,0 +1,466 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package compile holds the fulltext2 index's compile-layer (DDL) hooks. +package compile + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" + + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/sqlquote" + "github.com/matrixorigin/matrixone/pkg/fulltext2" + fulltext2runtime "github.com/matrixorigin/matrixone/pkg/fulltext2/plugin/runtime" + compileplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/compile" + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" +) + +// parserFromParams extracts the "parser" field from an index's IndexAlgoParams +// JSON (empty → default parser). +func parserFromParams(params string) string { + if len(params) == 0 { + return "" + } + var p struct { + Parser string `json:"parser"` + } + if err := json.Unmarshal([]byte(params), &p); err != nil { + return "" + } + return p.Parser +} + +var _ compileplugin.Hooks = Hooks{} + +type Hooks struct{} + +// HandleCreateIndex creates the storage + metadata hidden tables and builds the +// tag=0 base segment from the current source rows (Go-side: SELECT pk,col → +// tokenize → Segment → persist). Step-4 first cut: whole-table read + one base +// segment; CDC-async incremental maintenance follows. +func (Hooks) HandleCreateIndex(ctx compileplugin.CompileContext, indexDefs map[string]*plan.IndexDef) error { + // Gate CREATE FULLTEXT2 INDEX behind experimental_fulltext2_index. Frontend-only: + // re-checking here (in addition to the framework gate in pkg/sql/compile/util.go + // via ExperimentalFlag()) catches a flag toggled off since the original CREATE; + // the background CDC/reindex context may not surface the user's session value, so + // skip the check there. Mirrors bm25's HandleCreateIndex gate. + if ctx.IsFrontend() { + if ok, err := ctx.IsExperimentalEnabled(fulltext2runtime.Fulltext2IndexFlag); err != nil { + return err + } else if !ok { + return moerr.NewInternalErrorNoCtx("experimental_fulltext2_index is not enabled") + } + } + + storeDef, ok := indexDefs[catalog.FullText2Index_TblType_Storage] + if !ok { + return moerr.NewInternalErrorNoCtx("fulltext2 storage index definition not found") + } + metaDef, ok := indexDefs[catalog.FullText2Index_TblType_Metadata] + if !ok { + return moerr.NewInternalErrorNoCtx("fulltext2 metadata index definition not found") + } + + // 1. create the hidden tables. + if info := ctx.IndexInfo(); info != nil { + for _, table := range info.GetIndexTables() { + if err := ctx.BuildIndexTable(table); err != nil { + return err + } + } + } + + origTable := ctx.OriginalTableDef() + db := ctx.QryDatabase() + // CCPR: index data syncs via CCPR on a publication-subscribed table. + if ctx.IsCCPRTaskTransaction() && ctx.IsTableFromPublication(origTable) { + return nil + } + + // Fresh CREATE has no prior tail; build the base + register CDC. + return buildAndRegisterCDC(ctx, storeDef, metaDef, origTable, db, false) +} + +// buildAndRegisterCDC (re)builds the tag=0 base from source and registers the +// ISCP CDC task from now (startFromNow=true, no InitSQL): the inline build covers +// the current rows, so subsequent INSERT/UPDATE/DELETE flow into the tag=1 tail +// via RunFulltext2. The CDC task is dropped+recreated so a REINDEX re-entry does +// not replay history over the fresh base. On REBUILD (clearTail) the prior tag=1 +// tail is discarded first — the fresh base already reflects every committed row. +func buildAndRegisterCDC(ctx compileplugin.CompileContext, storeDef, metaDef *plan.IndexDef, origTable *plan.TableDef, db string, clearTail bool) error { + if clearTail { + cfg := fulltext2.TableConfig{DbName: db, IndexTable: storeDef.IndexTableName, MetadataTable: metaDef.IndexTableName} + // REBUILD: clear the tail AND the prior tag=0 base(s) here. The create TVF also + // clears the bases, but it SKIPS that when the rebuild sees zero source rows + // (empty/all-deleted table) — so without this a REBUILD over an emptied table + // would keep serving the stale old base. Evict the local cache too, since the + // create TVF's post-build eviction is likewise skipped on the zero-doc path. + clearSqls := append(fulltext2.DeleteTailSqls(cfg), fulltext2.DeleteAllBasesSqls(cfg)...) + for _, s := range clearSqls { + if err := ctx.RunSql(s); err != nil { + return err + } + } + cache.Cache.Remove(storeDef.IndexTableName) + } + // buildFromSource clears the prior tag=0 bases (idempotent) and rebuilds them. + if err := buildFromSource(ctx, storeDef, metaDef, origTable, db); err != nil { + return err + } + sinkerType := ctx.SinkerTypeFromAlgo(catalog.MoIndexFullText2Algo.ToString()) + if err := ctx.DropIndexCdcTask(origTable, db, origTable.Name, storeDef.IndexName); err != nil { + return err + } + if err := ctx.CreateIndexCdcTask(db, origTable.Name, origTable.TblId, storeDef.IndexName, sinkerType, true, "", origTable); err != nil { + return err + } + return registerFulltext2Idxcron(ctx, storeDef, db, origTable) +} + +// DefaultMaxIndexCapacity caps each tag=0 sub-index's doc count when the +// WITH max_index_capacity option is omitted. +const DefaultMaxIndexCapacity = int64(1000000) + +// DefaultMaxPostingsCapacity caps each tag=0 sub-index's posting (term-occurrence) +// count when WITH max_postings_capacity is omitted (~512 MB peak build heap). Bounds +// build memory for long-document corpora that a doc-count cap alone would not. +const DefaultMaxPostingsCapacity = int64(8_000_000) + +// buildFromSource builds the index straight from the SOURCE table in one +// statement: SELECT f.* FROM src CROSS APPLY fulltext2_create(params, cfg, pk, +// cols…). The create TVF tokenizes each row in execution (datalink → plain text, +// json → values, ngram/gojieba parser), Add's the tokens to a builder, and at +// end() splits into capacity-bounded tag=0 bases and persists the chunk rows. No +// segment assembly happens in this (compile) layer. +func buildFromSource(ctx compileplugin.CompileContext, storeDef, metaDef *plan.IndexDef, origTable *plan.TableDef, db string) error { + if len(storeDef.Parts) == 0 { + return moerr.NewInternalErrorNoCtx("fulltext2 index has no indexed column") + } + capacity, err := resolveFulltext2Capacity(storeDef.IndexAlgoParams) + if err != nil { + return err + } + postingCap, err := resolveFulltext2PostingCapacity(storeDef.IndexAlgoParams) + if err != nil { + return err + } + sql, err := genFulltext2BuildFromSourceSQL(origTable, storeDef, metaDef, db, capacity, postingCap) + if err != nil { + return err + } + return ctx.RunSql(sql) +} + +// resolveFulltext2Capacity reads max_index_capacity from the index's algo_params, +// defaulting when the WITH option was omitted. +func resolveFulltext2Capacity(algoParams string) (int64, error) { + if algoParams == "" { + return DefaultMaxIndexCapacity, nil // no WITH options + } + flat, err := catalog.IndexParamsStringToMap(algoParams) + if err != nil { + return 0, err + } + if v, ok := flat[catalog.IndexAlgoParamMaxIndexCapacity]; ok && v != "" { + n, perr := strconv.ParseInt(v, 10, 64) + if perr != nil { + return 0, perr + } + if n > 0 { + return n, nil + } + } + return DefaultMaxIndexCapacity, nil +} + +// resolveFulltext2PostingCapacity reads max_postings_capacity from the index's +// algo_params, defaulting when the WITH option was omitted. Mirrors the doc-cap +// resolver; both caps are pinned at CREATE and read back on every build path. +func resolveFulltext2PostingCapacity(algoParams string) (int64, error) { + if algoParams == "" { + return DefaultMaxPostingsCapacity, nil + } + flat, err := catalog.IndexParamsStringToMap(algoParams) + if err != nil { + return 0, err + } + if v, ok := flat[catalog.IndexAlgoParamMaxPostingsCapacity]; ok && v != "" { + n, perr := strconv.ParseInt(v, 10, 64) + if perr != nil { + return 0, perr + } + if n > 0 { + return n, nil + } + } + return DefaultMaxPostingsCapacity, nil +} + +// resolveFulltext2PositionFree reads position_free from the index's algo_params +// (absence ⇒ false, i.e. a positional/phrase-capable index). +func resolveFulltext2PositionFree(algoParams string) (bool, error) { + if algoParams == "" { + return false, nil + } + flat, err := catalog.IndexParamsStringToMap(algoParams) + if err != nil { + return false, err + } + return flat[catalog.IndexAlgoParamPositionFree] == "true", nil +} + +// genFulltext2BuildFromSourceSQL emits the CROSS APPLY fulltext2_create build SQL. +func genFulltext2BuildFromSourceSQL(origTable *plan.TableDef, storeDef, metaDef *plan.IndexDef, db string, capacity, postingCap int64) (string, error) { + const srcAlias = "src" + positionFree, err := resolveFulltext2PositionFree(storeDef.IndexAlgoParams) + if err != nil { + return "", err + } + cfg := fulltext2.TableConfig{ + DbName: db, + SrcTable: origTable.Name, + IndexTable: storeDef.IndexTableName, + MetadataTable: metaDef.IndexTableName, + PKey: origTable.Pkey.PkeyColName, + Parser: parserFromParams(storeDef.IndexAlgoParams), + Capacity: capacity, + PostingCapacity: postingCap, + PositionFree: positionFree, + FromSource: true, + } + cols := make([]string, 0, len(storeDef.Parts)) + for _, p := range storeDef.Parts { + cols = append(cols, sqlquote.QualifiedIdent(srcAlias, p)) + } + // INCLUDE columns: their actual values are stored in the segment docmap (prefilter + + // coverage). They are passed as the TRAILING fulltext2_create args (after the text + // columns); cfg.IncludeTypes tells the TVF how many trailing args are INCLUDE values and + // their types. Resolve names from the first-class field, falling back to algo_params. + incNames := fulltext2IncludeColumns(storeDef) + if len(incNames) > 0 { + incTypes := make([]int32, len(incNames)) + for i, name := range incNames { + ci, ok := origTable.Name2ColIndex[name] + if !ok { + return "", moerr.NewInternalErrorNoCtx("fulltext2 build: INCLUDE column " + name + " not found on source table") + } + incTypes[i] = origTable.Cols[ci].Typ.Id + cols = append(cols, sqlquote.QualifiedIdent(srcAlias, name)) + } + cfg.IncludeTypes = incTypes + } + cfgbytes, err := json.Marshal(cfg) + if err != nil { + return "", err + } + sql := fmt.Sprintf("SELECT f.* FROM %s AS %s CROSS APPLY fulltext2_create(%s, %s, %s, %s) AS f", + sqlquote.QualifiedIdent(db, origTable.Name), + sqlquote.Ident(srcAlias), + sqlquote.String(storeDef.IndexAlgoParams), + sqlquote.String(string(cfgbytes)), + sqlquote.QualifiedIdent(srcAlias, origTable.Pkey.PkeyColName), + strings.Join(cols, ", ")) + return sql, nil +} + +// fulltext2IncludeColumns returns the index's INCLUDE column names, preferring the +// first-class IndexDef field and falling back to the persisted algo_param (reload-safe). +func fulltext2IncludeColumns(def *plan.IndexDef) []string { + if len(def.IncludedColumns) > 0 { + return def.IncludedColumns + } + m, err := catalog.IndexParamsStringToMap(def.IndexAlgoParams) + if err != nil { + return nil + } + raw := m[catalog.IncludedColumns] + if raw == "" { + return nil + } + names, err := catalog.ParseIncludeColumnsValue(raw) + if err != nil { + return nil + } + return names +} + +// HandleReindex runs ALTER … ALTER REINDEX: merge=true compacts the tag=1 tail +// into the tag=0 base (dropping dead docs) via fulltext2_compact; merge=false +// (REBUILD) discards the tail and rebuilds the base from source. Mirrors bm25. +func (Hooks) HandleReindex(ctx compileplugin.CompileContext, indexDefs map[string]*plan.IndexDef, _, merge bool) error { + storeDef, ok := indexDefs[catalog.FullText2Index_TblType_Storage] + if !ok { + return moerr.NewInternalErrorNoCtx("fulltext2 storage index definition not found") + } + metaDef, ok := indexDefs[catalog.FullText2Index_TblType_Metadata] + if !ok { + return moerr.NewInternalErrorNoCtx("fulltext2 metadata index definition not found") + } + if merge { + return handleMergeCompact(ctx, storeDef, metaDef) + } + return buildAndRegisterCDC(ctx, storeDef, metaDef, ctx.OriginalTableDef(), ctx.QryDatabase(), true /* clear tail */) +} + +// handleMergeCompact folds the tag=1 CdcTail into the tag=0 base and drops dead +// docs, via the fulltext2_compact TVF (execution-side, one txn) — no re-tokenize. +// Capacity comes from the PERSISTED algo_params (pinned at CREATE) so a manual +// MERGE never depends on the triggering session. Mirrors bm25's handleMergeCompact. +func handleMergeCompact(ctx compileplugin.CompileContext, storeDef, metaDef *plan.IndexDef) error { + capacity, err := resolveFulltext2Capacity(storeDef.IndexAlgoParams) + if err != nil { + return err + } + postingCap, err := resolveFulltext2PostingCapacity(storeDef.IndexAlgoParams) + if err != nil { + return err + } + positionFree, err := resolveFulltext2PositionFree(storeDef.IndexAlgoParams) + if err != nil { + return err + } + sql := fmt.Sprintf("SELECT * FROM fulltext2_compact(%s, %s, %s, %s, %s, %s) AS f", + sqlquote.String(ctx.QryDatabase()), + sqlquote.String(storeDef.IndexTableName), + sqlquote.String(metaDef.IndexTableName), + sqlquote.String(strconv.FormatInt(capacity, 10)), + sqlquote.String(strconv.FormatBool(positionFree)), + sqlquote.String(strconv.FormatInt(postingCap, 10))) + return ctx.RunSql(sql) +} + +// RestoreInitSQL — the clone copies the storage+metadata rows; register CDC from +// the post-clone TS. +func (Hooks) RestoreInitSQL(_ compileplugin.CompileContext, _ map[string]*plan.IndexDef) (bool, string, error) { + return true, "SELECT 1", nil +} + +// ValidateReindexParams — fulltext2 honors position_free on a rebuild (its only +// reindex-time param): POSITION_FREE=TRUE sets it, =FALSE clears it (→ a positional +// rebuild that re-derives positions from source), absence keeps the current setting. +// Other reindex options are ignored. The REBUILD (buildFromSource) then reads the +// merged algo_params. NOTE: changing position_free needs REBUILD (no MERGE) — a MERGE +// cannot re-derive positions when turning position_free OFF. +func (Hooks) ValidateReindexParams(old map[string]string, alter compileplugin.ReindexParamUpdate) (map[string]string, error) { + pf, hasPF := alter.Params[catalog.IndexAlgoParamPositionFree] + pc, hasPC := alter.Params[catalog.IndexAlgoParamMaxPostingsCapacity] + if !hasPF && !hasPC { + return old, nil // nothing fulltext2 honors on a reindex + } + merged := make(map[string]string, len(old)+1) + for k, v := range old { + merged[k] = v + } + if hasPF { + // A MERGE folds the tag=1 tail into the existing base IN PLACE — it re-serializes the + // current segments and cannot change their positional format: turning POSITION_FREE OFF has + // no positions to re-derive (the base dropped them), and CompactSegments deletes the old + // rows before rebuilding, so a `MERGE POSITION_FREE=FALSE` would commit an EMPTY base even + // though the source rows are unchanged — data loss. Changing POSITION_FREE requires a full + // REBUILD (re-derives from the source table). Reject the combination BEFORE the params are + // persisted, so a MERGE can never mint a format-mismatched base. + curPF := old[catalog.IndexAlgoParamPositionFree] == "true" + newPF := pf == "true" + if alter.Merge && newPF != curPF { + return nil, moerr.NewInvalidInputNoCtx( + "fulltext2: changing POSITION_FREE requires a REBUILD (ALTER … REINDEX without MERGE); " + + "a MERGE compacts the tail into the existing base and cannot re-derive positions") + } + if pf == "true" { + // Only gojieba is meaningful position-free (ngram/json trigrams are noise as + // bag-of-words). Mirrors the CREATE-time check. + if old["parser"] != "gojieba" { + return nil, moerr.NewInvalidInputNoCtxf( + "fulltext2 POSITION_FREE requires the gojieba parser (this index uses %q); ngram/json need positions", old["parser"]) + } + merged[catalog.IndexAlgoParamPositionFree] = "true" + } else { + delete(merged, catalog.IndexAlgoParamPositionFree) // false ⇒ positional (param absent) + } + } + // max_postings_capacity override: the grammar already rejected <=0, so a present + // value is valid; the subsequent REBUILD/MERGE reads it back from the merged + // algo_params and seals segments at the new posting bound. (max_index_capacity is + // NOT re-honored on a reindex — a pre-existing fulltext2 limitation, unchanged.) + if hasPC { + merged[catalog.IndexAlgoParamMaxPostingsCapacity] = pc + } + return merged, nil +} + +// HandleDropIndex — no algorithm-specific cleanup beyond the generic hidden-table +// deletion the SQL layer performs. +func (Hooks) HandleDropIndex(_ compileplugin.CompileContext, _ map[string]*plan.IndexDef) error { + return nil +} + +// HiddenTableDropPriority: fulltext2's storage and metadata hidden tables have no +// inter-dependency that constrains drop order, so all types drop at the same +// priority (mirrors classic fulltext). +func (Hooks) HiddenTableDropPriority(_ string) int { + return 0 +} + +// actionFulltext2Reindex is the idxcron action key for fulltext2's scheduled +// compaction; must match the runtime plugin's SyncDescriptor.IdxcronAction. +const actionFulltext2Reindex = "fulltext2_reindex" + +// IdxcronMetadata — the scheduled compaction reads its cadence knobs +// (max_index_capacity, tail thresholds) from the PERSISTED algo_params, so the +// only thing worth pinning in the blob is lower_case_table_names: its VALUE +// matters because the background reindex must fold identifiers in the rebuild +// SQL exactly as the user built under (a mismatch resolves the wrong +// table/column). This mirrors the vector-index plugins' capture list. sql_mode +// is deliberately NOT captured — a background reindex is a system op that runs +// permissive, defaulted at the reindex hook via +// sqlexec.Metadata.ResolveVariableWithSessionDefaults (capturing the user's +// possibly-strict sql_mode would be wrong). +// +// A background re-entry (not frontend) returns nil so the existing task row +// persists. If lower_case_table_names can't be resolved in this context we still +// register with an empty blob (fulltext2 needs only a non-nil blob to register; +// it reads everything else from algo_params). Mirrors bm25. +func (Hooks) IdxcronMetadata(ctx compileplugin.CompileContext) ([]byte, error) { + if !ctx.IsFrontend() { + return nil, nil + } + blob, err := compileplugin.BuildIdxcronMetadata(ctx, compileplugin.IdxcronVarSpec{ + Capture: []string{"lower_case_table_names"}, + }) + if err != nil { + return nil, err + } + if len(blob) == 0 { + return []byte("{}"), nil + } + return blob, nil +} + +// registerFulltext2Idxcron registers the scheduled-compaction task (skipped on a +// background re-entry so the existing task row persists). Mirrors bm25. +func registerFulltext2Idxcron(ctx compileplugin.CompileContext, storeDef *plan.IndexDef, db string, origTable *plan.TableDef) error { + metadata, err := Hooks{}.IdxcronMetadata(ctx) + if err != nil { + return err + } + if len(metadata) == 0 { + return nil + } + return ctx.RegisterIdxcronUpdate(origTable.TblId, db, origTable.Name, storeDef.IndexName, actionFulltext2Reindex, metadata) +} diff --git a/pkg/fulltext2/plugin/idxcron/idxcron.go b/pkg/fulltext2/plugin/idxcron/idxcron.go new file mode 100644 index 0000000000000..31d4b985e07a7 --- /dev/null +++ b/pkg/fulltext2/plugin/idxcron/idxcron.go @@ -0,0 +1,164 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package idxcron is the fulltext2 index's scheduled-compaction hook (mirrors +// bm25's). fulltext2 participates in scheduled reindex +// (SyncDescriptor.IdxcronAction = "fulltext2_reindex"); the cron executor runs +// `ALTER … REINDEX … FULLTEXT2 [MERGE] FORCE_SYNC` when Updatable fires — MERGE +// folds the tag=1 CdcTail into the tag=0 base, or a full REBUILD once the dead-doc +// fraction is high (ReindexOption). +package idxcron + +import ( + "fmt" + "os" + "strconv" + "time" + + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/sqlquote" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/fulltext2" + idxcronplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/idxcron" + "github.com/matrixorigin/matrixone/pkg/logutil" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" +) + +// TailChunkThreshold gates the scheduled compaction on tag=1 CdcTail growth: fire +// only once the tail has at least this many chunk rows since the last reindex. A +// dev/test deploy can lower it via MO_IDXCRON_FULLTEXT2_TAIL_THRESHOLD (e.g. 1). +// Mirrors bm25's TailChunkThreshold. +var TailChunkThreshold = func() int64 { + if v := os.Getenv("MO_IDXCRON_FULLTEXT2_TAIL_THRESHOLD"); v != "" { + if n, err := strconv.ParseInt(v, 10, 64); err == nil && n > 0 { + return n + } + } + return 1024 +}() + +type Hooks struct{} + +var _ idxcronplugin.Hooks = Hooks{} +var _ idxcronplugin.ReindexOptioner = Hooks{} + +// Updatable fires the scheduled compaction once the tag=1 CdcTail has grown past +// TailChunkThreshold (the framework already applied the auto_update/hour/cadence +// gates). Mirrors bm25's Updatable. +func (Hooks) Updatable(in idxcronplugin.UpdatableInput) (bool, string, error) { + if in.LastUpdateAt != nil { + last := time.Unix(in.LastUpdateAt.Unix(), 0) + if last.Add(in.Interval).After(time.Now()) { + return false, fmt.Sprintf("within reindex interval (last %s + %v)", + last.Format("2006-01-02 15:04:05"), in.Interval), nil + } + } + + var storageTbl string + for _, idx := range in.TableDef.Indexes { + if idx.IndexName == in.IndexName && idx.IndexAlgoTableType == catalog.FullText2Index_TblType_Storage { + storageTbl = idx.IndexTableName + break + } + } + if storageTbl == "" { + return false, "not a fulltext2 index (no storage table)", nil + } + + cfg := fulltext2.TableConfig{DbName: in.TableDef.DbName, IndexTable: storageTbl} + count, err := fulltext2.CountTailChunks(in.Sqlproc, cfg) + if err != nil { + return false, "", err + } + logutil.Infof("[idxcron][ftv2] Updatable index=%s: tag=1 tail chunks=%d threshold=%d", + in.IndexName, count, TailChunkThreshold) + if count < TailChunkThreshold { + return false, fmt.Sprintf("tag=1 tail chunks %d < threshold %d", count, TailChunkThreshold), nil + } + return true, "", nil +} + +// RebuildDeadPct: once the dead-doc percentage in the base exceeds this, the +// scheduled compaction fires a full REBUILD instead of an incremental MERGE. +const RebuildDeadPct = 30 + +// ReindexOption picks the scheduled reindex mode: "MERGE" normally, "" (full +// REBUILD) once the dead-doc fraction (1 - liveSourceRows/baseDocs) exceeds +// RebuildDeadPct. Mirrors bm25's ReindexOption. +func (Hooks) ReindexOption(in idxcronplugin.UpdatableInput) (string, error) { + var metaTbl string + for _, idx := range in.TableDef.Indexes { + if idx.IndexName == in.IndexName && idx.IndexAlgoTableType == catalog.FullText2Index_TblType_Metadata { + metaTbl = idx.IndexTableName + break + } + } + if metaTbl == "" { + return "MERGE", nil + } + cfg := fulltext2.TableConfig{DbName: in.TableDef.DbName, MetadataTable: metaTbl} + baseDocs, err := fulltext2.SumBaseNrow(in.Sqlproc, cfg) + if err != nil { + return "", err + } + if baseDocs == 0 { + return "MERGE", nil + } + liveRows, err := countSourceRows(in.Sqlproc, in.TableDef.DbName, in.TableDef.Name) + if err != nil { + return "", err + } + opt := reindexOptionForCounts(liveRows, baseDocs) + deadPct := int64(0) + if baseDocs > 0 { + deadPct = 100 - liveRows*100/baseDocs + } + logutil.Infof("[idxcron][ftv2] ReindexOption index=%s: liveRows=%d baseDocs=%d dead=%d%% threshold=%d%% -> %s", + in.IndexName, liveRows, baseDocs, deadPct, RebuildDeadPct, optLabel(opt)) + return opt, nil +} + +// reindexOptionForCounts is the pure decision: "" (REBUILD) once dead% > +// RebuildDeadPct, else "MERGE". Integer arithmetic for an exact boundary. +func reindexOptionForCounts(liveRows, baseDocs int64) string { + if baseDocs <= 0 { + return "MERGE" + } + if liveRows*100 < baseDocs*(100-RebuildDeadPct) { + return "" // dead % > RebuildDeadPct → full REBUILD + } + return "MERGE" +} + +func optLabel(opt string) string { + if opt == "" { + return "REBUILD" + } + return opt +} + +// countSourceRows returns the source table's live row count (= live docs). +func countSourceRows(sqlproc *sqlexec.SqlProcess, db, table string) (int64, error) { + res, err := sqlexec.RunSql(sqlproc, fmt.Sprintf("SELECT COUNT(*) FROM %s", sqlquote.QualifiedIdent(db, table))) + if err != nil { + return 0, err + } + defer res.Close() + for _, bat := range res.Batches { + if bat != nil && bat.RowCount() > 0 { + return vector.GetFixedAtNoTypeCheck[int64](bat.Vecs[0], 0), nil + } + } + return 0, nil +} diff --git a/pkg/fulltext2/plugin/iscp/iscp.go b/pkg/fulltext2/plugin/iscp/iscp.go new file mode 100644 index 0000000000000..6d02fc718dcba --- /dev/null +++ b/pkg/fulltext2/plugin/iscp/iscp.go @@ -0,0 +1,46 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package iscp is fulltext2's ISCP hook layer. A fulltext2 index is +// model-building: its CDC sinker builds tag=1 CdcTail delta frames (the writer / +// consumer loop in pkg/iscp), NOT SQL text. Mirrors bm25's iscp hooks. +// +// Registered from pkg/indexplugin/iscp/import.go via iscp.Register. +package iscp + +import ( + "context" + + "github.com/matrixorigin/matrixone/pkg/catalog" + iscppkg "github.com/matrixorigin/matrixone/pkg/iscp" + "github.com/matrixorigin/matrixone/pkg/pb/plan" +) + +func init() { + iscppkg.Register(catalog.MoIndexFullText2Algo.ToString(), Hooks{}) +} + +// Hooks implements iscp.Hooks for fulltext2. +type Hooks struct{} + +var _ iscppkg.Hooks = Hooks{} + +func (Hooks) NewSqlWriter(jobID iscppkg.JobID, info *iscppkg.ConsumerInfo, + tabledef *plan.TableDef, indexdefs []*plan.IndexDef) (iscppkg.IndexSqlWriter, error) { + return iscppkg.NewFulltext2SqlWriter(catalog.MoIndexFullText2Algo.ToString(), jobID, info, tabledef, indexdefs) +} + +func (Hooks) Run(c *iscppkg.IndexConsumer, ctx context.Context, errch chan error, r iscppkg.DataRetriever) { + iscppkg.RunFulltext2(c, ctx, errch, r) +} diff --git a/pkg/fulltext2/plugin/plan/plan.go b/pkg/fulltext2/plugin/plan/plan.go new file mode 100644 index 0000000000000..0ad9bde9c6e21 --- /dev/null +++ b/pkg/fulltext2/plugin/plan/plan.go @@ -0,0 +1,50 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package plan holds the fulltext2 index's plan-layer hooks: hidden-table schema +// construction (BuildFullTextIndexDefs) and the inert vector-sort hooks (fulltext2 +// is queried via MATCH, not the ORDER BY rewrite). +package plan + +import ( + "github.com/matrixorigin/matrixone/pkg/common/moerr" + planplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/plan" + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" +) + +var _ planplugin.Hooks = Hooks{} + +type Hooks struct{} + +// BuildSecondaryIndexDefs — fulltext2 is created via CREATE FULLTEXT2 INDEX +// (*tree.FullTextIndex → BuildFullTextIndexDefs), not the vector `USING` path. +func (Hooks) BuildSecondaryIndexDefs( + _ planplugin.CompilerContext, _ *tree.Index, _ map[string]*plan.ColDef, _ []*plan.IndexDef, _ string, +) ([]*plan.IndexDef, []*plan.TableDef, error) { + return nil, nil, moerr.NewNotSupportedNoCtx("fulltext2 index is created via CREATE FULLTEXT2 INDEX") +} + +// CanApply — fulltext2 is queried via MATCH (the apply_indices_fulltext path), +// never the vector ORDER BY LIMIT rewrite. +func (Hooks) CanApply(planplugin.PlanBuilder, *planplugin.VectorSortContext, *planplugin.MultiTableIndexRef) (bool, error) { + return false, nil +} + +// ApplyForSort — inert (returns the node id unchanged). +func (Hooks) ApplyForSort( + _ planplugin.PlanBuilder, _ *planplugin.VectorSortContext, _ *planplugin.MultiTableIndexRef, nodeID int32, _ planplugin.ApplyForSortOpts, +) (int32, bool, error) { + return nodeID, false, nil +} diff --git a/pkg/fulltext2/plugin/plan/schema.go b/pkg/fulltext2/plugin/plan/schema.go new file mode 100644 index 0000000000000..f4dbdb5c45cc4 --- /dev/null +++ b/pkg/fulltext2/plugin/plan/schema.go @@ -0,0 +1,299 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plan + +import ( + "fmt" + "strconv" + "strings" + + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/types" + ftv2rt "github.com/matrixorigin/matrixone/pkg/fulltext2/plugin/runtime" + catalogplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/catalog" + planplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/plan" + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" + "github.com/matrixorigin/matrixone/pkg/sql/util" +) + +// fulltext2CatalogHooks is the runtime catalog contract used here for pk-type +// validation, so DDL rejects a primary key the segment codec can't encode. +var fulltext2CatalogHooks = ftv2rt.CatalogHooks{} + +// BuildFullTextIndexDefs constructs a fulltext2 index's two hidden tables — a +// chunked segment store + a metadata table (bm25 layout) — from CREATE FULLTEXT2 +// INDEX. Both defs are stamped algo="fulltext2" (told apart by IndexAlgoTableType) +// so every downstream hook dispatches to this plugin. +func (Hooks) BuildFullTextIndexDefs( + ctx planplugin.CompilerContext, + indexInfo *tree.FullTextIndex, + colMap map[string]*plan.ColDef, + existedIndexes []*plan.IndexDef, + pkeyName string, +) ([]*plan.IndexDef, []*plan.TableDef, error) { + if pkeyName == "" || pkeyName == catalog.FakePrimaryKeyColName { + return nil, nil, moerr.NewInternalErrorNoCtx("primary key cannot be empty for fulltext2 index") + } + // Reject a primary key the segment pk codec can't encode (e.g. FLOAT/DOUBLE) HERE, + // at CREATE, instead of letting an accepted DDL fail later at build/CDC. A composite + // PK is a synthesized CP key absent from colMap (ok=false) and is encoded as bytes, so + // it is intentionally not gated. + if col, ok := colMap[pkeyName]; ok && !catalogplugin.SupportsPrimaryKeyType(fulltext2CatalogHooks, types.T(col.Typ.Id)) { + return nil, nil, moerr.NewNotSupported(ctx.GetContext(), + fmt.Sprintf("fulltext2 index does not support primary key type '%s'", types.T(col.Typ.Id).String())) + } + + // Reject a duplicate fulltext2 index on the same column set. + for _, existed := range existedIndexes { + if existed.IndexAlgo != tree.INDEX_TYPE_FULLTEXT2.ToString() || len(indexInfo.KeyParts) != len(existed.Parts) { + continue + } + n := 0 + for _, keyPart := range indexInfo.KeyParts { + for _, ePart := range existed.Parts { + if ePart == keyPart.ColName.ColName() { + n++ + break + } + } + } + if n == len(indexInfo.KeyParts) { + return nil, nil, moerr.NewNotSupported(ctx.GetContext(), "fulltext2 index are not allowed to use the same column") + } + } + + // Validate column types (char/varchar/text/json/datalink). + for _, keyPart := range indexInfo.KeyParts { + name := keyPart.ColName.ColName() + col, ok := colMap[name] + if !ok { + return nil, nil, moerr.NewInvalidInput(ctx.GetContext(), fmt.Sprintf("column '%s' does not exist", keyPart.ColName.ColNameOrigin())) + } + typid := col.Typ.Id + if !(typid == int32(types.T_text) || typid == int32(types.T_char) || + typid == int32(types.T_varchar) || typid == int32(types.T_json) || typid == int32(types.T_datalink)) { + return nil, nil, moerr.NewNotSupported(ctx.GetContext(), "fulltext2 index only support char, varchar, text, datalink and json") + } + } + // Validate parser. + if indexInfo.IndexOption != nil && indexInfo.IndexOption.ParserName != "" { + p := strings.ToLower(indexInfo.IndexOption.ParserName) + if p != "ngram" && p != "default" && p != "json" && p != "json_value" && p != "gojieba" { + return nil, nil, moerr.NewNotSupported(ctx.GetContext(), fmt.Sprintf("fulltext2 parser %s not supported", p)) + } + } + + // Validate INCLUDE columns (stored as actual values in the segment docmap for + // prefilter + coverage). vecColName is "" — fulltext2 has no single indexed vector + // column; the plugin's SupportedIncludeColumnTypes() gates the accepted types. + var includeCols []string + if indexInfo.IndexOption != nil && len(indexInfo.IndexOption.IncludeColumns) > 0 { + // The indexed text column(s) themselves cannot be INCLUDE columns: they are + // tokenized into the postings, and storing the raw text again in the docmap + // would bloat the index for no prefilter/coverage benefit. fulltext2 may index + // multiple columns (MATCH(body, title)), so exclude every KeyPart. Checked + // before the shared validator so an indexed column reports THIS reason rather + // than a type error (an indexed varchar column would otherwise pass the type + // gate, and an indexed text column would report "unsupported type text"). + indexed := make(map[string]struct{}, len(indexInfo.KeyParts)) + for _, kp := range indexInfo.KeyParts { + indexed[kp.ColName.ColName()] = struct{}{} + } + for _, c := range indexInfo.IndexOption.IncludeColumns { + if _, ok := indexed[c.ColName()]; ok { + return nil, nil, moerr.NewInvalidInput(ctx.GetContext(), + fmt.Sprintf("INCLUDE column '%s' cannot be an indexed text column", c.ColNameOrigin())) + } + } + if err := planplugin.ValidateIncludeColumns(ctx, indexInfo.IndexOption.IncludeColumns, colMap, "", pkeyName, + fulltext2CatalogHooks.SupportedIncludeColumnTypes()); err != nil { + return nil, nil, err + } + includeCols = make([]string, 0, len(indexInfo.IndexOption.IncludeColumns)) + for _, c := range indexInfo.IndexOption.IncludeColumns { + includeCols = append(includeCols, c.ColName()) + } + } + + params, err := buildFullText2Params(indexInfo) + if err != nil { + return nil, nil, err + } + indexParts := make([]string, 0, len(indexInfo.KeyParts)) + for _, keyPart := range indexInfo.KeyParts { + indexParts = append(indexParts, keyPart.ColName.ColName()) + } + var option *plan.IndexOption + var comment string + if indexInfo.IndexOption != nil { + if indexInfo.IndexOption.ParserName != "" { + option = &plan.IndexOption{ParserName: indexInfo.IndexOption.ParserName, NgramTokenSize: int32(3)} + } + comment = indexInfo.IndexOption.Comment + } + algo := tree.INDEX_TYPE_FULLTEXT2.ToString() + mkIdx := func(tblName, tblType string) *plan.IndexDef { + return &plan.IndexDef{ + Unique: false, + IndexName: indexInfo.Name, + IndexTableName: tblName, + IndexAlgo: algo, + IndexAlgoTableType: tblType, + IndexAlgoParams: params, + Parts: indexParts, + IncludedColumns: includeCols, // stored in the segment docmap, not a hidden table + TableExist: true, + Option: option, + Comment: comment, + } + } + + // storage (chunk) table. + storeName, err := util.BuildIndexTableName(ctx.GetContext(), false) + if err != nil { + return nil, nil, err + } + storeTbl := &plan.TableDef{ + Name: storeName, + TableType: catalog.FullText2Index_TblType_Storage, + Cols: []*plan.ColDef{ + {Name: catalog.FullText2Index_TblCol_Storage_Index_Id, Alg: plan.CompressType_Lz4, Typ: plan.Type{Id: int32(types.T_varchar), Width: 128}, Default: &plan.Default{}}, + {Name: catalog.FullText2Index_TblCol_Storage_Chunk_Id, Alg: plan.CompressType_Lz4, Typ: plan.Type{Id: int32(types.T_int64)}, Default: &plan.Default{}}, + {Name: catalog.FullText2Index_TblCol_Storage_Data, Alg: plan.CompressType_Lz4, Typ: plan.Type{Id: int32(types.T_blob), Width: 65536}, Default: &plan.Default{}}, + {Name: catalog.FullText2Index_TblCol_Storage_Tag, Alg: plan.CompressType_Lz4, Typ: plan.Type{Id: int32(types.T_int64)}, Default: &plan.Default{}}, + }, + } + storePk := planplugin.MakeHiddenColDefByName(catalog.CPrimaryKeyColName) + storePk.Alg = plan.CompressType_Lz4 + storePk.Primary = true + storeTbl.Cols = append(storeTbl.Cols, storePk) + storeTbl.Pkey = &plan.PrimaryKeyDef{ + Names: []string{catalog.FullText2Index_TblCol_Storage_Index_Id, catalog.FullText2Index_TblCol_Storage_Chunk_Id}, + PkeyColName: catalog.CPrimaryKeyColName, + CompPkeyCol: storeTbl.Cols[3], + } + storeTbl.Defs = append(storeTbl.Defs, &plan.TableDef_DefType{ + Def: &plan.TableDef_DefType_Properties{Properties: &plan.PropertiesDef{Properties: []*plan.Property{ + {Key: catalog.SystemRelAttr_Kind, Value: catalog.FullText2Index_TblType_Storage}, + }}}, + }) + + // metadata table. + metaName, err := util.BuildIndexTableName(ctx.GetContext(), false) + if err != nil { + return nil, nil, err + } + metaTbl := &plan.TableDef{ + Name: metaName, + TableType: catalog.FullText2Index_TblType_Metadata, + Cols: []*plan.ColDef{ + {Name: catalog.FullText2Index_TblCol_Metadata_Index_Id, Alg: plan.CompressType_Lz4, Typ: plan.Type{Id: int32(types.T_varchar), Width: 128}, Primary: true, Default: &plan.Default{}}, + {Name: catalog.FullText2Index_TblCol_Metadata_Timestamp, Alg: plan.CompressType_Lz4, Typ: plan.Type{Id: int32(types.T_int64)}, Default: &plan.Default{}}, + {Name: catalog.FullText2Index_TblCol_Metadata_Checksum, Alg: plan.CompressType_Lz4, Typ: plan.Type{Id: int32(types.T_varchar), Width: types.MaxVarcharLen}, Default: &plan.Default{}}, + {Name: catalog.FullText2Index_TblCol_Metadata_Filesize, Alg: plan.CompressType_Lz4, Typ: plan.Type{Id: int32(types.T_int64)}, Default: &plan.Default{}}, + {Name: catalog.FullText2Index_TblCol_Metadata_Recency, Alg: plan.CompressType_Lz4, Typ: plan.Type{Id: int32(types.T_int64)}, Default: &plan.Default{}}, + {Name: catalog.FullText2Index_TblCol_Metadata_Nrow, Alg: plan.CompressType_Lz4, Typ: plan.Type{Id: int32(types.T_int64)}, Default: &plan.Default{}}, + }, + } + metaTbl.Pkey = &plan.PrimaryKeyDef{ + Names: []string{catalog.FullText2Index_TblCol_Metadata_Index_Id}, + PkeyColName: catalog.FullText2Index_TblCol_Metadata_Index_Id, + } + metaTbl.Defs = append(metaTbl.Defs, &plan.TableDef_DefType{ + Def: &plan.TableDef_DefType_Properties{Properties: &plan.PropertiesDef{Properties: []*plan.Property{ + {Key: catalog.SystemRelAttr_Kind, Value: catalog.FullText2Index_TblType_Metadata}, + }}}, + }) + + return []*plan.IndexDef{ + mkIdx(storeName, catalog.FullText2Index_TblType_Storage), + mkIdx(metaName, catalog.FullText2Index_TblType_Metadata), + }, []*plan.TableDef{storeTbl, metaTbl}, nil +} + +// buildFullText2Params records the parser name and max_index_capacity in +// algo_params (fulltext2 is always-async by algo, so no async/version params). +func buildFullText2Params(idx *tree.FullTextIndex) (string, error) { + res := make(map[string]string) + // Always record the parser (default "ngram" when unspecified), so algo_params + // is never an empty string — every consumer IndexParamsStringToMaps it (incl. + // the ALTER REINDEX flow in ddl.go, which errors on ""), and it self-documents + // the parser. Mirrors classic fulltext storing "parser" when set. + parser := "ngram" + if idx.IndexOption != nil && idx.IndexOption.ParserName != "" { + parser = strings.ToLower(idx.IndexOption.ParserName) + } + res["parser"] = parser + // Persist INCLUDE column names in algo_params so they survive catalog reload (the + // first-class IndexDef.IncludedColumns is repopulated from here via + // indexDefIncludedColumns / parseIncludedColumnsFromParams). Mirrors ivfpq's + // ParamsFromTree. Not rendered by IndexParamsToStringList (SHOW CREATE uses the field). + if idx.IndexOption != nil && len(idx.IndexOption.IncludeColumns) > 0 { + names := make([]string, 0, len(idx.IndexOption.IncludeColumns)) + for _, c := range idx.IndexOption.IncludeColumns { + if n := c.ColName(); n != "" { + names = append(names, n) + } + } + if len(names) > 0 { + marshaled, err := catalog.MarshalIncludeColumnsValue(names) + if err != nil { + return "", err + } + res[catalog.IncludedColumns] = marshaled + } + } + if idx.IndexOption != nil { + if idx.IndexOption.MaxIndexCapacity > 0 { + res[catalog.IndexAlgoParamMaxIndexCapacity] = strconv.FormatInt(idx.IndexOption.MaxIndexCapacity, 10) + } + // max_postings_capacity: postings (term occurrences) per built segment — the + // memory-correlated seal (a long-doc corpus seals on postings, not docs). Absence + // ⇒ DefaultPostingCapacity in the build paths. + if idx.IndexOption.MaxPostingsCapacity > 0 { + res[catalog.IndexAlgoParamMaxPostingsCapacity] = strconv.FormatInt(idx.IndexOption.MaxPostingsCapacity, 10) + } + // position_free ⇒ build without the positional payload (bag-of-words retrieval + // only). Recorded only when TRUE; absence ⇒ positional (phrase-capable). Only + // gojieba (word tokens) is meaningful position-free: ngram/json emit overlapping + // trigrams/values whose bag-of-words (IN BM25 MODE) result is noise — those need + // positions to reconstruct the term, so POSITION_FREE is rejected for them. + if idx.IndexOption.PositionFree { + if parser != "gojieba" { + return "", moerr.NewInvalidInputNoCtxf( + "fulltext2 POSITION_FREE requires WITH PARSER gojieba (got %q): a position-free index answers only IN BM25 MODE bag-of-words, and ngram/json tokens are meaningless without positions", parser) + } + res[catalog.IndexAlgoParamPositionFree] = "true" + } + // auto_update + day/hour/second drive the idxcron scheduled compaction + // cadence (read from algo_params by the idxcron executor). Mirrors bm25's + // ParamsFromTree. + if idx.IndexOption.AutoUpdate { + res[catalog.AutoUpdate] = "true" + } + if idx.IndexOption.Day > 0 { + res[catalog.Day] = strconv.FormatInt(idx.IndexOption.Day, 10) + } + if idx.IndexOption.Hour > 0 { + res[catalog.Hour] = strconv.FormatInt(idx.IndexOption.Hour, 10) + } + if idx.IndexOption.Second > 0 { + res[catalog.Second] = strconv.FormatInt(idx.IndexOption.Second, 10) + } + } + return catalog.IndexParamsMapToJsonString(res) +} diff --git a/pkg/fulltext2/plugin/plan/tablefunc.go b/pkg/fulltext2/plugin/plan/tablefunc.go new file mode 100644 index 0000000000000..e8c26c02b635b --- /dev/null +++ b/pkg/fulltext2/plugin/plan/tablefunc.go @@ -0,0 +1,159 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plan + +import ( + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/types" + planplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/plan" + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" +) + +// FullText2SearchFuncName is the search TVF: MATCH over a fulltext2 index → +// ranked (doc_id, score). Registered into the plan-side TVF dispatch like the +// vector plugins' *_search. Args: [param, TableConfig(JSON), pattern]. +const FullText2SearchFuncName = "fulltext2_search" + +// Must match the executor (fulltext2_search.go): doc_id via AppendAny (the pk's own +// type — int/varchar/uuid/…, so T_any, NOT T_int64), score via AppendFixed[float32] +// (T_float32/Width 4, NOT T_float64 — an 8-byte score column read 4-byte writes as +// garbage). Same shape as the MATCH-rewrite path's ftIndexColdefs; a direct +// `FROM fulltext2_search(...)` call uses THESE defs, so they must be correct too. +var fulltext2SearchColDefs = []*plan.ColDef{ + {Name: catalog.FullText2Search_OutCol_DocId, Typ: plan.Type{Id: int32(types.T_any)}}, + {Name: catalog.FullText2Search_OutCol_Score, Typ: plan.Type{Id: int32(types.T_float32), Width: 4}}, +} + +// FullText2CreateFuncName is the build TVF: CROSS APPLY'd over the source table +// at CREATE INDEX / REINDEX time, it tokenizes each row (datalink/json/parser +// resolved in execution), builds capacity-bounded tag=0 base segments, and +// persists them. Args: [param, TableConfig(JSON), pk, cols...]. Output: a single +// discarded status row. +const FullText2CreateFuncName = "fulltext2_create" + +var fulltext2CreateColDefs = []*plan.ColDef{ + {Name: "status", Typ: plan.Type{Id: int32(types.T_int32), Width: 4}}, +} + +// FullText2CompactFuncName is the standalone MERGE-compaction TVF: +// `fulltext2_compact(db, store, meta, capacity[, position_free[, posting_capacity]])`. +// No driving table; args are passed as-is (no leading param strip, unlike +// search/create). Output: a single `live_docs` count row. +const FullText2CompactFuncName = "fulltext2_compact" + +var fulltext2CompactColDefs = []*plan.ColDef{ + {Name: "live_docs", Typ: plan.Type{Id: int32(types.T_int64), NotNullable: true}}, +} + +func init() { + planplugin.RegisterTableFunc(FullText2SearchFuncName, buildFullText2Search) + planplugin.RegisterTableFunc(FullText2CreateFuncName, buildFullText2Create) + planplugin.RegisterTableFunc(FullText2CompactFuncName, buildFullText2Compact) +} + +// buildFullText2Create — arg list: [param, TableConfig(JSON), pk, cols...]. +func buildFullText2Create(pb planplugin.PlanBuilder, tbl *tree.TableFunction, ctx planplugin.BindContext, exprs []*plan.Expr, children []int32) (int32, error) { + if len(exprs) < 4 { + return 0, moerr.NewInvalidInput(pb.GetContext(), "fulltext2_create: invalid number of arguments (NARGS < 4)") + } + colDefs := planplugin.DeepCopyColDefList(fulltext2CreateColDefs) + params, err := getFullText2Params(pb, tbl.Func) + if err != nil { + return 0, err + } + exprs = exprs[1:] + + node := &plan.Node{ + NodeType: plan.Node_FUNCTION_SCAN, + Stats: &plan.Stats{}, + TableDef: &plan.TableDef{ + TableType: "func_table", + TblFunc: &plan.TableFunction{ + Name: FullText2CreateFuncName, + Param: []byte(params), + IsSingle: true, + }, + Cols: colDefs, + }, + BindingTags: []int32{pb.GenNewBindTag()}, + TblFuncExprList: exprs, + Children: children, + } + return pb.AppendNode(node, ctx), nil +} + +// buildFullText2Compact — the standalone `fulltext2_compact(db, store, meta, +// capacity, ...)` MERGE table function. No driving table; the varchar args are +// passed through as-is (no leading param strip). Mirrors the search/create +// registration so the plan-side dispatch routes through the plugin registry +// (query_builder.go) instead of a hardcoded switch case. +func buildFullText2Compact(pb planplugin.PlanBuilder, tbl *tree.TableFunction, ctx planplugin.BindContext, exprs []*plan.Expr, children []int32) (int32, error) { + colDefs := planplugin.DeepCopyColDefList(fulltext2CompactColDefs) + node := &plan.Node{ + NodeType: plan.Node_FUNCTION_SCAN, + Stats: &plan.Stats{}, + TableDef: &plan.TableDef{ + TableType: "func_table", + TblFunc: &plan.TableFunction{ + Name: FullText2CompactFuncName, + }, + Cols: colDefs, + }, + BindingTags: []int32{pb.GenNewBindTag()}, + Children: children, + TblFuncExprList: exprs, + } + return pb.AppendNode(node, ctx), nil +} + +func getFullText2Params(pb planplugin.PlanBuilder, fn *tree.FuncExpr) (string, error) { + if _, ok := fn.Exprs[0].(*tree.NumVal); ok { + return fn.Exprs[0].String(), nil + } + return "", moerr.NewNoConfig(pb.GetContext(), "first parameter must be string") +} + +// buildFullText2Search — arg list: [param, TableConfig(JSON), pattern, mode]. +// param is stripped; the exec side (fulltext2_search.go) reads [cfg, pattern, mode]. +func buildFullText2Search(pb planplugin.PlanBuilder, tbl *tree.TableFunction, ctx planplugin.BindContext, exprs []*plan.Expr, children []int32) (int32, error) { + if len(exprs) != 4 { + return 0, moerr.NewInvalidInput(pb.GetContext(), "fulltext2_search: invalid number of arguments (NARGS != 4)") + } + colDefs := planplugin.DeepCopyColDefList(fulltext2SearchColDefs) + params, err := getFullText2Params(pb, tbl.Func) + if err != nil { + return 0, err + } + exprs = exprs[1:] + + node := &plan.Node{ + NodeType: plan.Node_FUNCTION_SCAN, + Stats: &plan.Stats{}, + TableDef: &plan.TableDef{ + TableType: "func_table", + TblFunc: &plan.TableFunction{ + Name: FullText2SearchFuncName, + Param: []byte(params), + }, + Cols: colDefs, + }, + BindingTags: []int32{pb.GenNewBindTag()}, + TblFuncExprList: exprs, + Children: children, + } + return pb.AppendNode(node, ctx), nil +} diff --git a/pkg/fulltext2/plugin/plugin.go b/pkg/fulltext2/plugin/plugin.go new file mode 100644 index 0000000000000..7df5102073c4a --- /dev/null +++ b/pkg/fulltext2/plugin/plugin.go @@ -0,0 +1,66 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package plugin is the fulltext2 index plugin registration point. +// +// fulltext2 is the WAND positional fulltext engine (NL phrase + boolean, TF-IDF +// and BM25), a DISTINCT algo from classic fulltext so its plugin hooks stay +// static (bm25-shaped: storage+metadata hidden tables, always-async CDC). It is +// created via `CREATE FULLTEXT2 INDEX name ON t(col) [WITH PARSER ]` (parsed +// to *tree.FullTextIndex with IsV2, dispatched through BuildFullTextIndexDefs) +// and queried via `MATCH(col) AGAINST('query')`. The engine lives in pkg/fulltext2. +package plugin + +import ( + "github.com/matrixorigin/matrixone/pkg/catalog" + indexplugin "github.com/matrixorigin/matrixone/pkg/indexplugin" + catalogplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/catalog" + compileplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/compile" + idxcronplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/idxcron" + planplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/plan" + + ft2compile "github.com/matrixorigin/matrixone/pkg/fulltext2/plugin/compile" + ft2idxcron "github.com/matrixorigin/matrixone/pkg/fulltext2/plugin/idxcron" + ft2plan "github.com/matrixorigin/matrixone/pkg/fulltext2/plugin/plan" + ft2runtime "github.com/matrixorigin/matrixone/pkg/fulltext2/plugin/runtime" +) + +// Plugin is the fulltext2 AlgoPlugin. +type Plugin struct { + catalogHooks catalogplugin.Hooks + compileHooks compileplugin.Hooks + planHooks planplugin.Hooks + idxcronHooks idxcronplugin.Hooks +} + +func New() *Plugin { + return &Plugin{ + catalogHooks: ft2runtime.CatalogHooks{}, + compileHooks: ft2compile.Hooks{}, + planHooks: ft2plan.Hooks{}, + idxcronHooks: ft2idxcron.Hooks{}, + } +} + +func (*Plugin) Algo() string { return catalog.MoIndexFullText2Algo.ToString() } +func (p *Plugin) Catalog() catalogplugin.Hooks { return p.catalogHooks } +func (p *Plugin) Compile() compileplugin.Hooks { return p.compileHooks } +func (p *Plugin) Plan() planplugin.Hooks { return p.planHooks } +func (p *Plugin) Idxcron() idxcronplugin.Hooks { return p.idxcronHooks } + +var _ indexplugin.AlgoPlugin = (*Plugin)(nil) + +// init registers fulltext2. CREATE FULLTEXT2 INDEX now creates the hidden tables; +// build-from-source / MATCH search / CDC / reindex land in the following steps. +func init() { indexplugin.Register(New()) } diff --git a/pkg/fulltext2/plugin/runtime/runtime.go b/pkg/fulltext2/plugin/runtime/runtime.go new file mode 100644 index 0000000000000..3001c072ad9c7 --- /dev/null +++ b/pkg/fulltext2/plugin/runtime/runtime.go @@ -0,0 +1,145 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package runtime holds the fulltext2 index's catalog-side metadata. fulltext2 +// is a DISTINCT algo (CREATE FULLTEXT2 INDEX, the WAND positional engine), so — +// unlike a version-router — every hook here is a clean STATIC answer: the +// storage+metadata hidden-table layout (bm25-shaped), always-async CDC +// maintenance, and index_id-keyed clone/restore. +package runtime + +import ( + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/types" + catalogplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/catalog" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" +) + +var _ catalogplugin.Hooks = CatalogHooks{} + +// CatalogHooks implements plugin/catalog.Hooks for fulltext2. +type CatalogHooks struct{} + +// HiddenTableTypes — a chunked segment store + a metadata table (no postings +// table; segments are built from source and CDC-maintained). +func (CatalogHooks) HiddenTableTypes() []string { + return []string{ + catalog.FullText2Index_TblType_Storage, + catalog.FullText2Index_TblType_Metadata, + } +} + +// ShouldTruncateHiddenTable — both hidden tables are derived from source rows and +// reset on TRUNCATE. +func (CatalogHooks) ShouldTruncateHiddenTable(_ string) bool { return true } + +// AlterTableCloneBehavior — both hidden tables are empty at CREATE-INDEX time +// (segments land via the build/CDC), so the whole index is skipped when cloning +// (rebuilt from source + CDC on the copy). +func (CatalogHooks) AlterTableCloneBehavior() catalogplugin.AlterTableCloneBehavior { + return catalogplugin.AlterTableCloneBehavior{SkipWholeIndex: true} +} + +// RestoreBehavior — the storage+metadata rows are index_id-keyed, so a clone +// overwrites rather than appends: no delete-before-clone. The compile hook's +// RestoreInitSQL registers CDC from the post-clone TS. +func (CatalogHooks) RestoreBehavior() catalogplugin.RestoreBehavior { + return catalogplugin.RestoreBehavior{} +} + +// BuildSessionVars — fulltext2 captures no session vars into algo_params. +// BuildSessionVars — fulltext2 pins lower_case_table_names into +// algo_params.session_vars so the background reindex folds identifiers in the +// rebuild SQL exactly as the user built under (its VALUE matters). sql_mode is +// intentionally omitted: a background reindex runs permissive and gets sql_mode +// defaulted at the reindex hook (sqlexec.Metadata.ResolveVariableWithSessionDefaults), +// not captured. Mirrors the vector-index plugins' capture list, and matches this +// plugin's IdxcronMetadata capture. +func (CatalogHooks) BuildSessionVars() []string { + return []string{"lower_case_table_names"} +} + +// DefaultOptions — no statement-level option JSON required. +func (CatalogHooks) DefaultOptions() map[string]string { return nil } + +// Fulltext2IndexFlag gates CREATE FULLTEXT2 INDEX behind the +// experimental_fulltext2_index session var (mirrors bm25's Bm25IndexFlag / +// HNSW's HnswIndexFlag). Both the framework gate (pkg/sql/compile/util.go, via +// ExperimentalFlag()) and the per-plugin HandleCreateIndex gate reference it. +const Fulltext2IndexFlag = "experimental_fulltext2_index" + +// ExperimentalFlag — CREATE FULLTEXT2 INDEX is gated by experimental_fulltext2_index. +func (CatalogHooks) ExperimentalFlag() string { return Fulltext2IndexFlag } + +// SupportedVectorTypes / SupportedOpTypes — fulltext2 has no vector/op-type concept. +func (CatalogHooks) SupportedVectorTypes() []types.T { return nil } +func (CatalogHooks) IsVectorIndex() bool { return false } // fulltext-family, not ANN +func (CatalogHooks) SupportedOpTypes() map[string]string { return nil } + +// SupportedIncludeColumnTypes lists the scalar column types accepted as INCLUDE columns — +// stored as their ACTUAL value in the segment docmap (not a hash), so both prefiltering and +// covering projection work. The set is EXACTLY what the segment pk codec +// (pkcodec.go encodePk/decodePk) already round-trips, so include values reuse that codec +// verbatim: the integer family (dense fixed-width) and varchar/char (varlena [len][value]). +// float32/64, date/decimal/text are intentionally deferred — pkcodec has no float case and +// widening the set is a codec extension, not a plan change. +func (CatalogHooks) SupportedIncludeColumnTypes() []types.T { + return []types.T{ + types.T_int8, types.T_int16, types.T_int32, types.T_int64, + types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, + types.T_varchar, types.T_char, + } +} + +// SupportedPrimaryKeyTypes lists EXACTLY the pk types the segment codec (pkcodec.go +// encodePk/decodePk) can round-trip. It is deliberately NOT nil ("any type"): a nil let +// a FLOAT / DOUBLE (or any other unsupported) primary key pass CREATE INDEX validation +// and then fail at build / CDC with "unsupported pk type" — an accepted DDL that dies at +// runtime. Keep this in lockstep with encodePk's switch so DDL validation and the codec +// agree. +func (CatalogHooks) SupportedPrimaryKeyTypes() []types.T { + return []types.T{ + types.T_int8, types.T_int16, types.T_int32, types.T_int64, + types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, + types.T_bit, types.T_decimal64, types.T_decimal128, + types.T_date, types.T_datetime, types.T_time, types.T_timestamp, + types.T_char, types.T_varchar, types.T_text, + types.T_binary, types.T_varbinary, types.T_blob, types.T_json, + types.T_datalink, types.T_uuid, + } +} + +// ValidQuantization — no quantization. +func (CatalogHooks) ValidQuantization(_, _ string) error { return nil } + +// ParamsFromTree — fulltext2 parses to *tree.FullTextIndex (handled by the plan +// hook's BuildFullTextIndexDefs), not *tree.Index, so this is never reached. +func (CatalogHooks) ParamsFromTree(_ *tree.Index) (map[string]string, error) { + return nil, moerr.NewNotSupportedNoCtx("fulltext2 index parses to *tree.FullTextIndex, not *tree.Index") +} + +// SyncDescriptor — fulltext2 is ALWAYS async (CDC-maintained). UsesCDC drives the +// ISCP consumer (RunFulltext2); the Idxcron fields drive the scheduled MERGE +// compaction (ALTER … REINDEX … FULLTEXT2 MERGE FORCE_SYNC). Mirrors bm25. +func (CatalogHooks) SyncDescriptor() catalogplugin.SyncDescriptor { + return catalogplugin.SyncDescriptor{ + UsesCDC: true, + SinkerType: catalogplugin.SinkerType_IndexSync, + AlwaysAsync: true, + IdxcronAction: "fulltext2_reindex", + IdxcronAlgoToken: "FULLTEXT2", + IdxcronReindexOption: "MERGE", + } +} diff --git a/pkg/fulltext2/pool.go b/pkg/fulltext2/pool.go new file mode 100644 index 0000000000000..aaba6a865ab6b --- /dev/null +++ b/pkg/fulltext2/pool.go @@ -0,0 +1,121 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "sync" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/vectorindex" +) + +// Per-query cursor block buffers are BlockSize-sized and short-lived: buildWandIters +// makes one pair per term and newPhraseCursor one triple per slot, then they die when +// the search returns. Under concurrent query load that churn outruns the GC, so the +// buffers are recycled through sync.Pool instead of freshly allocated per query. Each +// buffer is Get by exactly one (single-goroutine) search and Put back on return; the +// returned Result/docTf values copy out pk+score and never alias the buffers, so there +// is no use-after-Put. Reused buffers may hold stale entries past the current block's +// blen, but cursors only ever read within [0,blen), so staleness is invisible. + +// wandBuf is a wandIter's decoded-block buffer pair (docIDs + capped tfs). +type wandBuf struct { + docs []int64 // cap BlockSize + tfs []uint8 // cap BlockSize +} + +var wandBufPool = sync.Pool{ + New: func() any { + return &wandBuf{docs: make([]int64, BlockSize), tfs: make([]uint8, BlockSize)} + }, +} + +func getWandBuf() *wandBuf { return wandBufPool.Get().(*wandBuf) } + +// releaseWandIters returns every cursor's block buffer to the pool. Safe to defer right +// after buildWandIters (a nil/empty slice is a no-op) and idempotent per cursor (buf is +// cleared so a double release cannot double-Put the same buffer). +func releaseWandIters(iters []*wandIter) { + for _, it := range iters { + if it == nil || it.buf == nil { + continue + } + wandBufPool.Put(it.buf) + it.buf = nil + it.bDocs = nil + it.bTfs = nil + } +} + +// phraseBuf is a phraseCursor's decoded-block buffers: docIDs + per-doc positions + a +// tf scratch (phrase ignores tf but fillBlock still writes it). +type phraseBuf struct { + docs []int64 + pos [][]int32 + tfs []uint8 +} + +var phraseBufPool = sync.Pool{ + New: func() any { + return &phraseBuf{ + docs: make([]int64, BlockSize), + pos: make([][]int32, BlockSize), + tfs: make([]uint8, BlockSize), + } + }, +} + +func getPhraseBuf() *phraseBuf { return phraseBufPool.Get().(*phraseBuf) } + +// releasePhraseCursors returns every cursor's block buffers to the pool; skips nil +// entries so it is safe to defer over a partially-built cursors slice (matchPhraseCursor +// bails out mid-build when a slot's term is absent). +func releasePhraseCursors(cursors []*phraseCursor) { + for _, c := range cursors { + if c == nil || c.buf == nil { + continue + } + phraseBufPool.Put(c.buf) + c.buf = nil + c.bDocs = nil + c.bPos = nil + c.tfbuf = nil + } +} + +// The no-LIMIT streaming path emits one vectorindex.ColumnBuffer per streamBatch rows. +// Its Data backing is recycled here so a million-row result does not allocate a fresh +// buffer per batch. GC-managed: a buffer never Put back (an aborted/drained query) is +// just reclaimed — a missed reuse, not a leak. The struct lives in vectorindex (the Emit +// contract references it); the POOLING POLICY lives here with its only user, fulltext2. +var columnBufferPool = sync.Pool{New: func() any { return &vectorindex.ColumnBuffer{} }} + +// GetColumnBuffer returns a reset ColumnBuffer of pk type t. The streaming producer Gets +// one per batch; the consumer PutColumnBuffers it back once it has copied Data out. +func GetColumnBuffer(t types.T) *vectorindex.ColumnBuffer { + k := columnBufferPool.Get().(*vectorindex.ColumnBuffer) + k.Type = t + k.Reset() + return k +} + +// PutColumnBuffer returns a ColumnBuffer to the pool. Call only after the batch is fully +// consumed (its Data copied out); the buffer must not be referenced afterward. Exported +// because the consumer (pkg/sql/colexec/table_function) returns the batch it received. +func PutColumnBuffer(k *vectorindex.ColumnBuffer) { + if k != nil { + columnBufferPool.Put(k) + } +} diff --git a/pkg/fulltext2/query.go b/pkg/fulltext2/query.go new file mode 100644 index 0000000000000..2e0925183797c --- /dev/null +++ b/pkg/fulltext2/query.go @@ -0,0 +1,678 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/bytejson" + "github.com/matrixorigin/matrixone/pkg/fulltext" + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" +) + +// BooleanTreeString parses a boolean-mode query and returns its parse tree as an +// S-expression (e.g. `+Matrix -Origin` → `(+ (text 0 matrix)) (- (text 1 origin))`). +// A debug aid: it reuses classic fulltext's PatternToString so the printed shape +// is identical to pkg/fulltext (fulltext2's boolean structure is parity with it). +func BooleanTreeString(query string) (string, error) { + return fulltext.PatternToString(query, int64(tree.FULLTEXT_BOOLEAN)) +} + +// BooleanTreeStringWithPosition is BooleanTreeString with each leaf's byte +// position included: `(text )` — mirrors classic +// fulltext's PatternToStringWithPosition (used for phrase-position debugging). +func BooleanTreeStringWithPosition(query string) (string, error) { + return fulltext.PatternToStringWithPosition(query, int64(tree.FULLTEXT_BOOLEAN)) +} + +// NLTreeString parses a natural-language query and returns its parse tree (the +// ngram/word patterns AGAINST(...) expands to), reusing classic fulltext's +// PatternToString. Debug aid for the NL side. +func NLTreeString(query string) (string, error) { + return fulltext.PatternToString(query, int64(tree.FULLTEXT_NL)) +} + +// NLTreeStringWithPosition is NLTreeString with each leaf's byte position: +// `(text )` — mirrors PatternToStringWithPosition. +func NLTreeStringWithPosition(query string) (string, error) { + return fulltext.PatternToStringWithPosition(query, int64(tree.FULLTEXT_NL)) +} + +// Parser selects how documents and queries are tokenized. fulltext2 mirrors +// classic fulltext's parser set: +// - default / ngram : SimpleTokenizer — whole words for Latin, sliding +// 3-grams for CJK (the classic build-side ngram tokenizer). +// - gojieba : dictionary word segmentation (CJK + Latin) via jieba. +// - json / json_value: json values are flattened to text, then tokenized as +// ngram; the query pattern is plain text (also ngram). +const ( + ParserDefault = "default" + ParserNgram = "ngram" + ParserGojieba = "gojieba" + ParserJSON = "json" + ParserJSONValue = "json_value" + + ngramSize = 3 +) + +// normalizeParser lowercases/trims and maps "" to the default parser. +func normalizeParser(p string) string { + p = strings.ToLower(strings.TrimSpace(p)) + if p == "" { + return ParserDefault + } + return p +} + +// hasCJK reports whether s contains a CJK-class rune — a non-Latin, non-breaker +// rune, matching SimpleTokenizer's own CJK classification (isLatin: rune<0x7FF). +// A bare CJK boolean operand is routed to the positional (byte-offset) phrase path +// so a short compound (e.g. 中文, a 2-char bigram absent as a whole token from an +// ngram-3 index) matches via a word* prefix rather than an empty exact-term lookup. +func hasCJK(s string) bool { + for _, r := range s { + if r > 0x7FF && !unicode.IsPunct(r) && !unicode.IsSpace(r) { + return true + } + } + return false +} + +// DocTokenizer returns the tokenizer that indexes DOCUMENTS under parser. +// json/json_value documents must be flattened first (FlattenJSON); their +// flattened text is tokenized as ngram. +func DocTokenizer(parser string) (tokenizer.Tokenizer, error) { + switch normalizeParser(parser) { + case ParserDefault, ParserNgram, ParserJSON, ParserJSONValue: + return tokenizer.NewSimpleTokenizer(), nil + case ParserGojieba: + return tokenizer.SharedJiebaTokenizer(false) + default: + return nil, moerr.NewInternalErrorNoCtx("fulltext2: invalid parser " + parser) + } +} + +// queryTokenizer returns the tokenizer for a QUERY pattern under parser. It is +// SimpleTokenizer for every parser except gojieba — a json index is queried with +// plain text, so json uses the ngram tokenizer at query time. +func queryTokenizer(parser string) (tokenizer.Tokenizer, error) { + if normalizeParser(parser) == ParserGojieba { + return tokenizer.SharedJiebaTokenizer(false) + } + return tokenizer.NewSimpleTokenizer(), nil +} + +// flattenJSONValues parses raw as a TEXT JSON document and returns its leaf +// values joined by spaces. Convenience for text/varchar json columns and tests. +func flattenJSONValues(raw []byte) ([]byte, error) { + return FlattenJSON(raw, false) +} + +// FlattenJSON returns a JSON document's leaf values joined by spaces, so an ngram +// tokenizer can index them as ordinary text. binary selects the encoding: true +// for a T_json column's stored bytes (bytejson.Unmarshal), false for text/varchar +// json (bytejson.ParseFromString). +func FlattenJSON(raw []byte, binary bool) ([]byte, error) { + var bj bytejson.ByteJson + var err error + if binary { + err = bj.Unmarshal(raw) + } else { + bj, err = bytejson.ParseFromString(string(raw)) + } + if err != nil { + return nil, err + } + return flattenBJ(bj), nil +} + +// flattenBJ joins a parsed json document's leaf values with spaces (shared by +// FlattenJSON and FlattenJSONColumn). +func flattenBJ(bj bytejson.ByteJson) []byte { + var b strings.Builder + for tk := range bj.TokenizeValue(false) { + n := int(tk.TokenBytes[0]) + if b.Len() > 0 { + b.WriteByte(' ') + } + b.Write(tk.TokenBytes[1 : 1+n]) + } + return []byte(b.String()) +} + +// FlattenJSONColumn flattens ONE indexed column's value to its space-joined leaf +// values for a json parser — the CDC-write-side analogue of the create-TVF's +// per-column flatten (rowTerms). A T_json column arrives as an already-parsed +// bytejson.ByteJson (types.DecodeJson, repr-independent); a text/varchar json column +// arrives as a string/[]byte of json TEXT. Doing this per column (vs joining raw +// columns then flattening the blob once) is what makes CDC json tokens match CREATE: +// the old joined-then-flattened path yielded ZERO tokens for T_json and for +// multi-column json, silently making CDC-inserted rows unsearchable. +func FlattenJSONColumn(v any) ([]byte, error) { + switch t := v.(type) { + case bytejson.ByteJson: + return flattenBJ(t), nil + case []byte: + return FlattenJSON(t, false) + case string: + return FlattenJSON([]byte(t), false) + default: + return nil, nil + } +} + +// IsJSONParser reports whether parser is one of the json variants. +func IsJSONParser(parser string) bool { + p := normalizeParser(parser) + return p == ParserJSON || p == ParserJSONValue +} + +// IsJSONValueParser reports whether parser is the json_value variant. json_value +// indexes each json leaf value as ONE whole atomic token and matches it EXACTLY +// (classic fulltext: tokenize.go stores the whole value, sql.go:173 queries +// `word = value`) — distinct from json, which ngrams over the flattened values. +func IsJSONValueParser(parser string) bool { + return normalizeParser(parser) == ParserJSONValue +} + +// flattenBJLines joins a parsed json document's leaf values with NEWLINES (not +// spaces): the json_value parser keeps each whole value as a separable atomic token, +// so a '\n' separator lets JSONValueTokenize recover the values (a space join would +// fuse values and lose the boundaries — and values may themselves contain spaces). +func flattenBJLines(bj bytejson.ByteJson) []byte { + var b strings.Builder + for tk := range bj.TokenizeValue(false) { + n := int(tk.TokenBytes[0]) + if b.Len() > 0 { + b.WriteByte('\n') + } + b.Write(tk.TokenBytes[1 : 1+n]) + } + return []byte(b.String()) +} + +// FlattenJSONLines is FlattenJSON for the json_value parser: leaf values joined by +// '\n' so each stays a separable whole token. binary selects the encoding as in +// FlattenJSON. +func FlattenJSONLines(raw []byte, binary bool) ([]byte, error) { + var bj bytejson.ByteJson + var err error + if binary { + err = bj.Unmarshal(raw) + } else { + bj, err = bytejson.ParseFromString(string(raw)) + } + if err != nil { + return nil, err + } + return flattenBJLines(bj), nil +} + +// FlattenJSONValueColumn is FlattenJSONColumn for the json_value parser: ONE indexed +// column's leaf values joined by '\n' (the CDC-write-side analogue of the create-TVF's +// per-column flatten). Mirrors FlattenJSONColumn's input-type handling. +func FlattenJSONValueColumn(v any) ([]byte, error) { + switch t := v.(type) { + case bytejson.ByteJson: + return flattenBJLines(t), nil + case []byte: + return FlattenJSONLines(t, false) + case string: + return FlattenJSONLines([]byte(t), false) + default: + return nil, nil + } +} + +// JSONValueTokenize splits '\n'-joined json values into whole (word, byte-position) +// tokens — the json_value parser's atomic-value tokenization: no ngram, no +// case-folding, punctuation kept, so `red..blue--brown` and `中文學習教材` each index +// as ONE token that a query matches exactly (classic fulltext json_value parity). +func JSONValueTokenize(text []byte) []WordPos { + var words []WordPos + for i := 0; i < len(text); { + j := i + for j < len(text) && text[j] != '\n' { + j++ + } + if j > i { + words = append(words, WordPos{Word: string(text[i:j]), Pos: int32(i)}) + } + i = j + 1 + } + return words +} + +// CdcTokenizer returns the parser-aware tokenize closure the CDC consumer feeds to +// TailBuilder — ordered words per parser. For a json parser the CDC WRITER +// (Fulltext2SqlWriter.rowText) already flattens each json column to its values, +// binary-aware and PER COLUMN, exactly as the create-TVF's rowTerms does — so the CDC +// text is plain flattened text here and is tokenized as ngram, NOT flattened again. +// (The old form flattened the whole joined multi-column blob once with binary=false, +// which produced zero tokens for a T_json column and mis-parsed multi-column json — so +// CDC-inserted json rows were unsearchable.) +func CdcTokenizer(parser string) (func(string) []WordPos, error) { + if IsJSONValueParser(parser) { + // json_value: the CDC writer (rowText) already flattened each json column to its + // '\n'-joined whole values, so here we just recover them as atomic tokens — NOT + // ngram — matching the create/base build. + return func(text string) []WordPos { return JSONValueTokenize([]byte(text)) }, nil + } + tok, err := DocTokenizer(parser) + if err != nil { + return nil, err + } + return func(text string) []WordPos { return tokenizeWords(tok, []byte(text)) }, nil +} + +// WordPos is a tokenized word with its BYTE position in the source text — carried +// through the CDC/merge build so positional phrase queries match by byte offset. +type WordPos struct { + Word string + Pos int32 +} + +// tokenizeWords flattens a tokenizer stream into ordered (word, byte-position) pairs. +func tokenizeWords(tok tokenizer.Tokenizer, text []byte) []WordPos { + // token count is unknown up front; estimate from input length to avoid regrowth. + words := make([]WordPos, 0, len(text)/4) + for tk, err := range tok.Tokenize(text) { + if err != nil { + break + } + words = append(words, WordPos{tokenWord(tk), tk.BytePos}) + } + return words +} + +// BuildSegmentFromDocsParser builds a segment from docs under parser, selecting +// the right document tokenizer and flattening json docs first. It is the +// parser-aware build entry (compile-side build-from-source uses it). +func BuildSegmentFromDocsParser(id string, pkType int32, docs []Doc, parser string) (*Segment, error) { + p := normalizeParser(parser) + if p == ParserJSONValue { + // json_value: each leaf value is one whole atomic token (no ngram). Build the + // tokenized docs directly from the '\n'-joined values so build matches query. + tds := make([]TokenizedDoc, 0, len(docs)) + for _, d := range docs { + lines, ferr := FlattenJSONLines(d.Text, false) + if ferr != nil { + return nil, ferr + } + td := TokenizedDoc{Pk: d.Pk} + for _, wp := range JSONValueTokenize(lines) { + td.Terms = append(td.Terms, wp.Word) + td.Positions = append(td.Positions, wp.Pos) + } + tds = append(tds, td) + } + return BuildSegmentFromTokenized(id, pkType, tds) + } + tok, err := DocTokenizer(parser) + if err != nil { + return nil, err + } + if p == ParserJSON { + fdocs := make([]Doc, 0, len(docs)) + for _, d := range docs { + ft, ferr := flattenJSONValues(d.Text) + if ferr != nil { + return nil, ferr + } + fdocs = append(fdocs, Doc{Pk: d.Pk, Text: ft}) + } + docs = fdocs + } + return BuildSegmentFromDocs(id, pkType, docs, tok) +} + +// SearchQuery runs a MATCH query over the index under parser and mode. It is the +// single query entry the fulltext2 search TVF calls. +// +// NL mode (boolean=false): an EXACT ORDERED PHRASE for EVERY parser — the terms +// must occur contiguously and in order (classic fulltext NL semantics). There is +// NO bag-of-words path: a CJK query is matched positionally by BYTE offset over +// classic fulltext's redundant-ngram-removed slots (full trigrams as TEXT, short +// tails as word* prefixes), so the phrase is robust despite overlapping/tail 3-grams. +// +// Boolean mode (boolean=true): standard +/-/>/= 3 chars), so matching is exact-term lookups + byte-position adjacency instead of the +// old ParsePatternInNLMode prefix decomposition, whose word* expansion + full-position +// materialization OOM-killed the CN at scale. Offsets are relative to the first slot. +func phraseSlots(pattern, parser string) ([]phraseSlot, error) { + if IsJSONValueParser(parser) { + // json_value: the whole pattern is ONE atomic value matched exactly (no ngram) — + // a single exact-term slot, mirroring classic `word = value`. + if pattern == "" { + return nil, nil + } + return []phraseSlot{{term: pattern, off: 0}}, nil + } + if normalizeParser(parser) == ParserGojieba { + tok, err := queryTokenizer(ParserGojieba) + if err != nil { + return nil, err + } + return tokenizePhraseSlots(tok, []byte(pattern)), nil + } + return ngramPhraseSlots(pattern), nil +} + +// isBreakerRune / isLatinRune mirror SimpleTokenizer's run classification (simple.go) so +// the query decomposition segments runs exactly as the document tokenizer does. +func isBreakerRune(r rune) bool { + if r < 128 { + return !((r >= '0' && r <= '9') || (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z')) + } + return unicode.IsPunct(r) || unicode.IsSpace(r) +} + +func isLatinRune(r rune) bool { return r < 0x7FF } + +// ngramPhraseSlots decomposes an ngram-parser query into positional phrase slots that +// match the SimpleTokenizer document index WITHOUT word* prefixes for CJK runs >= 3 +// chars. A CJK run is covered by EXACT full trigrams at char step ngramSize PLUS a final +// trigram that ends at the run's end (the last trigram is shifted back to cover the tail), +// so byte-position adjacency alone pins the whole run — the overlapping/adjacent trigrams +// uniquely determine the substring. Latin runs become whole (lowercased) word slots. Only +// a CJK run SHORTER than ngramSize (1-2 chars, e.g. a bare 2-char term) has no trigram and +// falls back to a single prefix (star) slot, handled presence-only at match time. +func ngramPhraseSlots(pattern string) []phraseSlot { + runes := []rune(pattern) + n := len(runes) + if n == 0 { + return nil + } + bpos := make([]int32, n+1) // byte offset of each rune index + b := int32(0) + for i, r := range runes { + bpos[i] = b + b += int32(utf8.RuneLen(r)) + } + bpos[n] = b + + var slots []phraseSlot + i := 0 + for i < n { + if isBreakerRune(runes[i]) { + i++ + continue + } + if isLatinRune(runes[i]) { + j := i + for j < n && !isBreakerRune(runes[j]) && isLatinRune(runes[j]) { + j++ + } + slots = append(slots, phraseSlot{term: strings.ToLower(string(runes[i:j])), off: bpos[i]}) + i = j + continue + } + // CJK run [i, j) + j := i + for j < n && !isBreakerRune(runes[j]) && !isLatinRune(runes[j]) { + j++ + } + if j-i < ngramSize { + // 1-2 char CJK run: no trigram exists → prefix (rare; presence-only in matchPhrase) + slots = append(slots, phraseSlot{term: string(runes[i:j]), star: true, off: bpos[i]}) + i = j + continue + } + last := j - ngramSize // start index of the trigram ending at the run's end + for c := i; c <= last; c += ngramSize { + slots = append(slots, phraseSlot{term: string(runes[c : c+ngramSize]), off: bpos[c]}) + } + if (last-i)%ngramSize != 0 { // tail not covered by the step-ngramSize loop → add the final trigram + slots = append(slots, phraseSlot{term: string(runes[last : last+ngramSize]), off: bpos[last]}) + } + i = j + } + if len(slots) == 0 { + return nil + } + base := slots[0].off + for k := range slots { + slots[k].off -= base + } + return slots +} + +// slotsKey is a deterministic string key for a phrase (term+star+offset per slot), used +// to memoize phrase hits/df per query. +func slotsKey(slots []phraseSlot) string { + var b strings.Builder + for _, s := range slots { + b.WriteString(s.term) + if s.star { + b.WriteByte('*') + } + b.WriteByte(0) + b.WriteString(strconv.Itoa(int(s.off))) + b.WriteByte(0) + } + return b.String() +} + +// tokenizePhraseSlots tokenizes text into (word, byte-position) TEXT slots — for gojieba +// NL and boolean-mode explicit "phrases". +func tokenizePhraseSlots(tok tokenizer.Tokenizer, text []byte) []phraseSlot { + wps := tokenizeWords(tok, text) + if len(wps) == 0 { + return nil + } + base := wps[0].Pos + slots := make([]phraseSlot, len(wps)) + for i, wp := range wps { + slots[i] = phraseSlot{term: wp.Word, off: wp.Pos - base} + } + return slots +} + +// buildBooleanQuery parses a boolean-mode query parser-aware: gojieba/Latin +// operands tokenize into word phrases/terms; a bare CJK operand under ngram/json +// is an exact positional (byte-offset) phrase, matching it contiguously — NOT an +// OR-group of its ngrams (which would over-match any doc sharing a single ngram). +func buildBooleanQuery(query, parser string) (BoolQuery, error) { + var q BoolQuery + for _, rc := range scanBoolean(query) { + c, ok, err := rawToClauseParser(rc, parser) + if err != nil { + return q, err + } + if !ok { + continue + } + switch bucketOf(rc.prefix) { + case bMust: + q.must = append(q.must, c) + case bMustNot: + q.mustNot = append(q.mustNot, c) + case bAdjust: + q.adjust = append(q.adjust, c) + default: + q.should = append(q.should, c) + } + } + return q, nil +} + +// rawToClauseParser is the parser-aware form of rawToClause (boolean.go): a bare +// CJK operand under ngram/json becomes an exact positional phrase (byte-offset +// slots); quoted phrases, prefixes, groups and gojieba/Latin operands behave as +// in rawToClause. +func rawToClauseParser(rc rawClause, parser string) (clause, bool, error) { + w := weightOf(rc.prefix) + if rc.group { + var children []clause + for _, ch := range rc.children { + cc, ok, err := rawToClauseParser(ch, parser) + if err != nil { + return clause{}, false, err + } + if ok { + children = append(children, cc) + } + } + if len(children) == 0 { + return clause{}, false, nil + } + return clause{kind: clauseGroup, children: children, weight: w}, true, nil + } + + // json_value: the whole operand is ONE atomic value matched exactly (no ngram, no + // tokenization) — classic fulltext queries `word = operand` (sql.go:173). A quoted + // operand ("update_json") is the same whole-value match. A trailing * is a value + // prefix. + if IsJSONValueParser(parser) { + if rc.text == "" { + return clause{}, false, nil + } + if rc.star { + return clause{kind: clausePrefix, terms: []string{rc.text}, weight: w}, true, nil + } + return clause{kind: clauseTerm, terms: []string{rc.text}, weight: w}, true, nil + } + + // A bare (non-quoted, non-prefix) CJK operand under ngram/json is an EXACT positional + // phrase (byte-offset slots), the same as NL and as classic fulltext — which matches + // the operand contiguously. (It previously expanded to an OR-group of its n-grams, + // which over-matched any doc sharing a single n-gram, e.g. `+中文學習` matched 中文學校. + // That was a workaround for the old token-index phrase matcher; byte positions make + // the phrase robust, so it is no longer needed.) + if parser != ParserGojieba && !rc.quoted && !rc.star && hasCJK(rc.text) { + slots, err := phraseSlots(rc.text, parser) + if err != nil { + return clause{}, false, err + } + if len(slots) == 0 { + return clause{}, false, nil + } + return clause{kind: clausePhrase, phrase: slots, weight: w}, true, nil + } + + tok, err := queryTokenizer(parser) + if err != nil { + return clause{}, false, err + } + terms, err := tokenizeToTerms([]byte(rc.text), tok) + if err != nil { + return clause{}, false, err + } + if len(terms) == 0 { + return clause{}, false, nil + } + switch { + case rc.quoted: + // Explicit "phrase": positional (byte-offset) slots — deduped/prefixed the same + // way NL is, so a CJK phrase matches by byte offset, not fragile token adjacency. + slots, serr := phraseSlots(rc.text, parser) + if serr != nil { + return clause{}, false, serr + } + if len(slots) == 0 { + return clause{}, false, nil + } + return clause{kind: clausePhrase, phrase: slots, weight: w}, true, nil + case rc.star: + return clause{kind: clausePrefix, terms: terms[:1], weight: w}, true, nil + case len(terms) == 1: + return clause{kind: clauseTerm, terms: terms, weight: w}, true, nil + default: + slots, serr := phraseSlots(rc.text, parser) + if serr != nil { + return clause{}, false, serr + } + if len(slots) == 0 { + return clause{}, false, nil + } + return clause{kind: clausePhrase, phrase: slots, weight: w}, true, nil + } +} diff --git a/pkg/fulltext2/search.go b/pkg/fulltext2/search.go new file mode 100644 index 0000000000000..fc6f158883971 --- /dev/null +++ b/pkg/fulltext2/search.go @@ -0,0 +1,543 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "math" + "math/bits" + "slices" + "sort" +) + +// ScoreAlgo selects the relevance formula (mirrors ft_relevancy_algorithm). Both +// use MatrixOne's squared-idf convention. +type ScoreAlgo int + +const ( + TfIdf ScoreAlgo = iota // w·tf·idf² + BM25 // w·idf²·bm25Factor(tf, docLen, avgDocLen) +) + +// fulltext2 has its OWN relevance session variable, distinct from classic +// fulltext's ft_relevancy_algorithm, and defaults to BM25 (the better ranked- +// retrieval default) rather than TF-IDF. Users opt into TF-IDF explicitly with +// SET ft2_relevancy_algorithm='TF-IDF'. +const ( + Fulltext2RelevancyAlgo = "ft2_relevancy_algorithm" + Fulltext2RelevancyAlgo_bm25 = "BM25" + Fulltext2RelevancyAlgo_tfidf = "TF-IDF" +) + +const ( + bm25K1 = 1.5 + bm25B = 0.75 +) + +// Result is one ranked hit: the source pk and its relevance score. Score is float32 +// (matches classic fulltext, whose score/score-map are float32 — float32 relevance is +// enough for BM25/TF-IDF); only the idf/avgDocLen intermediates are computed in float64 +// and narrowed, as classic does. +type Result struct { + Pk any + Score float32 + // Include carries the doc's INCLUDE column values (all of the index's INCLUDE columns, + // in index order; a nil element = SQL NULL) when the index has INCLUDE columns — so a + // COVERING query is served from the index without a base-table JOIN. nil otherwise. + Include []any +} + +// lookup resolves a term to its posting list on either a build-side segment (the +// terms map) or a loaded one (the FST ordinal). This lets the evaluator run +// against a freshly built segment or a deserialized one uniformly. +func (s *Segment) lookup(term string) (*termPostings, bool) { + if s.dict != nil { + return s.LookupLoaded(term) + } + p, ok := s.terms[term] + return p, ok +} + +// SearchPhrase runs an NL exact-phrase query: the terms (the tokenized query, in +// order) must occur as a CONTIGUOUS phrase in a document (MatrixOne NL semantics, +// §6). A single term degenerates to a plain term query. Returns up to k hits +// ordered by score desc (ties by ascending doc ord, for determinism). +// +// This is the correctness-first evaluator: it intersects the terms' posting +// lists and verifies positional adjacency exactly, without WAND block-max early +// termination (that optimization layers on later without changing results). +func (s *Segment) SearchPhrase(slots []phraseSlot, algo ScoreAlgo, k int) []Result { + if k <= 0 || s.N == 0 { + return nil + } + hits := s.matchPhrase(slots) + if len(hits) == 0 { + return nil + } + + // idf uses the PHRASE document frequency (number of docs the phrase occurs + // in), squared per MatrixOne. df <= N, so idf >= 0. + idf2 := idfSquared(s.N, len(hits)) + avgDocLen := s.avgDocLenOrMean() + + results := make([]Result, len(hits)) + for i, h := range hits { + results[i] = Result{ + Pk: s.pk(h.ord), + Score: s.scoreTerm(algo, float64(h.tf), idf2, h.ord, avgDocLen), + Include: s.decodeInclude(h.ord), + } + } + + // Order by score desc, ties by ascending ord (hits are already in ord order, + // so a stable sort on score desc keeps the ord tiebreak). + sort.SliceStable(results, func(a, b int) bool { return results[a].Score > results[b].Score }) + if len(results) > k { + results = results[:k] + } + return results +} + +// docTf is a document ord and the term/phrase occurrence count in it. +type docTf struct { + ord int64 + tf int +} + +// matchPhrase returns the documents that contain terms as a CONTIGUOUS phrase, +// each with the phrase occurrence count, in ascending doc ord. Returns nil if +// terms is empty or any term is absent from the segment. Shared by SearchPhrase +// and the boolean phrase clause. +// phraseSlot is one position of a positional phrase: a term (star=false) or a word* +// prefix (star=true) that must occur at byte offset `off` from the phrase's first slot. +// Byte offsets (not token indices) match classic fulltext's ngram phrase JOIN, so a +// redundant-ngram-removed CJK phrase (with gaps between kept trigrams) and a prefix tail +// both verify correctly against the doc's byte-position postings. +type phraseSlot struct { + term string + star bool + off int32 +} + +// matchPhrase verifies a positional phrase against the segment, returning per-doc phrase +// occurrence counts. The common shape — a multi-slot phrase of EXACT trigrams (every CJK +// query >= 3 chars decomposes to these) — runs the BLOCK-CURSOR conjunctive intersection +// (matchPhraseCursor): it advances a Block-Max cursor per slot in doc order, decoding one +// docID block + one position block at a time, so peak memory is O(nslots × BlockSize) +// regardless of corpus size. A single-slot phrase or one containing a star (prefix) slot +// — only 1-2 char CJK runs — takes the materialize fallback (which handles the presence- +// only prefix union). +func (s *Segment) matchPhrase(slots []phraseSlot) []docTf { + if len(slots) < 2 { + return s.matchPhraseFallback(slots) + } + for _, sl := range slots { + if sl.star { + return s.matchPhraseFallback(slots) + } + } + return s.matchPhraseCursor(slots) +} + +// matchPhraseFallback is the materialize-then-probe evaluator: it resolves each slot to +// doc-sorted postings, anchors on the rarest, and probes the others by binary search. It +// backs the single-slot and star-slot cases (a lone star slot is presence-only via a +// doc-ord bitset). No per-slot map, no re-sort (positions are stored ascending). +func (s *Segment) matchPhraseFallback(slots []phraseSlot) []docTf { + if len(slots) == 0 { + return nil + } + // Resolve each slot to its doc-sorted postings (docs[i] parallel to pos[i]). A lone + // star slot skips positions (presence-only); a resolved slot no doc satisfies ⇒ nil. + single := len(slots) == 1 + docsOf := make([][]int64, len(slots)) + posOf := make([][][]int32, len(slots)) + rare := 0 + for i, sl := range slots { + d, p, ok := s.slotPostings(sl, single) + if !ok { + return nil + } + docsOf[i], posOf[i] = d, p + if len(d) < len(docsOf[rare]) { + rare = i + } + } + + // Single slot: every doc containing it is a hit; tf = its occurrence count (1 for a + // presence-only star slot). + if single { + hits := make([]docTf, 0, len(docsOf[0])) + for di, ord := range docsOf[0] { + tf := 1 + if posOf[0] != nil { + tf = len(posOf[0][di]) + } + hits = append(hits, docTf{ord, tf}) + } + return hits + } + + // Multi-slot: iterate the rarest slot's docs; for each, look up this doc's positions in + // every slot (binary search), then anchor on the rare slot's positions and verify the + // others at B+off by binary search. Common slots are probed, never materialized whole. + var hits []docTf + posForDoc := make([][]int32, len(slots)) // scratch, reused across docs + for ri, ord := range docsOf[rare] { + present := true + for j := range slots { + if j == rare { + posForDoc[j] = posOf[rare][ri] + continue + } + k := sortedIndexInt64(docsOf[j], ord) + if k < 0 { + present = false + break + } + posForDoc[j] = posOf[j][k] + } + if !present { + continue + } + cnt := 0 + for _, pr := range posForDoc[rare] { + start := pr - slots[rare].off // candidate phrase-start byte position + matched := true + for j := range slots { + if j == rare { + continue + } + if !sortedContainsInt32(posForDoc[j], start+slots[j].off) { + matched = false + break + } + } + if matched { + cnt++ + } + } + if cnt > 0 { + hits = append(hits, docTf{ord, cnt}) + } + } + return hits +} + +// slotPostings resolves one phrase slot to doc-sorted postings (docs parallel to pos). +// An exact slot reuses the term's stored, already-ascending positions directly (no copy, +// no re-sort). A star slot expands its prefix: a LONE star slot (single-slot phrase) is +// presence-only — it unions just the doc IDs (pos==nil) so a hot 2-char prefix cannot +// materialize millions of positions; an embedded star slot (rare) merges positions. +func (s *Segment) slotPostings(sl phraseSlot, single bool) ([]int64, [][]int32, bool) { + if !sl.star { + pl, ok := s.lookup(sl.term) + if !ok { + return nil, nil, false + } + return pl.materializeDocIDs(), pl.materializePositions(), true + } + terms, err := s.prefixTerms(sl.term) + if err != nil || len(terms) == 0 { + return nil, nil, false + } + if single { + docs := s.unionDocIDs(terms) + if len(docs) == 0 { + return nil, nil, false + } + return docs, nil, true + } + return s.mergeStarPostings(terms) +} + +// unionDocIDs returns the ascending union of the doc IDs of the given terms — no +// positions (the presence-only path for a lone prefix slot). It unions into a doc-ord +// BITSET rather than a map: a hot 2-char prefix (e.g. 中文*) expands to many trigrams +// covering most of the corpus, and a growing map[int64]struct{} rehashed itself to death +// (the dominant cost once matchPhrase's own map was removed). A bitset is O(N/8) with no +// per-doc allocation or rehash, and the bit walk yields docs already ascending. +func (s *Segment) unionDocIDs(terms []string) []int64 { + if s.N == 0 { + return nil + } + bset := make([]uint64, (s.N+63)/64) + any := false + for _, t := range terms { + pl, ok := s.lookup(t) + if !ok { + continue + } + for _, ord := range pl.materializeDocIDs() { + bset[ord>>6] |= uint64(1) << (uint64(ord) & 63) + any = true + } + } + if !any { + return nil + } + docs := make([]int64, 0, 256) + for w, word := range bset { + base := int64(w) << 6 + for word != 0 { + docs = append(docs, base+int64(bits.TrailingZeros64(word))) + word &= word - 1 + } + } + return docs +} + +// mergeStarPostings merges the doc-sorted postings of a prefix expansion into one +// (docs, positions) with per-doc positions sorted (they come from different terms). This +// is the rare embedded-prefix path (a 1-2 char CJK run inside a longer phrase), so the +// map here is bounded and off the hot path. +func (s *Segment) mergeStarPostings(terms []string) ([]int64, [][]int32, bool) { + m := make(map[int64][]int32) + for _, t := range terms { + pl, ok := s.lookup(t) + if !ok { + continue + } + docs := pl.materializeDocIDs() + pos := pl.materializePositions() + for di, ord := range docs { + m[ord] = append(m[ord], pos[di]...) + } + } + if len(m) == 0 { + return nil, nil, false + } + docs := make([]int64, 0, len(m)) + for ord := range m { + docs = append(docs, ord) + } + slices.Sort(docs) + pos := make([][]int32, len(docs)) + for i, ord := range docs { + p := m[ord] + slices.Sort(p) + pos[i] = p + } + return docs, pos, true +} + +// sortedIndexInt64 returns the index of v in the ascending slice a, or -1 if absent. +func sortedIndexInt64(a []int64, v int64) int { + i := sort.Search(len(a), func(i int) bool { return a[i] >= v }) + if i < len(a) && a[i] == v { + return i + } + return -1 +} + +// phraseCursor walks one exact term's Block-Max postings in doc order for the phrase +// intersection, decoding ONE docID block + ONE position block at a time (never the whole +// list). It is the phrase analogue of wandIter, but also carries the block's positions. +type phraseCursor struct { + tp *termPostings + off int32 // this slot's byte offset within the phrase + idx int // current global posting index (0..df-1) + curBlk int // block currently decoded into bDocs/bPos (-1 = none) + blen int // valid entries in bDocs/bPos + bDocs []int64 // decoded docIDs of curBlk (cap BlockSize); backed by buf + bPos [][]int32 // decoded positions of curBlk (cap BlockSize); backed by buf + tfbuf []uint8 // scratch for fillBlock's tfs (phrase ignores tf); backed by buf + buf *phraseBuf // pooled backing for bDocs/bPos/tfbuf; returned by releasePhraseCursors +} + +func newPhraseCursor(tp *termPostings, off int32) *phraseCursor { + b := getPhraseBuf() + return &phraseCursor{ + tp: tp, off: off, curBlk: -1, + bDocs: b.docs, + bPos: b.pos, + tfbuf: b.tfs, + buf: b, + } +} + +func (c *phraseCursor) atEnd() bool { return c.idx >= c.tp.df() } + +// decode ensures block b is the one loaded in bDocs/bPos (a field compare when the cursor +// stays within a block; skipped blocks are never decoded). +func (c *phraseCursor) decode(b int) { + if b == c.curBlk { + return + } + c.blen = c.tp.fillBlock(b, c.bDocs, c.tfbuf) + c.tp.fillBlockPositions(b, c.bPos) + c.curBlk = b +} + +// doc returns the current doc ord, or MaxInt64 when exhausted (sorts last). +func (c *phraseCursor) doc() int64 { + if c.atEnd() { + return math.MaxInt64 + } + c.decode(c.idx / BlockSize) + return c.bDocs[c.idx%BlockSize] +} + +// positions returns the current doc's ascending byte positions. +func (c *phraseCursor) positions() []int32 { + c.decode(c.idx / BlockSize) + return c.bPos[c.idx%BlockSize] +} + +// skipTo advances (forward only) to the first doc >= target, using the resident +// blockLastDoc to pick the block so non-overlapping blocks are skipped without decoding. +func (c *phraseCursor) skipTo(target int64) { + bl := c.tp.blockLastDoc + b := c.idx / BlockSize + for b < len(bl) && bl[b] < target { + b++ + } + if b >= c.tp.nblk() { + c.idx = c.tp.df() // past the last posting → exhausted + return + } + c.decode(b) + w := sort.Search(c.blen, func(i int) bool { return c.bDocs[i] >= target }) + c.idx = b*BlockSize + w +} + +// matchPhraseCursor runs the block-cursor conjunctive phrase intersection over EXACT +// slots: it drives the rarest slot in doc order and, for each of its docs, skips the +// other cursors to that doc (block-skipping non-overlapping regions) and verifies byte- +// position adjacency. Peak memory is O(nslots × BlockSize) — one decoded block per +// cursor — independent of corpus size, vs the fallback's whole-posting-list materialize. +func (s *Segment) matchPhraseCursor(slots []phraseSlot) []docTf { + cursors := make([]*phraseCursor, len(slots)) + defer releasePhraseCursors(cursors) // recycle block buffers, incl. the early-return path + rare := 0 + for i, sl := range slots { + pl, ok := s.lookup(sl.term) + if !ok { + return nil // a slot no doc satisfies ⇒ the phrase can't occur + } + cursors[i] = newPhraseCursor(pl, sl.off) + if pl.df() < cursors[rare].tp.df() { + rare = i + } + } + rc := cursors[rare] + var hits []docTf + for !rc.atEnd() { + d := rc.doc() + present := true + for j, c := range cursors { + if j == rare { + continue + } + c.skipTo(d) + if c.doc() != d { + present = false + break + } + } + if present { + cnt := 0 + for _, pr := range rc.positions() { + start := pr - slots[rare].off // candidate phrase-start byte position + matched := true + for j := range slots { + if j == rare { + continue + } + if !sortedContainsInt32(cursors[j].positions(), start+slots[j].off) { + matched = false + break + } + } + if matched { + cnt++ + } + } + if cnt > 0 { + hits = append(hits, docTf{d, cnt}) + } + } + rc.idx++ + } + return hits +} + +// idfSquared is MatrixOne's squared inverse document frequency: (log10(N/df))². +// df is clamped to the range [1, n]. +// +// The upper clamp matters: df is summed over PHYSICAL postings (dead + live copies both +// counted, matching bm25's gdf), while n is the LIVE doc count, so heavy update churn on a +// small corpus can push df past n before a MERGE reclaims the dead copies. Without the clamp, +// df==n collapses idf to 0 and df>n squares a NEGATIVE log10(n/df) back into a POSITIVE impact +// — a non-monotonic idf that can rank a dirty base+tail differently from the identical corpus +// after compaction. Clamping keeps idf>=0 and monotonically non-increasing in df; a term that +// appears in (at least) every live doc correctly gets idf 0. n>=1 in every scoring path +// (globalN==0 / empty-segment queries short-circuit before this), so the clamp never divides +// by zero. +func idfSquared(n int64, df int) float64 { + if df < 1 { + df = 1 + } + if n >= 1 && int64(df) > n { + df = int(n) + } + idf := math.Log10(float64(n) / float64(df)) + return idf * idf +} + +// scoreTerm scores one (tf, idf², doc) contribution under the active algorithm. +func (s *Segment) scoreTerm(algo ScoreAlgo, tf, idf2 float64, ord int64, avgDocLen float64) float32 { + if algo == BM25 { + return float32(idf2 * bm25Factor(tf, s.docLen[ord], avgDocLen)) + } + return float32(tf * idf2) // TfIdf +} + +// avgDocLenOrMean returns the segment's AvgDocLen, falling back to the +// single-segment mean when it is unset (a lone segment queried directly). +func (s *Segment) avgDocLenOrMean() float64 { + if s.AvgDocLen != 0 { + return s.AvgDocLen + } + return meanDocLen(s.docLen) +} + +// sortedContainsInt32 reports whether v is in the ascending slice a. +func sortedContainsInt32(a []int32, v int32) bool { + i := sort.Search(len(a), func(i int) bool { return a[i] >= v }) + return i < len(a) && a[i] == v +} + +// bm25Factor is MatrixOne's BM25 tf component: tf·(k1+1)/(tf + k1·(1−b+b·dl/avgdl)). +func bm25Factor(tf float64, docLen int32, avgDocLen float64) float64 { + norm := 1.0 + if avgDocLen > 0 { + norm = 1.0 - bm25B + bm25B*float64(docLen)/avgDocLen + } + return tf * (bm25K1 + 1) / (tf + bm25K1*norm) +} + +// meanDocLen is the single-segment average document length (Σ docLen / N). For a +// multi-segment index AvgDocLen is set at load across all segments (§4); this is +// the fallback when a lone segment is queried directly. +func meanDocLen(docLen []int32) float64 { + if len(docLen) == 0 { + return 0 + } + var sum int64 + for _, d := range docLen { + sum += int64(d) + } + return float64(sum) / float64(len(docLen)) +} diff --git a/pkg/fulltext2/search_cache.go b/pkg/fulltext2/search_cache.go new file mode 100644 index 0000000000000..0bce2d91ebd7d --- /dev/null +++ b/pkg/fulltext2/search_cache.go @@ -0,0 +1,364 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "context" + "math" + "time" + + "github.com/matrixorigin/matrixone/pkg/common/docfilter" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/logutil" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + veccache "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" +) + +// Fulltext2Query is the query payload passed through VectorIndexCache.Search to a +// Fulltext2Search: the raw MATCH pattern, whether it is boolean mode, and the +// relevance formula (BM25/TF-IDF, resolved per query from the ft2_relevancy_algorithm +// session var). The parser is a fixed property of the index (carried in cfg), so it +// is NOT part of the per-query payload — the same cached index serves every query. +type Fulltext2Query struct { + Pattern []byte + Boolean bool + // BagOfWords is IN BM25 MODE: ranked bag-of-words retrieval (each token an OR + // term, no positional phrase), so it works on a POSITION_FREE index. Takes + // precedence over Boolean. + BagOfWords bool + Algo ScoreAlgo + // FilterBytes is the optional serialized docfilter membership — the WHERE-clause + // prefilter pushed down as a runtime filter (built in C from the eligible pks), + // applied INSIDE the search so a pushed LIMIT bounds the filtered set. nil = none. + FilterBytes []byte + // IncludePredsJSON is the optional pushed-down predicate set on INCLUDE columns + // (IncludePredicateSpec JSON), evaluated in Go against the stored per-doc INCLUDE + // values INSIDE the search — so a WHERE on an INCLUDE column needs no second base scan. + // nil = none. ANDed with FilterBytes. + IncludePredsJSON []byte +} + +// Fulltext2Search adapts a loaded fulltext2 Index to veccache.VectorIndexSearchIf so +// the positional index shares the VectorIndexCache (load-once, RW-shared, TTL +// eviction) with bm25 and the vector plugins, keyed by its storage table name. +// Before this, fulltext2_search reloaded the whole index (LoadAllBases + +// LoadTailSegments + NewIndex) on EVERY query — ~1s per query at 50K vs bm25's ~3ms. +// A loaded base segment's postings (docID/tf blocks + positions) are views into a +// shared read-only mmap, not the Go heap — reclaimable OS page cache, not GC-scanned; +// Destroy munmaps them (block-postings format, storage.go/segment.go). +type Fulltext2Search struct { + cfg TableConfig + idx *Index + loaded bool + + // Generation captured at Load (same txn snapshot as the loaded data) for the cache's + // cross-CN freshness check (IsStale, called off the housekeeping goroutine): loadedTs = + // MAX(metadata.timestamp) (REBUILD/MERGE), loadedTail = MAX(tag=1 CdcTail chunk_id) (CDC + // append). cnUUID/accountID are the durable handles to re-query in the background. + // genValid is false when capture failed / no resolver (unit tests) → IsStale is a no-op. + cnUUID string + accountID uint32 + loadedTs int64 + loadedTail int64 + genValid bool +} + +var _ veccache.VectorIndexSearchIf = (*Fulltext2Search)(nil) + +// NewFulltext2Search returns an unloaded search handle; the cache calls Load before +// the first Search. +func NewFulltext2Search(cfg TableConfig) *Fulltext2Search { + return &Fulltext2Search{cfg: cfg} +} + +// Load reads the index from the chunk store: the tag=0 base sub-indexes plus the +// tag=1 CdcTail delta frames (+ delete set), assembled into a queryable Index with +// global stats and per-pk liveness. An index created on an empty table has no tag=0 +// base, so segs may hold only tail segments (or be empty → a loaded, doc-less index). +func (s *Fulltext2Search) Load(sqlproc *sqlexec.SqlProcess) error { + // Fail fast on the QUERY path if the bases' per-doc metadata won't fit the heap + // budget, rather than OOM-killing the CN (which takes down every query on the node). + // This guard is on Load, NOT LoadAllBases, so CompactSegments (MERGE) — the remedy — + // stays exempt and can still load the bases to reclaim dead docs. + if err := checkBaseLoadBudget(sqlproc, s.cfg); err != nil { + return err + } + bases, err := LoadAllBases(sqlproc, s.cfg) + if err != nil { + return err + } + tails, deletes, err := LoadTailSegments(sqlproc, s.cfg) + if err != nil { + freeSegs(bases) // munmap the base segments on a tail-load error (don't leak the mappings) + return err + } + segs := append(bases, tails...) + s.idx = NewIndex(segs, deletes) + s.loaded = true + + // Capture the generation + durable handles for IsStale. Same txn as the load, so the + // captured generation matches the loaded snapshot exactly. On any capture failure genValid + // stays false and IsStale reports the entry as uncheckable-hence-stale (evict + reload to + // retry capture) rather than pinning it in cache forever — see IsStale. + s.cnUUID = sqlproc.GetService() + if acc, e := sqlproc.GetAccountID(); e == nil { + if ts, tail, e2 := LoadGeneration(sqlproc, s.cfg); e2 == nil { + s.accountID, s.loadedTs, s.loadedTail, s.genValid = acc, ts, tail, true + } + } + return nil +} + +// IsStale reports whether the underlying index has changed since this entry was loaded, by +// comparing the load-time generation to the current one (a REBUILD/MERGE bumps the metadata +// timestamp; a CDC append bumps the tag=1 tail chunk_id). Run on the cache housekeeping +// goroutine (NOT the search path), so it opens its own short auto-commit txn via the durable +// cnUUID/accountID captured at load. +// +// On a query ERROR it returns (true, err): the most likely cause is the index tables were +// dropped/rebuilt out from under us (DROP INDEX, restore), so the dead entry must be +// reclaimed — the next search reloads, or fails cleanly if the index is truly gone. The err +// is surfaced only for logging. (A no-op reload for a genuinely-transient blip is cheaper +// than pinning a possibly-dead entry until TTL.) No captured generation (capture failed at load, +// or no service to re-query) ⇒ (true, nil): the entry cannot self-check freshness, so evict it to +// force a reload that retries capture. Returning (false, nil) would pin a hot entry — whose TTL +// keeps sliding on every search — in cache indefinitely, serving pre-CDC/rebuild data forever; +// the bounded reload (one per freshness sweep) self-heals the moment capture succeeds. +func (s *Fulltext2Search) IsStale() (bool, error) { + if !s.genValid || s.cnUUID == "" { + return true, nil + } + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + ts, tail, err := QueryGeneration(ctx, s.cnUUID, s.accountID, s.cfg) + if err != nil { + return true, err + } + stale := ts != s.loadedTs || tail != s.loadedTail + logutil.Debugf("[ft2-isstale] index=%s loadedGen=(ts=%d,tail=%d) curGen=(ts=%d,tail=%d) stale=%v", + s.cfg.IndexTable, s.loadedTs, s.loadedTail, ts, tail, stale) + return stale, nil +} + +// prepare validates the query, resolves the top-k bound, and builds the WHERE prefilter, +// shared by Search and SearchInto. free is ALWAYS non-nil and must be deferred by the caller +// even on error (it frees the C-backed docfilter created here, so it never outlives the +// query — the cached Index is shared, so the filter must be per-query local). empty=true means +// a loaded but doc-less index (matches nothing) — the caller returns an empty result. +func (s *Fulltext2Search) prepare(proc *sqlexec.SqlProcess, query any, rt vectorindex.RuntimeConfig) (q Fulltext2Query, k int, pf *prefilter, free func(), empty bool, err error) { + free = func() {} + if !s.loaded || s.idx == nil { + err = moerr.NewInternalError(proc.GetContext(), "fulltext2 index not loaded") + return + } + if s.idx.NumDocs() == 0 { + empty = true + return + } + var ok bool + if q, ok = query.(Fulltext2Query); !ok { + err = moerr.NewInternalError(proc.GetContext(), "fulltext2 search: invalid query payload") + return + } + // rt.Limit is uint; a value past MaxInt32 (an absurd pushed LIMIT) would wrap negative + // in int(...), and 0 means "no pushed LIMIT" — return the whole result set (the TVF + // paginates). Mirror bm25's clamp. + k = int(rt.Limit) + if rt.Limit > uint(math.MaxInt32) { + k = math.MaxInt32 + } else if k <= 0 { + k = int(s.idx.NumDocs()) + } + if len(q.FilterBytes) > 0 || len(q.IncludePredsJSON) > 0 { + pf = &prefilter{} + if len(q.FilterBytes) > 0 { + var filter docfilter.MembershipFilter + if filter, err = docfilter.New(q.FilterBytes); err != nil { + return + } + free = filter.Free // freed via the caller's defer, incl. the error paths below + pf.docFilter = filter + } + if len(q.IncludePredsJSON) > 0 { + var preds []compiledIncludePred + if preds, err = compileIncludePredicates(q.IncludePredsJSON, s.idx.includeTypes(), s.idx.pkType()); err != nil { + return + } + pf.include = preds + } + } + return +} + +// runTopK dispatches the bounded top-k search (ranked bag-of-words vs positional query). +func (s *Fulltext2Search) runTopK(q Fulltext2Query, k int, pf *prefilter) ([]Result, error) { + if q.BagOfWords { + return s.idx.SearchBagOfWords(q.Pattern, s.cfg.Parser, q.Algo, k, pf) + } + return s.idx.SearchQuery(q.Pattern, q.Boolean, s.cfg.Parser, q.Algo, k, pf) +} + +// Search runs the WAND positional query (NL exact-phrase or boolean) and returns +// ([]any pks of the source type, []float64 scores). The covered LIMIT path is served by the +// box-free SearchInto instead; this []any form is the streaming (Emit) path and the legacy +// non-covered return. +func (s *Fulltext2Search) Search(proc *sqlexec.SqlProcess, query any, rt vectorindex.RuntimeConfig) (keys any, distances []float64, err error) { + q, k, pf, free, empty, err := s.prepare(proc, query, rt) + defer free() + if err != nil { + return nil, nil, err + } + if empty { + return []any{}, []float64{}, nil + } + + // No-LIMIT streaming path: when the caller passes an Emit callback (the TVF does this + // only for a query with no pushed LIMIT), yield every matching doc in bounded batches — + // no top-K heap, no materialization. Results are handed off through Emit, so return empty + // keys/distances (mirrors bm25). wantInclude tells the stream to carry each doc's INCLUDE + // values (segment order) through Emit's 3rd arg. + if rt.Emit != nil { + wantInclude := len(rt.RequestedIncludeColumns) > 0 + var serr error + if q.BagOfWords { + serr = s.idx.StreamBagOfWords(q.Pattern, s.cfg.Parser, q.Algo, pf, wantInclude, rt.Emit) + } else { + serr = s.idx.StreamQuery(q.Pattern, q.Boolean, s.cfg.Parser, q.Algo, pf, wantInclude, rt.Emit) + } + if serr != nil { + return nil, nil, serr + } + return []any{}, []float64{}, nil + } + + results, err := s.runTopK(q, k, pf) + if err != nil { + return nil, nil, err + } + keysOut := make([]any, len(results)) + dist := make([]float64, len(results)) + for i, r := range results { + keysOut[i] = r.Pk + dist[i] = float64(r.Score) + } + return keysOut, dist, nil +} + +// SearchInto is the box-free LIMIT-path twin of Search: it fills the caller-owned out +// SearchOutput (pk column, scores, covered INCLUDE columns) instead of returning boxed []any +// keys — so a warm query allocates nothing for its results (the TVF pools out and Resets it +// per query). out.Keys is the pk in ColumnBuffer form (box-free for every pk type), out.Dists +// the float32 scores, out.Include one nullable buffer per FULL index INCLUDE column (populated +// only when rt.RequestedIncludeColumns is set; the TVF projects what it needs by segPos). +func (s *Fulltext2Search) SearchInto(proc *sqlexec.SqlProcess, query any, rt vectorindex.RuntimeConfig, out *vectorindex.SearchOutput) error { + if out == nil { + return moerr.NewInternalError(proc.GetContext(), "fulltext2 SearchInto: nil out") + } + q, k, pf, free, empty, err := s.prepare(proc, query, rt) + defer free() + if err != nil { + return err + } + // Reset the caller-owned buffers (reused across queries → alloc-free after warmup). + pkType := s.idx.pkType() + if out.Keys == nil { + out.Keys = &vectorindex.ColumnBuffer{} + } else { + out.Keys.Reset() + } + out.Keys.Type = types.T(pkType) + out.Dists = out.Dists[:0] + if empty { + out.Include = out.Include[:0] + return nil + } + + results, err := s.runTopK(q, k, pf) + if err != nil { + return err + } + // pk column (box-free re-encode of each already-materialized boxed pk) + scores. + w, fixed := fixedPkByteWidth(pkType) + if !fixed { + w = 0 + } + for i := range results { + appendAnyPk(out.Keys, w, results[i].Pk) + out.Dists = append(out.Dists, results[i].Score) + } + return s.fillInclude(rt, results, out) +} + +// fillInclude populates out.Include with one nullable ColumnBuffer per FULL index INCLUDE +// column (segment order), covering the whole result set — so a covered LIMIT query reads its +// projected INCLUDE columns straight from the index (no base-table JOIN, no map[string][]any, +// no per-row reflection append). The TVF maps its requested output columns to segment +// positions (by includeOut.segPos), exactly as the streaming consumer does. No-op (empties +// out.Include) when no include columns were requested. The buffers are REUSED across queries +// (Reset in place) so includes are alloc-free after warmup, like out.Keys/out.Dists. +// +// Source note: this re-encodes each result's already-materialized boxed Include value — the +// top-k's per-winner decodeInclude boxing (O(limit), bounded) is retained; what this removes +// is the transport map + the reflection AppendAny. +func (s *Fulltext2Search) fillInclude(rt vectorindex.RuntimeConfig, results []Result, out *vectorindex.SearchOutput) error { + if len(rt.RequestedIncludeColumns) == 0 { + out.Include = out.Include[:0] + return nil + } + it := s.idx.includeTypes() + if cap(out.Include) < len(it) { + out.Include = make([]*vectorindex.ColumnBuffer, len(it)) + } else { + out.Include = out.Include[:len(it)] + } + for c := range it { + if out.Include[c] == nil { + out.Include[c] = &vectorindex.ColumnBuffer{} + } else { + out.Include[c].Reset() + } + out.Include[c].Type = types.T(it[c]) + } + for r := range results { + inc := results[r].Include + for c := range out.Include { + var v any + if c < len(inc) { + v = inc[c] + } + if err := appendAnyInclude(out.Include[c], v); err != nil { + return err + } + } + } + return nil +} + +// SearchFloat32 is unsupported (fulltext scores are float64; the vector float32 +// fast-path does not apply). +func (s *Fulltext2Search) SearchFloat32(proc *sqlexec.SqlProcess, query any, rt vectorindex.RuntimeConfig, outKeys []int64, outDists []float32) error { + return moerr.NewInternalError(proc.GetContext(), "fulltext2 search: SearchFloat32 not supported") +} + +// Destroy munmaps the loaded index's base segments (their posting blocks + positions +// are views into those mappings), then drops it. The cache holds the write lock +// around this, so no search is in flight. +func (s *Fulltext2Search) Destroy() { + s.idx.Free() + s.idx = nil + s.loaded = false +} diff --git a/pkg/fulltext2/segment.go b/pkg/fulltext2/segment.go new file mode 100644 index 0000000000000..f6e1bc68a58d5 --- /dev/null +++ b/pkg/fulltext2/segment.go @@ -0,0 +1,733 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "encoding/binary" + "os" + "sort" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/vectorindex" +) + +const ( + // MaxCappedTf caps stored term frequency at one byte (matches classic + // fulltext's tf cap and bm25's MaxCappedTf) — long docs with a hugely + // repeated term saturate rather than skewing the score. + MaxCappedTf = 255 + + // BlockSize is the doc count per block-max skip block (matches bm25's + // BlockSize). One (lastDoc, maxTf, minDocLen) triple is derived per block at + // load, letting WAND skip whole blocks whose score bound can't make top-k. + BlockSize = 128 +) + +// termPostings is the in-memory posting list for one term, ordered by doc ord. +// +// It mirrors bm25's termPostings but carries POSITIONS (per doc, the token +// offsets of this term) — the positional payload phrase / NL adjacency and +// ngram reassembly need, which the position-free bm25 engine omits. The score +// upper-bound inputs (maxTf, minDocLen, block-max) are stored RAW and +// idf/avgdl-free so one segment serves both TF-IDF and BM25; the active scorer +// derives its max-impact bound at query time. +type termPostings struct { + docIDs []int64 // BUILD-side doc ords, ascending, len == df; nil on a LOADED segment + tfs []uint8 // BUILD-side parallel capped tf (<= MaxCappedTf); nil on a LOADED segment + positions [][]int32 // BUILD-side per-doc positions (len == df); nil on a LOADED segment + + // ndoc is the document frequency (df). On a LOADED segment docIDs/tfs are NOT + // expanded — they live block-compressed in blockData (a view into the mmap) — + // so ndoc is the authoritative count. On a build-side segment it equals + // len(docIDs) (set by deriveTermStats). + ndoc int + + // LOADED-side docID/tf blocks: this term's postings kept COMPRESSED as + // BlockSize-doc blocks (per block: docID gaps as delta+varint from the previous + // block's last ord, then the block's raw tf bytes), a view into the segment's + // mmap. docIDs are the largest resident section (~46% at load), needed in full + // ONLY by phrase verification / MERGE / boolean — WAND ranking touches only the + // blocks its block-max walk lands on. So they are NOT expanded at load; the WAND + // cursor decodes one block on demand (fillBlock), and the cold paths materialize + // transiently (materializeDocIDs/materializeTfs). blockOff[b] is block b's byte + // offset within blockData (len nblk+1, cumulative). nil on a build-side segment. + blockData []byte + blockOff []int64 + + // LOADED-side positions (Deserialize): this term's positions kept COMPRESSED + // (delta+varint, per doc: pc + position gaps), a view into the segment's + // resident positions buffer. Positions are the largest section (~48%) and are + // needed ONLY for phrase verification / MERGE — never for WAND ranking — so they + // are NOT expanded into RAM at load; callers decode on demand via + // materializePositions() (whole list) or fillBlockPositions() (one block, for the + // phrase cursor). nil on a build-side segment. + posRaw []byte + // blockPosOff[b] is block b's byte offset within posRaw (len nblk+1, cumulative), + // so a block's positions are seekable without decoding the prior blocks — the + // position analogue of blockOff. nil on a build-side segment (positions is flat there). + blockPosOff []int64 + + // Term-level score-UB inputs (raw, scorer-agnostic). + maxTf uint8 // max tf over all postings + minDocLen int32 // min doc length over all postings + + // Block-Max skip-block metadata, one entry per ceil(df/BlockSize). Computed at + // BUILD (deriveTermStats) and STORED in the ranking directory, read back RESIDENT + // at load (never re-derived from the compressed blocks). Raw / scorer-agnostic. + blockLastDoc []int64 // last (max) ord in each block + blockMaxTf []uint8 // max tf in each block + blockMinDocLn []int32 // min doc length in each block +} + +// df is the document frequency (number of docs containing the term). +func (p *termPostings) df() int { + if p.docIDs != nil { + return len(p.docIDs) // build-side (authoritative before ndoc is set) + } + return p.ndoc +} + +// nblk is the number of Block-Max skip blocks (== ceil(df/BlockSize)). +func (p *termPostings) nblk() int { return len(p.blockLastDoc) } + +// blockLen is the number of postings in block b (BlockSize, or the remainder for +// the last block). +func (p *termPostings) blockLen(b int) int { + n := p.df() - b*BlockSize + if n > BlockSize { + n = BlockSize + } + return n +} + +// fillBlock decodes block b's docIDs and tfs into outDocs/outTfs (each cap >= +// BlockSize) and returns the block length. Build-side copies the flat slices; +// loaded-side varint-decodes the block from blockData (the mmap view): docID gaps +// accumulate from the previous block's last ord (resident blockLastDoc[b-1]), then +// the raw tf bytes follow. The WAND cursor calls this once per block it lands on; +// materializeDocIDs/Tfs call it per block to rebuild the flat arrays. +func (p *termPostings) fillBlock(b int, outDocs []int64, outTfs []uint8) int { + blen := p.blockLen(b) + if p.docIDs != nil { // build-side: copy from the flat arrays + lo := b * BlockSize + copy(outDocs[:blen], p.docIDs[lo:lo+blen]) + copy(outTfs[:blen], p.tfs[lo:lo+blen]) + return blen + } + data := p.blockData[p.blockOff[b]:p.blockOff[b+1]] + var prev int64 + if b > 0 { + prev = p.blockLastDoc[b-1] + } + off := 0 + for i := 0; i < blen; i++ { + // Guard corrupt/torn block bytes that survive the CRC: a malformed docID varint + // (n<=0) or one that over-consumes would otherwise make the tf copy below slice + // out of range and panic the query goroutine. Return the docs decoded so far — + // the sibling fillBlockPositions degrades the same way rather than crashing. + if off >= len(data) { + return i + } + g, n := binary.Uvarint(data[off:]) + if n <= 0 { + return i + } + off += n + prev += int64(g) + outDocs[i] = prev + } + // tfs are blen raw bytes after the docID gaps; on corruption they may not all be + // present. Copy only what the block actually holds (never past len(data)). + if off+blen > len(data) { + blen = len(data) - off + } + copy(outTfs[:blen], data[off:off+blen]) + return blen +} + +// fillBlockPositions decodes block b's per-doc token positions into out[:blen], +// returning the block length. loaded-side varint-decodes only block b's slice of posRaw +// (via blockPosOff) — the per-block analogue of fillBlock, so the phrase cursor holds ONE +// block's positions (O(BlockSize)) instead of the whole list. +// +// out[i] is REUSED in place (out[i] = append(out[i][:0], …)): out is a pooled phraseBuf +// buffer (getPhraseBuf), and reallocating a fresh []int32 per doc per block both churned +// the allocator under concurrent phrase load and left the pool holding stale per-doc +// arrays. Reusing keeps each slot at its high-water capacity and makes the pool actually +// pool. The build-side branch COPIES the flat slice into the owned out[i] (rather than +// aliasing p.positions) so a buffer reused later on a loaded segment can never write back +// into build data. matchPhrase reads positions via range / sortedContainsInt32, both of +// which treat a nil and an empty slice identically, so the degenerate (no-data / corrupt) +// slots leave out[i] empty rather than nil. +func (p *termPostings) fillBlockPositions(b int, out [][]int32) int { + blen := p.blockLen(b) + if p.positions != nil { // build-side: copy the flat slice into the buffer's own arrays + lo := b * BlockSize + for i := 0; i < blen; i++ { + out[i] = append(out[i][:0], p.positions[lo+i]...) + } + return blen + } + data := p.posRaw[p.blockPosOff[b]:p.blockPosOff[b+1]] + off := 0 + for i := 0; i < blen; i++ { + out[i] = out[i][:0] // reuse the pooled backing array; empty ≡ nil for the reader + if off >= len(data) { + continue + } + pc, n := binary.Uvarint(data[off:]) + if n <= 0 { + continue + } + off += n + if pc > uint64(len(data)-off) { // corrupt guard (each gap is >= 1 byte) + pc = uint64(len(data) - off) + } + var pp int32 + for m := uint64(0); m < pc; m++ { + g, k := binary.Uvarint(data[off:]) + if k <= 0 { + break + } + off += k + pp += int32(g) + out[i] = append(out[i], pp) + } + } + return blen +} + +// materializeDocIDs returns this term's full ascending doc ords (df entries): the +// build-side flat slice as-is, or a transient decode of every loaded block. Cold +// paths (phrase / MERGE / boolean) that scan all postings call this ONCE; WAND +// ranking never does (it decodes only the blocks its walk lands on). +func (p *termPostings) materializeDocIDs() []int64 { + if p.docIDs != nil { + return p.docIDs + } + out := make([]int64, p.ndoc) + var scratch [BlockSize]uint8 + for b := 0; b < p.nblk(); b++ { + lo := b * BlockSize + p.fillBlock(b, out[lo:], scratch[:]) + } + return out +} + +// materializeTfs returns this term's full capped tf bytes (df entries): the +// build-side flat slice as-is, or a transient decode of every loaded block. +func (p *termPostings) materializeTfs() []uint8 { + if p.tfs != nil { + return p.tfs + } + out := make([]uint8, p.ndoc) + var scratch [BlockSize]int64 + for b := 0; b < p.nblk(); b++ { + lo := b * BlockSize + p.fillBlock(b, scratch[:], out[lo:]) + } + return out +} + +// materializePositions decodes this term's per-doc token positions (df entries): +// the build-side stored [][]int32 as-is, or the loaded-side compressed posRaw +// (delta+varint) transiently. Positions are NOT resident on a loaded segment, so a +// caller that needs them (phrase verification, MERGE) materializes ONCE per query +// and discards — WAND ranking never calls this. Panics on a build-side... no: a +// build-side segment returns tp.positions directly. +func (p *termPostings) materializePositions() [][]int32 { + if p.positions != nil { + return p.positions + } + df := p.df() + out := make([][]int32, df) + off := 0 + for i := 0; i < df; i++ { + if off >= len(p.posRaw) { + break // truncated/corrupt (defense-in-depth on already-checksummed data) + } + pc, n := binary.Uvarint(p.posRaw[off:]) + if n <= 0 { + break + } + off += n + // A doc's position count cannot exceed the remaining bytes (each gap is >= 1 + // varint byte), so a garbage pc can't make() an unbounded slice. + if pc > uint64(len(p.posRaw)-off) { + pc = uint64(len(p.posRaw) - off) + } + doc := make([]int32, pc) + var pp int32 + for m := uint64(0); m < pc; m++ { + g, k := binary.Uvarint(p.posRaw[off:]) + if k <= 0 { + break + } + off += k + pp += int32(g) + doc[m] = pp + } + out[i] = doc + } + return out +} + +// Segment is one loadable in-memory fulltext v2 index unit — a tag=0 base sub or +// a tag=1 CDC tail frame (§4 of fulltext2.md). It is the positional analogue of +// bm25's WandModel: same docmap (ord → pk, docLen) and avgDocLen, but its term +// dict is keyed by the actual indexed term STRING and kept sorted so one +// structure serves both O(log n) lookup and prefix enumeration (`word*`). +type Segment struct { + Id string + N int64 // number of documents (= len(pks)) + PkType int32 // types.T of the source primary key (output decode + membership) + AvgDocLen float64 // average doc length; set at LOAD across all loaded segments + + // Recency orders segments for liveness when the same pk lands in several + // (UPDATE / reinsert / a stale base copy) — only the highest-Recency copy is + // live. Same semantics as bm25's WandModel.Recency (chunk_id for a tag=1 + // tail delta, metadata.recency for a tag=0 base). + Recency int64 + + // pks is the BUILD-side ord->pk slice (set while building an in-memory segment). + // On a LOADED segment it is nil: instead of materializing N boxed `any` pks (~24 B + // each — the O(docs) resident floor after the liveLoc removal), decodeDocmap keeps + // pkOffsets (4 B/doc, the byte offset of each pk in the docmap bytes) + pkRaw (a + // VIEW into those bytes, mmap-backed for a base), and pk(ord) decodes on demand. + pks []any + pkOffsets []int32 // loaded-side: ord -> offset of pk's length prefix in pkRaw + pkRaw []byte // loaded-side: the docmap bytes (view; pks live here len-prefixed) + docLen []int32 // ord -> document length (token count), for BM25 + + // INCLUDE columns: actual per-doc scalar values stored in the docmap (ord-aligned), + // used for in-index prefiltering and covering projection so a covered query needs no + // JOIN back to the base table. Encoded with the same pk codec (fixed-width dense for + // numerics, [u32 len][content] for varchar/char). All slices are nil when the index has + // no INCLUDE columns. + // + // Build-side: includeVals[ord] is the row's per-column values ([]any of len(includeTypes)); + // a nil element is SQL NULL. Loaded-side: includeVals is nil, and + // includeOffsets[ord*nIncl+col] is the byte offset (into includeRaw, a view over the + // docmap bytes == pkRaw) of column col's [nullFlag][u32 len][content] entry, decoded on + // demand by includeVal(ord, col). + // The docmap include section splits columns into a dense stride-addressed FIXED region + // (the integer family — no per-doc offset needed, computed by stride) and an + // offset-addressed VARLENA region (varchar/char). So a numeric-only INCLUDE index keeps + // ZERO resident offset entries; only varlena columns cost a resident offset table. + includeTypes []int32 // types.T of each INCLUDE column (len = nIncl, col order) + includeVals [][]any // build-side: ord -> per-column values (nil element = NULL) + includeRaw []byte // loaded-side: docmap bytes view (== pkRaw), mmap-backed on a base + includeLay includeLayout // loaded-side: derived per-column fixed/varlena addressing + // loaded-side: byte offset of the FIXED region within includeRaw; the VARLENA offset table + // (ord*nVarlena+varlenaIdx -> offset). includeVarOffsets is nil when all columns are fixed. + includeFixedStart int + includeVarOffsets []int32 + + // term dict, BUILD-side representation — dictionary-free, keyed by the + // indexed term string. `terms` accumulates postings during build and gives + // O(1) exact lookup; `sortedTerms` is the ascending key list. On serialize + // these feed buildTermDictFST (termdict.go), whose vellum FST is the compact + // on-disk / loaded form used for query lookup + prefix. Kept consistent: + // every key in `terms` appears once in `sortedTerms`. + terms map[string]*termPostings + sortedTerms []string + + // term dict, LOADED-side representation — set by Deserialize, nil on a + // build-side segment. `dict` is the vellum FST mapping term → the BYTE OFFSET of + // that term's self-contained directory entry in `ranking`. A loaded segment does + // NOT expand any term at load: query lookup (LookupLoaded) decodes just the touched + // term's directory entry from `ranking` on demand and points its blocks/positions at + // `blocks`/`positions` — so the resident directory heap is O(the current query), not + // O(vocabulary). `ranking`/`blocks`/`positions` are views into the mmap/blob (kept + // alive by mmapData or GC). The build-side `terms` map is left nil. + dict *termDict + ranking, blocks, positions []byte + + // mmapData is the shared read-only mmap of a base segment's on-disk file: the + // FST, the compressed docID/tf blocks, and the compressed positions section are + // all views into it (page-cache-backed, reclaimable, shared by all concurrent + // queries — no copy, no off-heap). mmapPath is that file (empty for the anonymous + // SSD file, whose inode is freed by munmap). Both are released by Free() under the + // cache's eviction write-lock (no reader in flight). nil on a build-side or + // in-memory (tail) segment, whose bytes are GC-managed Go slices. + mmapData []byte + mmapPath string +} + +// numDocs is the segment's document count, valid for both a build-side segment (== len(pks)) +// and a loaded segment (== N, where pks is nil and pk(ord) decodes from pkRaw). +func (s *Segment) numDocs() int { return int(s.N) } + +// pk returns the original pk value at ord. Build-side segments read the resident pks +// slice; loaded segments decode on demand from pkRaw at pkOffsets[ord] (a length-prefixed +// pk), so no O(docs) []any is held resident. pkType is validated once at decodeDocmap, so +// decodePk cannot fail here for a well-formed segment. +func (s *Segment) pk(ord int64) any { + if s.pks != nil { + return s.pks[ord] + } + off := int(s.pkOffsets[ord]) + l := int(binary.LittleEndian.Uint32(s.pkRaw[off:])) + v, _ := decodePk(s.PkType, s.pkRaw[off+4:off+4+l]) + return v +} + +// includeLayout is the derived per-column addressing for the docmap include section: a dense +// stride-addressed FIXED region (the integer family) plus an offset-addressed VARLENA region +// (varchar/char). Fixed columns need NO per-doc offset — their value sits at a computed +// (ord*fixedStride + maskBytes + fixedValOff[col]) position — so only varlena columns cost a +// resident offset table. Derived once from the column types (build encode + load decode). +type includeLayout struct { + fixedIdx []int // per col: index among fixed cols (0..nFixed-1), or -1 if varlena + varlenaIdx []int // per col: index among varlena cols (0..nVarlena-1), or -1 if fixed + fixedValOff []int // per col: byte offset of its value within a row's fixed value region (fixed cols) + fixedWidth []int // per col: byte width (fixed cols) + nFixed int + nVarlena int + maskBytes int // ceil(nFixed/8): the per-row NULL bitmask for fixed cols + fixedStride int // maskBytes + sum(fixedWidth): bytes per doc in the fixed region +} + +// computeIncludeLayout derives the fixed/varlena addressing from the INCLUDE column types. +func computeIncludeLayout(types []int32) includeLayout { + l := includeLayout{ + fixedIdx: make([]int, len(types)), + varlenaIdx: make([]int, len(types)), + fixedValOff: make([]int, len(types)), + fixedWidth: make([]int, len(types)), + } + valOff := 0 + for c, t := range types { + l.fixedIdx[c], l.varlenaIdx[c] = -1, -1 + if w, ok := fixedPkByteWidth(t); ok { + l.fixedIdx[c] = l.nFixed + l.fixedWidth[c] = w + l.fixedValOff[c] = valOff + valOff += w + l.nFixed++ + } else { + l.varlenaIdx[c] = l.nVarlena + l.nVarlena++ + } + } + l.maskBytes = (l.nFixed + 7) / 8 + l.fixedStride = l.maskBytes + valOff + return l +} + +// nIncludeCols is the number of INCLUDE columns carried by this segment (0 = none). +func (s *Segment) nIncludeCols() int { return len(s.includeTypes) } + +// decodeInclude returns the doc's INCLUDE column values (all columns, in index order; a nil +// element = SQL NULL), or nil when the segment has no INCLUDE columns. Carried out with a +// search Result so a covering query needs no base-table JOIN. +func (s *Segment) decodeInclude(ord int64) []any { + n := len(s.includeTypes) + if n == 0 { + return nil + } + out := make([]any, n) + for c := 0; c < n; c++ { + if v, isNull, err := s.includeVal(ord, c); err == nil && !isNull { + out[c] = v + } + } + return out +} + +// includeRawAt locates INCLUDE column col at ord in a LOADED segment (includeVals == nil) +// and returns the raw value bytes — for a FIXED column the width bytes, for a VARLENA +// column the content WITHOUT its [nullFlag][u32 len] prefix — whether the value is SQL NULL, +// and an error. It is the single decode primitive shared by includeVal (which then decodePk's +// the bytes) and appendIncludeTo (which copies them box-free). +// +// Belt-and-suspenders bounds (F3): every computed offset into includeRaw / includeVarOffsets +// is validated against the slice length, and a malformed/out-of-range segment returns an +// error rather than panicking on an out-of-range slice. Callers must pass col in [0,nIncl); +// includeRawAt still re-checks it defensively. The returned byte slice aliases includeRaw +// (mmap-backed on a loaded base segment) — the caller must copy before the segment is freed. +func (s *Segment) includeRawAt(ord int64, col int) (content []byte, isNull bool, err error) { + if col < 0 || col >= len(s.includeTypes) || ord < 0 { + return nil, false, moerr.NewInternalErrorNoCtxf("fulltext2: include access out of range (ord=%d col=%d)", ord, col) + } + lay := &s.includeLay + n := len(s.includeRaw) + if fi := lay.fixedIdx[col]; fi >= 0 { + // FIXED region: value at a stride-computed position, NULL via the per-row bitmask. + rowBase := s.includeFixedStart + int(ord)*lay.fixedStride + if rowBase < 0 || rowBase+lay.fixedStride > n { + return nil, false, moerr.NewInternalErrorNoCtxf("fulltext2: include fixed row out of range (ord=%d col=%d)", ord, col) + } + if s.includeRaw[rowBase+fi/8]&(1<<(uint(fi)&7)) != 0 { + return nil, true, nil + } + valOff := rowBase + lay.maskBytes + lay.fixedValOff[col] + w := lay.fixedWidth[col] + if valOff < 0 || valOff+w > n { // within rowBase+fixedStride, but re-check the exact span + return nil, false, moerr.NewInternalErrorNoCtxf("fulltext2: include fixed value out of range (ord=%d col=%d)", ord, col) + } + return s.includeRaw[valOff : valOff+w], false, nil + } + // VARLENA region: offset-addressed [nullFlag][u32 len][content]. + oi := int(ord)*lay.nVarlena + lay.varlenaIdx[col] + if oi < 0 || oi >= len(s.includeVarOffsets) { + return nil, false, moerr.NewInternalErrorNoCtxf("fulltext2: include varlena offset out of range (ord=%d col=%d)", ord, col) + } + off := int(s.includeVarOffsets[oi]) + if off < 0 || off >= n { + return nil, false, moerr.NewInternalErrorNoCtxf("fulltext2: include varlena entry out of range (ord=%d col=%d)", ord, col) + } + if s.includeRaw[off] == includeNull { + return nil, true, nil + } + if off+5 > n { // [nullFlag(1)][u32 len(4)] + return nil, false, moerr.NewInternalErrorNoCtxf("fulltext2: include varlena header out of range (ord=%d col=%d)", ord, col) + } + l := int(binary.LittleEndian.Uint32(s.includeRaw[off+1:])) + if l < 0 || off+5+l > n { + return nil, false, moerr.NewInternalErrorNoCtxf("fulltext2: include varlena content out of range (ord=%d col=%d)", ord, col) + } + return s.includeRaw[off+5 : off+5+l], false, nil +} + +// includeVal returns the value of INCLUDE column col at ord and whether it is SQL NULL. +// Build-side reads the resident includeVals; a loaded segment decodes on demand via +// includeRawAt (bounds-checked) + decodePk. Returns (nil, true, nil) when the segment has +// no INCLUDE columns. +func (s *Segment) includeVal(ord int64, col int) (any, bool, error) { + nIncl := len(s.includeTypes) + if nIncl == 0 || col < 0 || col >= nIncl { + return nil, true, nil + } + if s.includeVals != nil { // build-side + var v any + if int(ord) < len(s.includeVals) && col < len(s.includeVals[ord]) { + v = s.includeVals[ord][col] + } + return v, v == nil, nil + } + content, isNull, err := s.includeRawAt(ord, col) + if err != nil || isNull { + return nil, isNull, err + } + v, err := decodePk(s.includeTypes[col], content) + if err != nil { + return nil, false, err + } + return v, false, nil +} + +// appendIncludeTo decodes INCLUDE column col at ord straight into k in the box-free +// ColumnBuffer form (the mirror of appendPkTo, extended for NULLs): a fixed-width column +// contributes its width bytes, a varlena column a [u32 len][content] entry, and a NULL is +// recorded in k.Nulls with a well-formed placeholder (width zero-bytes / [u32 0]) so the +// Data cursor stays aligned for AppendColumnBuffer. k.Type MUST be col's type. Returns an +// error only on a malformed/out-of-range segment (includeRawAt bounds); the caller stops +// the stream so a torn batch is never emitted. +func (s *Segment) appendIncludeTo(k *vectorindex.ColumnBuffer, col int, ord int64) error { + fixedW, fixed := fixedPkByteWidth(int32(k.Type)) + if s.includeVals != nil { // build-side: re-encode the already-boxed value (no NEW box) + var v any + if int(ord) < len(s.includeVals) && col >= 0 && col < len(s.includeVals[ord]) { + v = s.includeVals[ord][col] + } + if v == nil { + cbAppendNull(k, fixedW, fixed) + return nil + } + content, err := encodePk(int32(k.Type), v) + if err != nil { + return err + } + cbAppendVal(k, content, fixedW, fixed) + return nil + } + content, isNull, err := s.includeRawAt(ord, col) + if err != nil { + return err + } + if isNull { + cbAppendNull(k, fixedW, fixed) + return nil + } + cbAppendVal(k, content, fixedW, fixed) + return nil +} + +// cbAppendVal appends a non-NULL value's bytes to a nullable ColumnBuffer: fixed types the +// content verbatim (exactly fixedW bytes), varlena types a [u32 len][content] entry. It +// keeps k.Nulls parallel to k.N once Nulls has been materialized (a prior NULL). +func cbAppendVal(k *vectorindex.ColumnBuffer, content []byte, fixedW int, fixed bool) { + if fixed { + k.Data = append(k.Data, content...) // exactly fixedW bytes + } else { + var lp [4]byte + binary.LittleEndian.PutUint32(lp[:], uint32(len(content))) + k.Data = append(k.Data, lp[:]...) + k.Data = append(k.Data, content...) + } + if k.Nulls != nil { + k.Nulls = append(k.Nulls, false) + } + k.N++ +} + +// appendAnyInclude appends a boxed INCLUDE value (or nil ⇒ SQL NULL) to a nullable +// ColumnBuffer, re-encoding via encodePk — the boxed-source analogue of appendIncludeTo, +// used by the LIMIT path whose top-k already materialized boxed Result.Include values. +// k.Type MUST be the column's type. An encode failure is RETURNED (not silently coerced to +// NULL): the value should be encodable (INCLUDE types are validated at CREATE), so a failure +// means real corruption — surface it, matching the streaming path (appendIncludeTo) which +// stops the stream on the same error rather than projecting a wrong NULL. +func appendAnyInclude(k *vectorindex.ColumnBuffer, v any) error { + fixedW, fixed := fixedPkByteWidth(int32(k.Type)) + if v == nil { + cbAppendNull(k, fixedW, fixed) + return nil + } + content, err := encodePk(int32(k.Type), v) + if err != nil { + return err + } + cbAppendVal(k, content, fixedW, fixed) + return nil +} + +// cbAppendNull appends a SQL-NULL element: a well-formed placeholder in Data (width +// zero-bytes / a [u32 0] entry) so the Data cursor stays aligned, plus a true flag in +// k.Nulls (lazily materialized, backfilling the prior all-non-null elements). +func cbAppendNull(k *vectorindex.ColumnBuffer, fixedW int, fixed bool) { + if k.Nulls == nil { + k.Nulls = make([]bool, k.N, k.N+1) // backfill prior non-nulls as false + } + if fixed { + k.Data = append(k.Data, make([]byte, fixedW)...) + } else { + var lp [4]byte // [u32 0]: zero-length content + k.Data = append(k.Data, lp[:]...) + } + k.Nulls = append(k.Nulls, true) + k.N++ +} + +// Free releases a loaded segment's mmap (and its backing file, if linked). Safe on +// a build-side / in-memory tail segment (nil mmapData → no-op) and idempotent. After +// Free the segment must not be queried; the VectorIndexCache holds the write lock +// when calling Destroy, and single-shot loaders (compact) Free after they finish +// reading. The loaded posting blocks (blockData) are views into mmapData, so +// munmap reclaims them — there is no off-heap buffer to deallocate. +func (s *Segment) Free() { + if s.mmapData != nil { + _ = munmap(s.mmapData) + s.mmapData = nil + } + if s.mmapPath != "" { + _ = os.Remove(s.mmapPath) + s.mmapPath = "" + } + s.ranking, s.blocks, s.positions = nil, nil, nil +} + +// freeSegs frees every segment's off-heap buffers (nil-safe on build-side segs). +func freeSegs(segs []*Segment) { + for _, s := range segs { + if s != nil { + s.Free() + } + } +} + +// NewSegment returns an empty segment with the given id and pk type. Postings +// are added by the builder (a later slice); this is the shared zero value used +// by both the build sink and the loader. +func NewSegment(id string, pkType int32) *Segment { + return &Segment{ + Id: id, + PkType: pkType, + terms: make(map[string]*termPostings), + } +} + +// NumDocs returns the document count in this segment. +func (s *Segment) NumDocs() int64 { return s.N } + +// NumTerms returns the number of distinct terms in this segment. +func (s *Segment) NumTerms() int { return len(s.sortedTerms) } + +// Lookup returns the posting list for an exact term, or (nil, false) if the term +// is not in this segment. O(1). +func (s *Segment) Lookup(term string) (*termPostings, bool) { + p, ok := s.terms[term] + return p, ok +} + +// forEachPosting calls fn for every (term, posting-list) in the segment, whether +// build-side (terms map) or loaded-side (FST dict + loaded slices). Used by MERGE +// compaction to reconstruct each doc's terms from the positional postings. +func (s *Segment) forEachPosting(fn func(term string, tp *termPostings)) error { + if s.dict == nil { + for term, tp := range s.terms { + fn(term, tp) + } + return nil + } + // Stream the FST (every term, ascending) instead of materializing the whole + // vocabulary as a []string — MERGE reconstruction over a large index must not add an + // O(vocabulary) string spike on top of the reconstruction buckets. + return s.dict.forEachTerm(func(term string) error { + if tp, ok := s.LookupLoaded(term); ok { + fn(term, tp) + } + return nil + }) +} + +// PrefixRange returns the sorted terms of this segment that start with prefix, +// in ascending order — the enumeration the `word*` boolean operator expands into +// a disjunctive slot. An empty prefix returns all terms. O(log n) to locate the +// range start, then linear in the number of matches. +// +// It relies solely on the sorted key list, so it works uniformly for gojieba +// words and ngram bigrams (both live in the same dictionary-free term dict). +func (s *Segment) PrefixRange(prefix string) []string { + if prefix == "" { + return s.sortedTerms + } + // First index whose term >= prefix. + lo := sort.SearchStrings(s.sortedTerms, prefix) + hi := lo + for hi < len(s.sortedTerms) && hasPrefix(s.sortedTerms[hi], prefix) { + hi++ + } + return s.sortedTerms[lo:hi] +} + +// hasPrefix reports whether s begins with prefix. (Local rather than +// strings.HasPrefix to keep the hot prefix-scan allocation-free and explicit.) +func hasPrefix(s, prefix string) bool { + return len(s) >= len(prefix) && s[:len(prefix)] == prefix +} + +// setTerms installs the term dict from a built/loaded map and (re)derives the +// sorted key list. Used by the builder and the loader; kept here so the +// terms/sortedTerms consistency invariant lives in one place. +func (s *Segment) setTerms(terms map[string]*termPostings) { + s.terms = terms + s.sortedTerms = make([]string, 0, len(terms)) + for t := range terms { + s.sortedTerms = append(s.sortedTerms, t) + } + sort.Strings(s.sortedTerms) +} diff --git a/pkg/fulltext2/serialize.go b/pkg/fulltext2/serialize.go new file mode 100644 index 0000000000000..3a53015af6525 --- /dev/null +++ b/pkg/fulltext2/serialize.go @@ -0,0 +1,721 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "archive/tar" + "bytes" + "encoding/binary" + "fmt" + "hash/crc32" + "io" + "math" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" +) + +// Tar member names — one archive per segment (same multi-member shape as bm25). +const ( + memberDocmap = "docmap" // pkType + N + ord->pk + ord->docLen + memberTermDict = "termdict" // the vellum FST: term -> ordinal + memberPostings = "postings" // ranking DIRECTORY: per-term df, per-block max/skip meta (resident) + memberBlocks = "blocks" // per-term docID/tf blocks (delta+varint; mmap, block-decoded on demand) + memberPositions = "positions" // per-term compressed positions (phrase-only, lazy) +) + +// Checksum returns the CRC32 (IEEE) of the serialized bytes (matches bm25's +// Checksum so the shared storage/CDC framing verifies both engines uniformly). +func Checksum(b []byte) uint32 { return crc32.ChecksumIEEE(b) } + +// Serialize encodes the (build-side) segment into a tar archive of three +// members: docmap, the FST term dict, and the positional postings. Terms are +// written in sorted order and the FST maps each term to its ordinal (index into +// the postings member), so load resolves term -> ordinal -> posting list. +func (s *Segment) Serialize() ([]byte, error) { + var buf bytes.Buffer + tw := tar.NewWriter(&buf) + + docmap, err := s.encodeDocmap() + if err != nil { + return nil, err + } + if err = writeMember(tw, memberDocmap, docmap); err != nil { + return nil, err + } + + fst, ranking, blocks, positions, err := s.encodeTermsAndPostings() + if err != nil { + return nil, err + } + if err = writeMember(tw, memberTermDict, fst); err != nil { + return nil, err + } + if err = writeMember(tw, memberPostings, ranking); err != nil { + return nil, err + } + if err = writeMember(tw, memberBlocks, blocks); err != nil { + return nil, err + } + if err = writeMember(tw, memberPositions, positions); err != nil { + return nil, err + } + + if err = tw.Close(); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// Deserialize parses a tar archive produced by Serialize into a loaded, queryable +// segment (dict + loaded[] postings, NOT the build-side terms map). id is the +// segment id (carried from storage, not stored in the archive). AvgDocLen is left +// unset — it is a cross-segment aggregate computed when all segments are loaded +// (§4), not a per-segment value. +func Deserialize(id string, r io.Reader) (*Segment, error) { + // In-memory path (tail deltas, tests): read the whole archive to a Go slice and + // point the FST + positions at it (no mmap). Bases use the mmap path in storage. + data, err := io.ReadAll(r) + if err != nil { + return nil, err + } + s := &Segment{Id: id} + if err := s.decodeSegment(data); err != nil { + return nil, err + } + return s, nil +} + +// sliceMembers returns the FST / docmap / ranking / positions members as SLICES +// into data (zero-copy) by walking the tar and tracking the reader offset, so the +// same code serves an in-memory blob or an mmap'd file. +func sliceMembers(data []byte) (docmap, fst, ranking, blocks, positions []byte, err error) { + br := bytes.NewReader(data) + tr := tar.NewReader(br) + for { + h, e := tr.Next() + if e == io.EOF { + break + } + if e != nil { + return nil, nil, nil, nil, nil, e + } + off := int64(len(data)) - int64(br.Len()) // tar positions br at the content + if off < 0 || off+h.Size > int64(len(data)) { + return nil, nil, nil, nil, nil, moerr.NewInternalErrorNoCtx("fulltext2: member out of range") + } + seg := data[off : off+h.Size] + switch h.Name { + case memberDocmap: + docmap = seg + case memberTermDict: + fst = seg + case memberPostings: + ranking = seg + case memberBlocks: + blocks = seg + case memberPositions: + positions = seg + } + } + return docmap, fst, ranking, blocks, positions, nil +} + +// decodeSegment builds the loaded segment over data (an in-memory blob or an mmap): +// the ranking DIRECTORY (per-block skip/max meta) expands RESIDENT, while the FST, +// the docID/tf blocks, and the compressed positions stay as views into data (decoded +// on demand). Callers keep data alive (mmapData for a base, GC for a tail). +func (s *Segment) decodeSegment(data []byte) error { + docmap, fst, ranking, blocks, positions, err := sliceMembers(data) + if err != nil { + return err + } + if err := s.decodeDocmap(docmap); err != nil { + return err + } + if err := s.decodePostings(ranking, blocks, positions); err != nil { + return err + } + dict, err := loadTermDict(fst) + if err != nil { + return err + } + s.dict = dict + // s.N is set by decodeDocmap (loaded segments have pks==nil, so len(s.pks) is 0). + return nil +} + +func writeMember(tw *tar.Writer, name string, data []byte) error { + if err := tw.WriteHeader(&tar.Header{Name: name, Mode: 0o600, Size: int64(len(data)), Typeflag: tar.TypeReg}); err != nil { + return err + } + _, err := tw.Write(data) + return err +} + +// leBuf appends little-endian scalars without the per-call heap allocation +// binary.Write incurs (its `data any` boxes every scalar) — the bytes are +// identical to binary.Write(LittleEndian, ...). Mirrors bm25's leBuf. +type leBuf struct { + b bytes.Buffer + tmp [binary.MaxVarintLen64]byte +} + +func (w *leBuf) u32(v uint32) { binary.LittleEndian.PutUint32(w.tmp[:4], v); w.b.Write(w.tmp[:4]) } +func (w *leBuf) u64(v uint64) { binary.LittleEndian.PutUint64(w.tmp[:8], v); w.b.Write(w.tmp[:8]) } +func (w *leBuf) i32(v int32) { w.u32(uint32(v)) } +func (w *leBuf) i64(v int64) { w.u64(uint64(v)) } + +// uvarint appends v as a LEB128 varint (1–10 bytes). Used for the delta-encoded +// posting streams (docID gaps, position gaps) where values are small. +func (w *leBuf) uvarint(v uint64) { n := binary.PutUvarint(w.tmp[:], v); w.b.Write(w.tmp[:n]) } + +// ---- docmap: pkType + N + ord->pk + ord->docLen [+ INCLUDE section] ---- + +const ( + // docmapIncludeTag marks an optional INCLUDE-values section appended after docLen. + // Its presence (a byte remaining after docLen) is how a decoder tells an INCLUDE index + // from a plain one — fulltext2 is new, so there is no old format to stay compatible with; + // a non-INCLUDE docmap is byte-identical to before. The tag guards against a future + // trailing section being misread as INCLUDE values. + docmapIncludeTag byte = 0x49 // 'I' + includeNotNull byte = 0 // per-value flag + includeNull byte = 1 + // maxIncludeCols bounds the decoded column count so a corrupt header can't allocate an + // enormous offset table. Well above the DDL-time practical limit. + maxIncludeCols = 64 +) + +func (s *Segment) encodeDocmap() ([]byte, error) { + var w leBuf + w.i32(s.PkType) + w.i64(int64(len(s.pks))) + for _, pk := range s.pks { + if err := w.encodePkLen(s.PkType, pk); err != nil { + return nil, err + } + } + for _, dl := range s.docLen { + w.i32(dl) + } + // INCLUDE section (only when the index has INCLUDE columns): tag, nCols, per-col type, + // then a dense stride-addressed FIXED region (integer cols: per-doc [NULL bitmask][values]) + // followed by an offset-addressed VARLENA region (varchar/char cols: per-doc per-col + // [nullFlag][u32 len][content]). Fixed cols carry no per-doc offset (stride-computed), so a + // numeric-only INCLUDE index needs no resident offset table. + if nIncl := len(s.includeTypes); nIncl > 0 { + if len(s.includeVals) != len(s.pks) { + return nil, moerr.NewInternalErrorNoCtxf( + "fulltext2: includeVals len %d != docs %d", len(s.includeVals), len(s.pks)) + } + w.b.WriteByte(docmapIncludeTag) + w.u32(uint32(nIncl)) + for _, t := range s.includeTypes { + w.i32(t) + } + lay := computeIncludeLayout(s.includeTypes) + valRegion := lay.fixedStride - lay.maskBytes + // FIXED region. + for ord := range s.pks { + row := s.includeVals[ord] + mask := make([]byte, lay.maskBytes) + vals := make([]byte, valRegion) + for c, t := range s.includeTypes { + fi := lay.fixedIdx[c] + if fi < 0 { + continue + } + var v any + if c < len(row) { + v = row[c] + } + if v == nil { + mask[fi/8] |= 1 << (uint(fi) & 7) + continue // value bytes stay zero + } + vb, err := encodePk(t, v) + if err != nil { + return nil, err + } + if len(vb) != lay.fixedWidth[c] { + return nil, moerr.NewInternalErrorNoCtxf("fulltext2: include fixed col %d width %d != %d", c, len(vb), lay.fixedWidth[c]) + } + copy(vals[lay.fixedValOff[c]:], vb) + } + w.b.Write(mask) + w.b.Write(vals) + } + // VARLENA region. + if lay.nVarlena > 0 { + for ord := range s.pks { + row := s.includeVals[ord] + for c, t := range s.includeTypes { + if lay.varlenaIdx[c] < 0 { + continue + } + var v any + if c < len(row) { + v = row[c] + } + if v == nil { + w.b.WriteByte(includeNull) + continue + } + w.b.WriteByte(includeNotNull) + if err := w.encodePkLen(t, v); err != nil { + return nil, err + } + } + } + } + } + return w.b.Bytes(), nil +} + +func (s *Segment) decodeDocmap(data []byte) error { + if len(data) < 12 { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap too short") + } + s.PkType = int32(binary.LittleEndian.Uint32(data[0:4])) + n := int64(binary.LittleEndian.Uint64(data[4:12])) + if n < 0 { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap negative N") + } + s.N = n + // Build a pk offset table into the (view) docmap bytes rather than materializing N + // boxed `any` pks — pk(ord) decodes on demand from pkRaw. pkOffsets[i] is the byte + // offset of pk i's u32 length prefix within data; pkRaw retains the view so the pk + // bytes stay mmap-backed (base) / GC-alive (tail). This is the O(docs) resident-heap + // reduction: 4 B/doc (offset) instead of ~24 B/doc (interface + boxed value). + offs := make([]int32, n) + pos := 12 + for i := int64(0); i < n; i++ { + if pos+4 > len(data) { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap truncated pk length") + } + if pos > math.MaxInt32 { + // pkOffsets is []int32; a >2GB docmap (e.g. huge blob pks) would wrap the offset + // negative and panic in pk(ord). Fail loudly instead. Bounded by max_index_capacity + // docs, so this only trips with very large per-pk payloads. + return moerr.NewInternalErrorNoCtx("fulltext2: docmap pk section exceeds 2GB") + } + offs[i] = int32(pos) + l := int(binary.LittleEndian.Uint32(data[pos:])) + // For a fixed-width pk type the stored length MUST equal the type width; a + // corrupt smaller length would pass the pos bound below but then make pk(ord) + // read past its slice (LittleEndian.Uint64 on <8 bytes panics). Validate here at + // load so corruption fails cleanly instead of crashing a query goroutine. + if w, fixed := fixedPkByteWidth(s.PkType); fixed && l != w { + return moerr.NewInternalErrorNoCtxf("fulltext2: docmap pk %d width %d != expected %d", i, l, w) + } + pos += 4 + l + if pos < 0 || pos > len(data) { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap truncated pk data") + } + } + // Validate pkType ONCE (decodePk rejects unsupported types) so pk(ord) can trust it; + // per-pk width was already checked in the loop above for fixed-width types. + if n > 0 { + off := int(offs[0]) + l := int(binary.LittleEndian.Uint32(data[off:])) + if _, err := decodePk(s.PkType, data[off+4:off+4+l]); err != nil { + return err + } + } + s.pks = nil + s.pkOffsets = offs + s.pkRaw = data // pkOffsets are absolute offsets into data + + // docLen: N contiguous little-endian int32 after the pk section. + if pos+int(n)*4 > len(data) { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap truncated docLen") + } + dl := make([]int32, n) + for i := int64(0); i < n; i++ { + dl[i] = int32(binary.LittleEndian.Uint32(data[pos+int(i)*4:])) + } + s.docLen = dl + pos += int(n) * 4 + + // Optional INCLUDE section. Absent ⇒ nothing after docLen (plain index). + if pos < len(data) { + if err := s.decodeIncludeSection(data, pos); err != nil { + return err + } + } + return nil +} + +// decodeIncludeSection parses the trailing INCLUDE-values section starting at pos: a tag, the +// column count + types, then the dense FIXED region (skipped by stride — no offsets) and the +// VARLENA region (for which it builds an (ord*nVarlena+varlenaIdx)->offset table into `data`, +// a view; mmap-backed for a base). Fixed values are read directly by stride at query time; no +// value is materialized at load, and a numeric-only INCLUDE index builds NO offset table. +func (s *Segment) decodeIncludeSection(data []byte, pos int) error { + if data[pos] != docmapIncludeTag { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap unknown trailing section") + } + pos++ + if pos+4 > len(data) { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap truncated include header") + } + nIncl := int(binary.LittleEndian.Uint32(data[pos:])) + pos += 4 + if nIncl <= 0 || nIncl > maxIncludeCols { + return moerr.NewInternalErrorNoCtxf("fulltext2: docmap bad include col count %d", nIncl) + } + itypes := make([]int32, nIncl) + for c := 0; c < nIncl; c++ { + if pos+4 > len(data) { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap truncated include types") + } + itypes[c] = int32(binary.LittleEndian.Uint32(data[pos:])) + pos += 4 + } + lay := computeIncludeLayout(itypes) + + // FIXED region: N rows × fixedStride bytes, read directly by stride at query time. + fixedStart := pos + fixedSize := int(s.N) * lay.fixedStride + if fixedSize < 0 || fixedStart+fixedSize > len(data) { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap truncated include fixed region") + } + pos += fixedSize + + // VARLENA region: build the offset table (only varlena cols cost resident offsets). + var varOffs []int32 + if lay.nVarlena > 0 { + varOffs = make([]int32, int(s.N)*lay.nVarlena) + for ord := int64(0); ord < s.N; ord++ { + for vi := 0; vi < lay.nVarlena; vi++ { + if pos >= len(data) { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap truncated include varlena values") + } + if pos > math.MaxInt32 { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap include section exceeds 2GB") + } + varOffs[int(ord)*lay.nVarlena+vi] = int32(pos) + flag := data[pos] + pos++ + if flag == includeNull { + continue + } + if flag != includeNotNull { + return moerr.NewInternalErrorNoCtxf("fulltext2: docmap bad include null-flag %d", flag) + } + if pos+4 > len(data) { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap truncated include value length") + } + l := int(binary.LittleEndian.Uint32(data[pos:])) + pos += 4 + l + if pos < 0 || pos > len(data) { + return moerr.NewInternalErrorNoCtx("fulltext2: docmap truncated include value data") + } + } + } + } + s.includeTypes = itypes + s.includeRaw = data + s.includeLay = lay + s.includeFixedStart = fixedStart + s.includeVarOffsets = varOffs + return nil +} + +// ---- termdict (FST) + postings ---- + +// encodeTermsAndPostings builds the FST (term -> ordinal) and THREE posting sections +// in one pass over the sorted terms (ordinal i in the FST is posting list i): +// +// - RANKING directory (kept RESIDENT at load): version, nterms, then per term +// df(varint), nblk(varint), the term's absolute base offsets into the blocks/positions +// sections + term-level maxTf/minDocLen, and per BlockSize-doc block +// {lastDocGap(varint, from the previous block's last ord), maxTf(1 byte), +// minDocLn(varint), blkByteLen(varint), posByteLen(varint)}. This is the Block-Max +// skip metadata + a directory into the blocks section; it is all WAND needs to prune, +// and it is O(df/BlockSize) — ~1/128 the postings. Each entry is SELF-CONTAINED and the +// FST value is its BYTE OFFSET, so LookupLoaded decodes one term's directory on demand +// (resident directory heap is O(the current query), not O(vocabulary)). +// - BLOCKS (mmap, block-decoded on demand): per term, per block, the docID GAPS +// (varint, from the previous block's last ord → tiny deltas) then the block's raw +// tf bytes. The WAND cursor decodes only the blocks its walk lands on; docIDs +// never fully expand into RAM at load (the OOM-scaling fix). +// - POSITIONS (mmap, phrase-only): per term (same order) per doc pc(varint) + +// position GAPS(varint). Never touched by ranking. +// +// Delta+varint shrinks docIDs/positions ~4×; blocking docIDs (vs one flat per-term +// stream) is what lets the loader keep them on the mmap and random-access one block. +// +// postingsFormatV1 is the single, current on-disk format for BOTH base index segments and +// CDC tail segments (they share this serialization). fulltext2 is experimental and +// fresh-deploy-only, so there is no migration and the version is NOT advanced as the engine +// evolves — the version byte is only a stale-blob guard. New per-doc data (e.g. INCLUDE +// column values) is added in the docmap member (presence-tagged), not by bumping this. +const postingsFormatV1 byte = 1 + +func (s *Segment) encodeTermsAndPostings() (fst, ranking, blocks, positions []byte, err error) { + n := len(s.sortedTerms) + values := make([]uint64, n) + var w, bw, pw leBuf // w=ranking directory, bw=blocks, pw=positions + w.b.WriteByte(postingsFormatV1) + w.i64(int64(n)) + for i, term := range s.sortedTerms { + tp := s.terms[term] + if tp.blockLastDoc == nil { // ensure the block-max directory (build normally sets it) + deriveTermStats(tp, s.docLen) + } + df := len(tp.docIDs) + nblk := (df + BlockSize - 1) / BlockSize + // Self-contained entry: the FST maps term -> this byte offset, and the entry + // carries its own blocks/positions base offsets + term-level max/min so it can be + // decoded in isolation (no walk of prior terms). + values[i] = uint64(w.b.Len()) + w.uvarint(uint64(df)) + w.uvarint(uint64(nblk)) + w.uvarint(uint64(bw.b.Len())) // blockDataBase: absolute offset into the blocks section + w.uvarint(uint64(pw.b.Len())) // posRawBase: absolute offset into the positions section + w.b.WriteByte(tp.maxTf) // term-level max tf (was recomputed at load in V5) + w.uvarint(uint64(tp.minDocLen)) + var prevLast int64 + for b := 0; b < nblk; b++ { + lo := b * BlockSize + hi := lo + BlockSize + if hi > df { + hi = df + } + // block bytes: docID gaps (from prevLast) then raw tfs. + blkStart := bw.b.Len() + prev := prevLast + for j := lo; j < hi; j++ { + bw.uvarint(uint64(tp.docIDs[j] - prev)) + prev = tp.docIDs[j] + } + bw.b.Write(tp.tfs[lo:hi]) + // this block's positions (parallel to its docs) — grouped per block so the + // directory can record each block's byte length, making positions block- + // SEEKABLE for the phrase cursor (posRaw bytes are unchanged vs V4: still + // per-doc pc + ascending gaps in doc order, so materializePositions is + // unaffected). V4 wrote them once per term after all blocks. + posStart := pw.b.Len() + if tp.positions != nil { // position-free segments omit the positional payload + for j := lo; j < hi; j++ { + pos := tp.positions[j] + pw.uvarint(uint64(len(pos))) + var pp int32 + for _, p := range pos { // ascending positions → non-negative gaps + pw.uvarint(uint64(p - pp)) + pp = p + } + } + } + // directory entry. + w.uvarint(uint64(tp.blockLastDoc[b] - prevLast)) // lastDocGap + w.b.WriteByte(tp.blockMaxTf[b]) + w.uvarint(uint64(tp.blockMinDocLn[b])) + w.uvarint(uint64(bw.b.Len() - blkStart)) // this block's docID/tf byte length + w.uvarint(uint64(pw.b.Len() - posStart)) // this block's positions byte length (V5) + prevLast = tp.blockLastDoc[b] + } + } + if fst, err = buildTermDictFST(s.sortedTerms, values); err != nil { + return nil, nil, nil, nil, err + } + return fst, w.b.Bytes(), bw.b.Bytes(), pw.b.Bytes(), nil +} + +// decodePostings BINDS the loaded segment to its (mmap'd) ranking/blocks/positions +// sections WITHOUT expanding any term. V6 entries are self-contained and reachable by +// byte offset (the FST value), so terms decode lazily in LookupLoaded — the resident +// directory heap is O(the current query), not O(vocabulary). +func (s *Segment) decodePostings(ranking, blocks, positions []byte) error { + if len(ranking) == 0 { + s.ranking, s.blocks, s.positions = nil, nil, nil + return nil + } + if len(ranking) < 1+8 { + return moerr.NewInternalErrorNoCtx("fulltext2: ranking blob too short") + } + if ranking[0] != postingsFormatV1 { + return moerr.NewInternalErrorNoCtx(fmt.Sprintf("fulltext2: unsupported postings format %d", ranking[0])) + } + s.ranking, s.blocks, s.positions = ranking, blocks, positions + return nil +} + +// decodeTermEntry lazily decodes ONE term's self-contained directory entry at byte +// offset `off` in s.ranking (the FST value) into a transient termPostings whose +// blockData/posRaw are views into s.blocks/s.positions. Callers hold the result for the +// query's lifetime (WAND/phrase cursors, evalClause), so the entry decodes at most a few +// times per query and never persists — resident directory heap is O(query), not O(vocab). +// Returns (nil,false) on a corrupt/out-of-bounds entry (defense-in-depth on already-CRC'd +// data). +func (s *Segment) decodeTermEntry(off int) (*termPostings, bool) { + r := s.ranking + if off < 0 || off >= len(r) { + return nil, false + } + p := off + uv := func() (uint64, bool) { + v, n := binary.Uvarint(r[p:]) + if n <= 0 { + return 0, false + } + p += n + return v, true + } + dfu, ok := uv() + if !ok { + return nil, false + } + nblku, ok := uv() + if !ok { + return nil, false + } + baseB, ok := uv() // blockDataBase: absolute offset into s.blocks + if !ok { + return nil, false + } + baseP, ok := uv() // posRawBase: absolute offset into s.positions + if !ok { + return nil, false + } + if p >= len(r) { + return nil, false + } + termMaxTf := r[p] + p++ + minDLu, ok := uv() + if !ok { + return nil, false + } + // A df/nblk larger than the blocks section is impossible (each posting is >= 1 byte). + if dfu > uint64(len(s.blocks)) || nblku > uint64(len(s.blocks)) { + return nil, false + } + df, nblk := int(dfu), int(nblku) + tp := &termPostings{ndoc: df, maxTf: termMaxTf, minDocLen: int32(minDLu)} + if nblk == 0 { + return tp, true + } + tp.blockLastDoc = make([]int64, nblk) + tp.blockMaxTf = make([]uint8, nblk) + tp.blockMinDocLn = make([]int32, nblk) + tp.blockOff = make([]int64, nblk+1) // byte offsets RELATIVE to this term's blockData + tp.blockPosOff = make([]int64, nblk+1) // byte offsets RELATIVE to this term's posRaw + var prevLast, cb, cpos int64 + for b := 0; b < nblk; b++ { + gap, ok := uv() + if !ok { + return nil, false + } + prevLast += int64(gap) + tp.blockLastDoc[b] = prevLast + if p >= len(r) { + return nil, false + } + tp.blockMaxTf[b] = r[p] + p++ + mdl, ok := uv() + if !ok { + return nil, false + } + tp.blockMinDocLn[b] = int32(mdl) + blkLen, ok := uv() + if !ok || blkLen > uint64(len(s.blocks)) { + return nil, false + } + tp.blockOff[b] = cb + cb += int64(blkLen) + posLen, ok := uv() + if !ok || posLen > uint64(len(s.positions)) { + return nil, false + } + tp.blockPosOff[b] = cpos + cpos += int64(posLen) + } + tp.blockOff[nblk] = cb + tp.blockPosOff[nblk] = cpos + // blockData/posRaw are views into the mmap sections at this term's stored bases. + if int64(baseB)+cb > int64(len(s.blocks)) || int64(baseP)+cpos > int64(len(s.positions)) { + return nil, false + } + tp.blockData = s.blocks[baseB : int64(baseB)+cb] + tp.posRaw = s.positions[baseP : int64(baseP)+cpos] + return tp, true +} + +// deriveTermStats fills tp's raw, scorer-agnostic score-UB fields from its +// postings + docLen: the term-level maxTf/minDocLen AND the per-block Block-Max +// skip metadata (blockLastDoc/blockMaxTf/blockMinDocLn), one entry per +// ceil(df/BlockSize). Both are idf/avgdl-free so one segment serves BM25 and +// TF-IDF; the active scorer derives its impact bound at query time. Computed in a +// single pass (the term-level max/min is the max/min over the block stats), and +// called from BOTH the builder and Deserialize — the direct analogue of bm25's +// WandModel.finalizeScoring. Assumes df >= 1 (a term with no postings is never +// stored). +func deriveTermStats(tp *termPostings, docLen []int32) { + df := len(tp.docIDs) + tp.ndoc = df + if df == 0 { + return + } + nblk := (df + BlockSize - 1) / BlockSize + tp.blockLastDoc = make([]int64, nblk) + tp.blockMaxTf = make([]uint8, nblk) + tp.blockMinDocLn = make([]int32, nblk) + var termMaxTf uint8 + termMinDL := int32(math.MaxInt32) + for b := 0; b < nblk; b++ { + lo := b * BlockSize + hi := lo + BlockSize + if hi > df { + hi = df + } + var maxTf uint8 + minDL := int32(math.MaxInt32) + for i := lo; i < hi; i++ { + if tp.tfs[i] > maxTf { + maxTf = tp.tfs[i] + } + if dl := docLen[tp.docIDs[i]]; dl < minDL { + minDL = dl + } + } + tp.blockLastDoc[b] = tp.docIDs[hi-1] // ascending → last is the block's max ord + tp.blockMaxTf[b] = maxTf + tp.blockMinDocLn[b] = minDL + if maxTf > termMaxTf { + termMaxTf = maxTf + } + if minDL < termMinDL { + termMinDL = minDL + } + } + tp.maxTf = termMaxTf + tp.minDocLen = termMinDL +} + +// LookupLoaded resolves a term to its posting list on a LOADED segment (via the +// FST ordinal). Returns (nil, false) if the segment is not loaded or the term is +// absent. Query-side counterpart of the build-side Lookup. +func (s *Segment) LookupLoaded(term string) (*termPostings, bool) { + if s.dict == nil { + return nil, false + } + off, ok, err := s.dict.get(term) // V6: the FST value is the entry's byte offset in ranking + if err != nil || !ok { + return nil, false + } + return s.decodeTermEntry(int(off)) +} diff --git a/pkg/fulltext2/sink.go b/pkg/fulltext2/sink.go new file mode 100644 index 0000000000000..0bc6826e16f40 --- /dev/null +++ b/pkg/fulltext2/sink.go @@ -0,0 +1,106 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" +) + +// RowEvent is one source-table change fed to the build sink: an insert (Pk + +// Text) or a delete (Pk, Delete=true). An UPDATE is delivered as a delete of the +// old row followed by an insert of the new one — both for the same Pk. +type RowEvent struct { + Delete bool + Pk any + Text []byte +} + +// Sink accumulates a batch of row events and produces one segment (the inserted +// rows) plus a delete set. It is the engine-side of the maintenance path: the +// initial full build feeds every source row through one Sink to make a tag=0 +// base; each subsequent CDC batch feeds its changes through a Sink to make a +// tag=1 tail segment + the batch's deletes. The Index then merges base + tails + +// the accumulated deletes, and liveness (highest Recency) resolves updates. +// +// A pk that is BOTH deleted and (re)inserted in the same batch is an UPDATE — the +// insert at this batch's recency supersedes older copies via liveness, so no +// delete tombstone is emitted for it. Only a pk that is deleted and NOT +// reinserted becomes a tombstone. +type Sink struct { + pkType int32 + tok tokenizer.Tokenizer + inserts []Doc + insertKeys map[any]bool + deleteKeys map[any]bool +} + +// NewSink creates a sink for a batch. pkType is the source pk's types.T; tok is +// the index's tokenizer (used for both this build and later query tokenization). +func NewSink(pkType int32, tok tokenizer.Tokenizer) *Sink { + return &Sink{ + pkType: pkType, + tok: tok, + insertKeys: make(map[any]bool), + deleteKeys: make(map[any]bool), + } +} + +// Add feeds one row event into the batch. +func (s *Sink) Add(ev RowEvent) { + key := normalizeKey(ev.Pk) + if ev.Delete { + s.deleteKeys[key] = true + return + } + s.inserts = append(s.inserts, Doc{Pk: ev.Pk, Text: ev.Text}) + s.insertKeys[key] = true +} + +// Build finalizes the batch into a segment (the inserted rows, at the given +// recency) and the tombstone set (pk key → recency) for pks deleted but not +// reinserted in this batch. +func (s *Sink) Build(id string, recency int64) (*Segment, map[any]int64, error) { + seg, err := BuildSegmentFromDocs(id, s.pkType, s.inserts, s.tok) + if err != nil { + return nil, nil, err + } + seg.Recency = recency + + var deletes map[any]int64 + for key := range s.deleteKeys { + if s.insertKeys[key] { + continue // reinserted in the same batch → an UPDATE, not a tombstone + } + if deletes == nil { + deletes = make(map[any]int64) + } + deletes[key] = recency + } + return seg, deletes, nil +} + +// MergeDeletes folds several per-batch delete maps into one (latest recency +// wins), for feeding NewIndex. +func MergeDeletes(maps ...map[any]int64) map[any]int64 { + out := make(map[any]int64) + for _, m := range maps { + for key, rec := range m { + if cur, ok := out[key]; !ok || rec > cur { + out[key] = rec + } + } + } + return out +} diff --git a/pkg/fulltext2/storage.go b/pkg/fulltext2/storage.go new file mode 100644 index 0000000000000..7626f9f8d1583 --- /dev/null +++ b/pkg/fulltext2/storage.go @@ -0,0 +1,877 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "context" + "fmt" + "os" + "path/filepath" + "strings" + "sync" + + "github.com/google/uuid" + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/sqlquote" + "github.com/matrixorigin/matrixone/pkg/common/system" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/fileservice" + "github.com/matrixorigin/matrixone/pkg/util/executor" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + cuvscdc "github.com/matrixorigin/matrixone/pkg/vectorindex/cuvs" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" +) + +// TableConfig locates a fulltext2 index's persistent segment store + metadata +// table; it is the JSON const arg passed to the fulltext2_create / fulltext2_search +// TVFs. Mirrors bm25.wand.TableConfig. +type TableConfig struct { + DbName string `json:"db"` + SrcTable string `json:"src"` + IndexTable string `json:"index"` // chunk store (FullText2Index_TblType_Storage) + MetadataTable string `json:"metadata"` // metadata (FullText2Index_TblType_Metadata) + PKey string `json:"pkey"` + Parser string `json:"parser,omitempty"` + // Capacity is max_index_capacity: the create build splits the tag=0 base into + // sub-indexes of at most Capacity docs each (0 => DefaultBuildCapacity). + Capacity int64 `json:"capacity,omitempty"` + // PostingCapacity is max_postings_capacity: a segment also seals once it holds + // this many postings (term occurrences), whichever comes first with Capacity. + // Bounds per-segment build memory regardless of doc size (0 => DefaultPostingCapacity). + PostingCapacity int64 `json:"posting_capacity,omitempty"` + // PositionFree builds segments without the positional payload (bag-of-words + // retrieval only; ~half the footprint, FST kept). Sourced from the persisted + // position_free algo_param so every build path (create, CDC tail, compact) agrees. + PositionFree bool `json:"position_free,omitempty"` + FromSource bool `json:"from_source,omitempty"` + // IncludeTypes is the INCLUDE columns' types.T in column order. The build SQL passes the + // INCLUDE source columns as the trailing fulltext2_create args (after the text columns), + // so the TVF knows how many trailing argVecs are INCLUDE values (vs text) and their types + // to store the actual per-doc value in the docmap. nil ⇒ no INCLUDE columns. + IncludeTypes []int32 `json:"include_types,omitempty"` + // IncludeColumns is the INCLUDE columns' NAMES in column order — carried in the SEARCH + // cfg so Fulltext2Search.Search can map a covering query's RequestedIncludeColumns (by + // name) to each result's positional Include values. nil ⇒ no INCLUDE columns. + IncludeColumns []string `json:"include_columns,omitempty"` +} + +// runSql / runStreamingSql indirect the sqlexec executor entry points so unit tests can +// mock the DB round-trips (metadata reads, base/tail chunk loads, budget gates, +// compaction). Production points them at the real executor; tests swap them for fakes +// that return synthetic result batches, optionally dispatching by SQL text. +var ( + runSql = sqlexec.RunSql + runStreamingSql = sqlexec.RunStreamingSql +) + +// SubIndexId is the index_id for the i-th tag=0 base sub-index of a build +// identified by uid (which must carry a per-build-unique component so concurrent +// builds never collide). Load enumerates ids from the metadata table. +func SubIndexId(uid string, i int) string { return fmt.Sprintf("%s:%d", uid, i) } + +// maxInsertTuples bounds the (index_id, chunk_id, data, tag) rows per INSERT (base-segment and +// batched CDC-tail persist). Its primary job is cutting RunSql round-trips — many tiny frames become +// a few multi-row INSERTs. It is kept MODERATE (500, well below hnsw's 2000 in +// pkg/vectorindex/hnsw/model.go) because each row's load_file data flows into the storage engine's +// ASYNC object writer (file/S3), which buffers-and-flushes and thus accumulates memory as rows are +// pushed; fulltext2 CDC persist is a BACKGROUND op, so a smaller batch hands that writer a bounded +// slice (<= maxInsertTuples*MaxChunkSize ≈ 32 MiB) per statement instead of a large burst. NOTE: this is only +// a secondary throttle — the dominant memory bound is the TOTAL data pushed per iteration (the whole +// tail delta), which the per-iteration work bound (not batch size) is what actually caps. +const maxInsertTuples = 500 + +// ToInsertSqls serializes the segment, spills it to a temp file under the LOCAL +// fileservice's __fulltext2 dir (load_file reads it back by path), and emits the +// SQL to persist it: one metadata row + the bytes split into <= MaxChunkSize +// (index_id, chunk_id, data, tag) rows read via load_file. tag=0 is a compacted +// base (sync CREATE/REINDEX build); tag=1 is a CDC delta. sqlproc resolves the LOCAL +// SSD spill dir (falls back to /tmp when none is attached). The returned cleanup MUST +// run after the SQLs execute (they read the temp file at execution). +func (s *Segment) ToInsertSqls(sqlproc *sqlexec.SqlProcess, cfg TableConfig, ts int64, tag int) (sqls []string, cleanup func(), err error) { + buf, err := s.Serialize() + if err != nil { + return nil, nil, err + } + checksum := vectorindex.CheckSumFromBuffer(buf) + filesize := int64(len(buf)) + + fp, err := createLocalSpillFile(sqlproc, "ft2build") + if err != nil { + return nil, nil, err + } + path := fp.Name() + cleanup = func() { fp.Close(); os.Remove(path) } + if _, err = fp.Write(buf); err != nil { + cleanup() + return nil, nil, err + } + if err = fp.Sync(); err != nil { + cleanup() + return nil, nil, err + } + + metaTbl := sqlquote.QualifiedIdent(cfg.DbName, cfg.MetadataTable) + sqls = append(sqls, fmt.Sprintf("INSERT INTO %s (%s, %s, %s, %s, %s, %s) VALUES (%s, %d, %s, %d, %d, %d)", + metaTbl, + catalog.FullText2Index_TblCol_Metadata_Index_Id, catalog.FullText2Index_TblCol_Metadata_Timestamp, + catalog.FullText2Index_TblCol_Metadata_Checksum, catalog.FullText2Index_TblCol_Metadata_Filesize, + catalog.FullText2Index_TblCol_Metadata_Recency, catalog.FullText2Index_TblCol_Metadata_Nrow, + sqlquote.String(s.Id), ts, sqlquote.String(checksum), filesize, s.Recency, s.N)) + sqls = append(sqls, fileChunkInsertSqls(cfg, s.Id, 0, path, 0, int(filesize), tag)...) + return sqls, cleanup, nil +} + +// fileChunkRange is one contiguous [offset, offset+length) byte range of a spilled file to persist +// as storage rows: offset 0 for a whole-file base segment, or a frame's offset within a PACKED CDC +// spool file (many frames share one file). +type fileChunkRange struct { + path string + offset int64 + length int +} + +// framesInsertSqls emits the load_file storage-row tuples for a SEQUENCE of file ranges, each range +// split into <= MaxChunkSize rows, batched at maxInsertTuples rows per INSERT statement +// ACROSS range boundaries. chunk_ids are assigned contiguously from startChunkId in range order; +// the next free chunk_id is returned. Batching across ranges is what keeps a burst of tiny CDC tail +// frames to O(totalChunks/maxInsertTuples) statements instead of one INSERT per frame. Mirrors +// bm25.wand.FileChunkInsertSqls (single-range) generalized to many ranges. +func framesInsertSqls(cfg TableConfig, id string, startChunkId int64, ranges []fileChunkRange, tag int) (sqls []string, nextChunkId int64) { + prefix := fmt.Sprintf("INSERT INTO %s (%s, %s, %s, %s) VALUES ", + sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + catalog.FullText2Index_TblCol_Storage_Index_Id, catalog.FullText2Index_TblCol_Storage_Chunk_Id, + catalog.FullText2Index_TblCol_Storage_Data, catalog.FullText2Index_TblCol_Storage_Tag) + var vals []string + chunkID := startChunkId + for _, r := range ranges { + for off := 0; off < r.length; off += vectorindex.MaxChunkSize { + sz := vectorindex.MaxChunkSize + if off+sz > r.length { + sz = r.length - off + } + url := fmt.Sprintf("file://%s?offset=%d&size=%d", r.path, r.offset+int64(off), sz) + vals = append(vals, fmt.Sprintf("(%s, %d, load_file(cast(%s as datalink)), %d)", + sqlquote.String(id), chunkID, sqlquote.String(url), tag)) + chunkID++ + if len(vals) == maxInsertTuples { + sqls = append(sqls, prefix+strings.Join(vals, ", ")) + vals = vals[:0] + } + } + } + if len(vals) > 0 { + sqls = append(sqls, prefix+strings.Join(vals, ", ")) + } + return sqls, chunkID +} + +// fileChunkInsertSqls splits a [baseOffset, baseOffset+dataLen) BYTE RANGE of a spilled file into +// <= MaxChunkSize storage rows read via load_file (no hex/unhex). Single-range convenience over +// framesInsertSqls (used by the base-segment persist). +func fileChunkInsertSqls(cfg TableConfig, id string, startChunkId int64, path string, baseOffset int64, dataLen int, tag int) []string { + sqls, _ := framesInsertSqls(cfg, id, startChunkId, []fileChunkRange{{path: path, offset: baseOffset, length: dataLen}}, tag) + return sqls +} + +// TailFileInsertSqls persists ONE spilled tag=1 CDC frame (index_id=CdcTailId) that lives at +// [offset, offset+frameLen) within a packed spool file. Its chunk rows are contiguous from +// startChunkId, so recency (chunk_id) ordering across frames is unchanged by the packing. +func TailFileInsertSqls(cfg TableConfig, startChunkId int64, path string, offset int64, frameLen int) []string { + return fileChunkInsertSqls(cfg, vectorindex.CdcTailId, startChunkId, path, offset, frameLen, int(vectorindex.Tag_CdcEvents)) +} + +// TailFramesInsertSqls persists ALL of an iteration's spilled tag=1 CDC frames as FEW multi-row +// INSERTs: the per-chunk load_file tuples of every frame, batched at maxInsertTuples rows per +// statement ACROSS frame boundaries — so N tiny frames cost ~N/maxInsertTuples statements (and +// RunSql round-trips) instead of one INSERT per frame. chunk_ids are contiguous from startChunkId in frame +// order (recency identical to a per-frame persist); the next free chunk_id (end of the tail range) +// is returned. +func TailFramesInsertSqls(cfg TableConfig, startChunkId int64, frames []TailSegment) (sqls []string, nextChunkId int64) { + ranges := make([]fileChunkRange, len(frames)) + for i, f := range frames { + ranges[i] = fileChunkRange{path: f.Path, offset: f.Offset, length: f.FrameLen} + } + return framesInsertSqls(cfg, vectorindex.CdcTailId, startChunkId, ranges, int(vectorindex.Tag_CdcEvents)) +} + +// DeleteSqls removes one index id's chunks + metadata row (rebuild idempotency). +func DeleteSqls(cfg TableConfig, id string) []string { + return []string{ + fmt.Sprintf("DELETE FROM %s WHERE %s = %s", sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + catalog.FullText2Index_TblCol_Storage_Index_Id, sqlquote.String(id)), + fmt.Sprintf("DELETE FROM %s WHERE %s = %s", sqlquote.QualifiedIdent(cfg.DbName, cfg.MetadataTable), + catalog.FullText2Index_TblCol_Metadata_Index_Id, sqlquote.String(id)), + } +} + +// DeleteAllBasesSqls removes every tag=0 base (all sub-index chunk rows + all +// metadata rows); the tag=1 CdcTail is untouched. Makes CREATE idempotent. +func DeleteAllBasesSqls(cfg TableConfig) []string { + return []string{ + fmt.Sprintf("DELETE FROM %s WHERE %s = %d", sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + catalog.FullText2Index_TblCol_Storage_Tag, int(vectorindex.Tag_ModelChunk)), + fmt.Sprintf("DELETE FROM %s WHERE TRUE", sqlquote.QualifiedIdent(cfg.DbName, cfg.MetadataTable)), + } +} + +// DeleteTailSqls removes the entire tag=1 CdcTail (a REINDEX discards the delta log). +func DeleteTailSqls(cfg TableConfig) []string { + return []string{ + fmt.Sprintf("DELETE FROM %s WHERE %s = %d", sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + catalog.FullText2Index_TblCol_Storage_Tag, int(vectorindex.Tag_CdcEvents)), + } +} + +// readMetadata fetches an index id's metadata (checksum + filesize + recency). +func readMetadata(sqlproc *sqlexec.SqlProcess, cfg TableConfig, id string) (checksum string, filesize, recency int64, found bool, err error) { + sql := fmt.Sprintf("SELECT %s, %s, %s FROM %s WHERE %s = %s", + catalog.FullText2Index_TblCol_Metadata_Checksum, catalog.FullText2Index_TblCol_Metadata_Filesize, + catalog.FullText2Index_TblCol_Metadata_Recency, + sqlquote.QualifiedIdent(cfg.DbName, cfg.MetadataTable), + catalog.FullText2Index_TblCol_Metadata_Index_Id, sqlquote.String(id)) + res, err := runSql(sqlproc, sql) + if err != nil { + return "", 0, 0, false, err + } + for _, bat := range res.Batches { + if bat == nil || bat.RowCount() == 0 { + continue + } + checksum = bat.Vecs[0].GetStringAt(0) + filesize = vector.GetFixedAtNoTypeCheck[int64](bat.Vecs[1], 0) + recency = vector.GetFixedAtNoTypeCheck[int64](bat.Vecs[2], 0) + found = true + break + } + res.Close() + return checksum, filesize, recency, found, nil +} + +// estBytesPerDocHeap approximates the Go-heap cost of loading ONE document's +// index-level metadata: the docmap slot (pk `any` + docLen) plus the per-doc +// liveness structures NewIndex builds from it (liveLoc/top map entries + a liveOrd +// bit). Base posting blocks are mmap'd (reclaimable OS page cache) and are excluded — +// they cannot OOM-kill the CN. It is deliberately generous so checkBaseLoadBudget +// fails a doomed load fast rather than letting it crash the node; a modest +// over-estimate only rejects a borderline load (recoverable via compaction / more +// memory), never silently OOMs. +const estBytesPerDocHeap = 256 + +// checkBaseLoadBudget fails fast if loading every tag=0 base's per-doc metadata into +// the Go heap would blow the CN memory budget — the base analogue of +// checkTailLoadBudget (which guards the tail). It gates on SUM(nrow) (doc count), NOT +// filesize: the bulk of a base (posting blocks) is mmap'd and reclaimable, so only the +// O(docs) docmap + NewIndex liveness maps land on the Go heap. Budget = MemoryTotal*0.8 +// - live Go heap (cgroup-aware). Turns a node-killing OOM into a clear, actionable +// error suggesting compaction. +func checkBaseLoadBudget(sqlproc *sqlexec.SqlProcess, cfg TableConfig) error { + // CAST AS SIGNED so the sum reads back as int64 regardless of how SUM types its + // result (GetFixedAtNoTypeCheck[int64] would misread a decimal vector). + sql := fmt.Sprintf("SELECT CAST(COALESCE(SUM(%s), 0) AS SIGNED) FROM %s", + catalog.FullText2Index_TblCol_Metadata_Nrow, sqlquote.QualifiedIdent(cfg.DbName, cfg.MetadataTable)) + res, err := runSql(sqlproc, sql) + if err != nil { + return err + } + defer res.Close() + var ndoc int64 + for _, bat := range res.Batches { + if bat == nil || bat.RowCount() == 0 { + continue + } + ndoc = vector.GetFixedAtNoTypeCheck[int64](bat.Vecs[0], 0) + } + need := ndoc * estBytesPerDocHeap + avail := int64(system.MemoryTotal())*8/10 - int64(system.MemoryGolang()) + if need > avail { + return moerr.NewInternalError(sqlproc.GetContext(), fmt.Sprintf( + "fulltext2 index %s.%s needs ~%d MB of heap for %d docs' metadata but only ~%d MB is free "+ + "(MemoryTotal*0.8 - Go heap). If the index has many deleted rows, MERGE "+ + "(ALTER TABLE ... ALTER REINDEX ... MERGE) reclaims them and may bring it under budget; "+ + "otherwise the live index is too large for this CN — add memory", + cfg.DbName, cfg.IndexTable, need>>20, ndoc, avail>>20)) + } + return nil +} + +// LoadAllBases loads every tag=0 base sub-index listed in the metadata table. +// Returns nil when no base was built (empty-table create → CDC-only index). +// +// NOTE: the heap-budget guard (checkBaseLoadBudget) is deliberately NOT called here. +// This is shared by the query load path AND CompactSegments (MERGE). Guarding it here +// would make the guard self-blocking — MERGE is the very remedy the guard's error +// suggests, but MERGE must load the bases to reclaim dead docs. The query path calls +// the guard itself (Fulltext2Search.Load); MERGE stays exempt so it can always run. +func LoadAllBases(sqlproc *sqlexec.SqlProcess, cfg TableConfig) ([]*Segment, error) { + idSQL := fmt.Sprintf("SELECT %s FROM %s", + catalog.FullText2Index_TblCol_Metadata_Index_Id, sqlquote.QualifiedIdent(cfg.DbName, cfg.MetadataTable)) + res, err := runSql(sqlproc, idSQL) + if err != nil { + return nil, err + } + var ids []string + for _, bat := range res.Batches { + if bat == nil { + continue + } + for i := 0; i < bat.RowCount(); i++ { + ids = append(ids, bat.Vecs[0].GetStringAt(i)) + } + } + res.Close() + + bases := make([]*Segment, 0, len(ids)) + for _, id := range ids { + m, lerr := LoadFromStorage(sqlproc, cfg, id) + if lerr != nil { + // Free the segments already mapped this call before bailing: each owns an + // mmap (+ a linked /tmp spill file on the fallback path), so returning without + // freeing leaks them, and a retrying caller (query reload / MERGE) accumulates + // both. Mirrors LoadTailSegments' freeSegs(bases) on its own error path. + freeSegs(bases) + return nil, lerr + } + bases = append(bases, m) + } + return bases, nil +} + +// LoadFromStorage reads an index id's metadata + chunks, verifies the checksum, +// and deserializes it. Chunks stream by chunk_id offset into a temp file so the +// mpool never holds the whole index. +func LoadFromStorage(sqlproc *sqlexec.SqlProcess, cfg TableConfig, id string) (*Segment, error) { + checksum, filesize, recency, found, err := readMetadata(sqlproc, cfg, id) + if err != nil { + return nil, err + } + if !found { + return nil, moerr.NewInternalError(sqlproc.GetContext(), fmt.Sprintf("fulltext2 index %s metadata not found", id)) + } + if filesize <= 0 { + return nil, moerr.NewInternalError(sqlproc.GetContext(), fmt.Sprintf("fulltext2 index %s has empty filesize", id)) + } + + // Materialize the segment on the fast LOCAL (SSD) fileservice so mmap page faults + // come off the 2 GB/s mount, not /tmp (128 MB/s on AWS). path=="" means an + // anonymous SSD file (unlinked; munmap frees the inode) — the same + // CreateAndRemoveFile the JOIN/mergeorder spill uses. + fp, path, err := createLocalTempFile(sqlproc, "ft2idx") + if err != nil { + return nil, err + } + cleanup := func() { + fp.Close() + if path != "" { + os.Remove(path) + } + } + if err = fp.Truncate(filesize); err != nil { + cleanup() + return nil, err + } + if err = streamChunksToFile(sqlproc, cfg, id, filesize, fp); err != nil { + cleanup() + return nil, err + } + // mmap the file read-only (shared across queries; FST + positions are views into + // it, page-cache-backed). The fd is not needed once mapped. + data, err := mmapReadOnly(fp) + fp.Close() + if err != nil { + if path != "" { + os.Remove(path) + } + return nil, err + } + // Checksum the mapped bytes (the anonymous SSD file has no path to CheckSum). + if vectorindex.CheckSumFromBuffer(data) != checksum { + _ = munmap(data) + if path != "" { + os.Remove(path) + } + return nil, moerr.NewInternalError(sqlproc.GetContext(), fmt.Sprintf("fulltext2 index %s checksum mismatch", id)) + } + // The Segment OWNS the mapping (+ path for the /tmp fallback): Free() munmaps and, + // if linked, deletes it. + m := &Segment{Id: id, mmapData: data, mmapPath: path} + if err := m.decodeSegment(data); err != nil { + m.Free() + return nil, err + } + m.Recency = recency + return m, nil +} + +// ft2LocalDir is fulltext2's own subdir under the LOCAL fileservice (on the SSD +// data-dir) for segment spill / mmap files — sibling to the JOIN spill's "__spill". +// EVERY fulltext2 temp file (the build-side load_file spill, the CDC tail spill, and +// the query-side mmap materialization) lives here so it lands on the fast SSD mount, +// not /tmp (128 MB/s on AWS). +const ft2LocalDir = "__fulltext2" + +// localSpillDir returns the on-disk __fulltext2 scratch directory under the LOCAL +// (SSD) fileservice, creating it if absent. Returns "" when no LOCAL fileservice is +// attached (tests / one-shot tools) so callers fall back to the OS temp dir via +// os.CreateTemp("", …)/os.MkdirTemp("", …). Used by the build-side spill (load_file +// needs a real path) — the query mmap path uses the anonymous createLocalTempFile. +func localSpillDir(sqlproc *sqlexec.SqlProcess) string { + if sqlproc == nil || sqlproc.Proc == nil { + return "" + } + return LocalSpillDir(sqlproc.GetContext(), sqlproc.Proc.Base.FileService) +} + +// LocalSpillDir resolves the __fulltext2 dir under the LOCAL fileservice of rootFS +// (the CN's root FileService), creating it. Engine-agnostic so the ISCP CDC tail can +// resolve it from the engine's FileService without a sqlproc. Returns "" when rootFS +// has no LOCAL fileservice. +func LocalSpillDir(ctx context.Context, rootFS fileservice.FileService) string { + if rootFS == nil { + return "" + } + local, err := fileservice.Get[*fileservice.LocalFS](rootFS, defines.LocalFileServiceName) + if err != nil { + return "" + } + dir := filepath.Join(local.RootPath(), ft2LocalDir) + if err := os.MkdirAll(dir, 0o755); err != nil { + return "" + } + return dir +} + +// createLocalSpillFile creates a NAMED temp file (prefix) under the LOCAL fileservice's +// __fulltext2 dir — its absolute on-disk path is what load_file reads back. Falls back +// to the OS temp dir when no LOCAL fileservice is attached (localSpillDir==""). +func createLocalSpillFile(sqlproc *sqlexec.SqlProcess, prefix string) (*os.File, error) { + return os.CreateTemp(localSpillDir(sqlproc), prefix) +} + +// createLocalTempFile returns a temp file for the segment's mmap. It prefers the +// LOCAL fileservice's __fulltext2 subdir (on the SSD data-dir) via the same +// CreateAndRemoveFile the JOIN spill uses — an ANONYMOUS file (unlinked; the fd + +// mapping keep the inode alive, Free just munmaps, no os.Remove). Falls back to +// os.CreateTemp (/tmp, linked → Free deletes by path) when no process/fileservice +// is attached (tests / one-shot tools). Returns (file, path, err); path=="" for the +// anonymous SSD file. name is the caller-chosen file-name prefix. +func createLocalTempFile(sqlproc *sqlexec.SqlProcess, name string) (*os.File, string, error) { + if sqlproc != nil && sqlproc.Proc != nil { + ctx := sqlproc.GetContext() + // LOCAL fileservice (SSD data-dir) -> ensure our __fulltext2 subdir -> a + // MutableFileService rooted there (mirrors process.GetSpillFileService). + if local, e := fileservice.Get[fileservice.MutableFileService]( + sqlproc.Proc.Base.FileService, defines.LocalFileServiceName); e == nil { + if e2 := local.EnsureDir(ctx, ft2LocalDir); e2 == nil { + if sub, ok := fileservice.SubPath(local, ft2LocalDir).(fileservice.MutableFileService); ok { + if f, e3 := sub.CreateAndRemoveFile(ctx, name+"_"+uuid.NewString()); e3 == nil { + return f, "", nil + } + } + } + } + } + f, err := os.CreateTemp("", name) + if err != nil { + return nil, "", err + } + return f, f.Name(), nil +} + +// checkTailLoadBudget fails fast when the CDC tail's stored bytes exceed the CN memory +// budget, returning a clear, actionable error instead of letting the tail load OOM-kill +// the CN (which would take down EVERY query on the node, not just this one). The tail +// chunks are read into the Go HEAP (append([]byte)) and each frame Deserialized into a +// Go-heap segment, so SUM(LENGTH(data)) — the ACTUAL stored bytes — approximates the load +// footprint. The mmap'd base is reclaimable OS page cache and is deliberately NOT counted +// (it cannot cause an OOM-kill). Budget = MemoryTotal*0.8 - live Go heap; MemoryTotal is +// cgroup-aware, and MemoryGolang already includes any tails currently resident, so this +// gates an INCREMENTAL load. The 0.8 headroom absorbs the (small) transient query-mpool +// usage the formula omits. +func checkTailLoadBudget(sqlproc *sqlexec.SqlProcess, cfg TableConfig) error { + sql := fmt.Sprintf("SELECT COALESCE(SUM(LENGTH(%s)), 0) FROM %s WHERE %s = %s AND %s = %d", + catalog.FullText2Index_TblCol_Storage_Data, sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + catalog.FullText2Index_TblCol_Storage_Index_Id, sqlquote.String(vectorindex.CdcTailId), + catalog.FullText2Index_TblCol_Storage_Tag, int(vectorindex.Tag_CdcEvents)) + res, err := runSql(sqlproc, sql) + if err != nil { + return err + } + defer res.Close() + var need int64 + for _, bat := range res.Batches { + if bat == nil || bat.RowCount() == 0 { + continue + } + need = vector.GetFixedAtNoTypeCheck[int64](bat.Vecs[0], 0) + } + // LoadTailSegments holds SEVERAL full copies of the tail bytes at once — the raw + // per-chunk []byte copies, the reassembled per-frame buffers, and the Deserialized + // segments — so the real peak is a multiple of the stored bytes, not 1x. Scale the + // estimate up so the guard fails BEFORE that peak OOM-kills the CN rather than after + // the SUM passes. Conservative (may reject a borderline load that would just fit); a + // clear "compact/add memory" error beats a node-killing OOM. + const tailLoadPeakFactor = 3 + need *= tailLoadPeakFactor + avail := int64(system.MemoryTotal())*8/10 - int64(system.MemoryGolang()) + if need > avail { + return moerr.NewInternalError(sqlproc.GetContext(), fmt.Sprintf( + "fulltext2 CDC tail for %s.%s needs ~%d MB to load but only ~%d MB is free "+ + "(MemoryTotal*0.8 - Go heap); compact it (ALTER ... REINDEX) or increase CN memory", + cfg.DbName, cfg.IndexTable, need>>20, avail>>20)) + } + return nil +} + +// LoadTailSegments loads the tag=1 CdcTail: the SELECTed chunk rows are ordered, +// reassembled into frames, and each insert frame decoded into a segment (its +// Recency = the frame's first chunk_id). Delete frames are folded into the pk +// tombstone map. Empty tail → (nil, nil, nil). +func LoadTailSegments(sqlproc *sqlexec.SqlProcess, cfg TableConfig) ([]*Segment, map[any]int64, error) { + // Fail fast if the tail can't fit in the memory budget, rather than letting it + // OOM-kill the CN as it decodes into the Go heap. + if err := checkTailLoadBudget(sqlproc, cfg); err != nil { + return nil, nil, err + } + sql := fmt.Sprintf("SELECT %s, %s FROM %s WHERE %s = %s AND %s = %d", + catalog.FullText2Index_TblCol_Storage_Chunk_Id, catalog.FullText2Index_TblCol_Storage_Data, + sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + catalog.FullText2Index_TblCol_Storage_Index_Id, sqlquote.String(vectorindex.CdcTailId), + catalog.FullText2Index_TblCol_Storage_Tag, int(vectorindex.Tag_CdcEvents)) + res, err := runSql(sqlproc, sql) + if err != nil { + return nil, nil, err + } + var chunks []TailChunk + for _, bat := range res.Batches { + if bat == nil { + continue + } + cids := vector.MustFixedColNoTypeCheck[int64](bat.Vecs[0]) + for i := 0; i < bat.RowCount(); i++ { + chunks = append(chunks, TailChunk{ChunkId: cids[i], Data: append([]byte(nil), bat.Vecs[1].GetRawBytesAt(i)...)}) + } + } + res.Close() + if len(chunks) == 0 { + return nil, nil, nil + } + + ordered, err := orderTailChunks(chunks) + if err != nil { + return nil, nil, err + } + frames, err := reassembleFrames(ordered) + if err != nil { + return nil, nil, err + } + + var segs []*Segment + deletes := make(map[any]int64) + for _, f := range frames { + records, _, nInserts, nDeletes, _, uerr := cuvscdc.UnframeCdcChunk(f.Data) + if uerr != nil { + return nil, nil, uerr + } + switch { + case nInserts > 0: + seg, derr := Deserialize(fmt.Sprintf("tail-%d", f.ChunkId), bytes.NewReader(records)) + if derr != nil { + return nil, nil, derr + } + seg.Recency = f.ChunkId + segs = append(segs, seg) + case nDeletes > 0: + recs, derr := DecodeDeleteLog(records) + if derr != nil { + return nil, nil, derr + } + deletes = foldDeleteFrame(deletes, recs, f.ChunkId) + } + } + return segs, deletes, nil +} + +// streamChunksToFile streams a tag=0 index's chunk rows, writing each at +// chunk_id*MaxChunkSize into fp; the assembled bytes must fill filesize exactly. +func streamChunksToFile(sqlproc *sqlexec.SqlProcess, cfg TableConfig, id string, filesize int64, fp *os.File) error { + sql := fmt.Sprintf("SELECT %s, %s FROM %s WHERE %s = %s", + catalog.FullText2Index_TblCol_Storage_Chunk_Id, catalog.FullText2Index_TblCol_Storage_Data, + sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + catalog.FullText2Index_TblCol_Storage_Index_Id, sqlquote.String(id)) + written, _, err := streamChunkRowsToFile(sqlproc, sql, 0, filesize, fp) + if err != nil { + return err + } + if written != filesize { + return moerr.NewInternalError(sqlproc.GetContext(), + fmt.Sprintf("fulltext2 index %s incomplete: wrote %d of %d bytes", id, written, filesize)) + } + return nil +} + +// streamChunkRowsToFile streams the (chunk_id, data) rows of sql and writes each +// at (chunk_id-baseChunk)*MaxChunkSize into fp, bounding the mpool to the stream +// buffer. Returns bytes written + chunk-row count. +func streamChunkRowsToFile(sqlproc *sqlexec.SqlProcess, sql string, baseChunk, bound int64, fp *os.File) (written, nchunks int64, err error) { + streamCh := make(chan executor.Result, 2) + errorCh := make(chan error, 2) + ctx, cancel := context.WithCancelCause(sqlproc.GetTopContext()) + defer cancel(nil) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer func() { + close(streamCh) + wg.Done() + }() + if _, e := runStreamingSql(ctx, sqlproc, sql, streamCh, errorCh); e != nil { + errorCh <- e + } + }() + + var loopErr error + closed := false + seen := make(map[int64]struct{}) // chunk_ids already written, to catch duplicates + for !closed { + select { + case res, ok := <-streamCh: + if !ok { + closed = true + break + } + for _, bat := range res.Batches { + if bat == nil || bat.RowCount() == 0 { + continue + } + cids := vector.MustFixedColNoTypeCheck[int64](bat.Vecs[0]) + for i, cid := range cids { + data := bat.Vecs[1].GetRawBytesAt(i) + off := (cid - baseChunk) * int64(vectorindex.MaxChunkSize) + if off < 0 || off+int64(len(data)) > bound { + loopErr = moerr.NewInternalError(sqlproc.GetContext(), + fmt.Sprintf("fulltext2 chunk_id %d out of range [base %d, bound %d]", cid, baseChunk, bound)) + break + } + // A duplicate (index_id, chunk_id) row would write its region twice and + // inflate `written`, letting the caller's written==filesize check pass while + // a DIFFERENT chunk's region is never filled (a hole the CRC only catches + // after a full stream+mmap). Reject the duplicate here, fail-fast. + if _, dup := seen[cid]; dup { + loopErr = moerr.NewInternalError(sqlproc.GetContext(), + fmt.Sprintf("fulltext2 duplicate chunk_id %d", cid)) + break + } + seen[cid] = struct{}{} + if _, e := fp.WriteAt(data, off); e != nil { + loopErr = e + break + } + written += int64(len(data)) + nchunks++ + } + if loopErr != nil { + break + } + } + res.Close() + if loopErr != nil { + closed = true + } + case e := <-errorCh: + loopErr = e + closed = true + case <-ctx.Done(): + loopErr = context.Cause(ctx) + closed = true + } + } + if loopErr != nil { + cancel(loopErr) + } + for res := range streamCh { + res.Close() + } + wg.Wait() + if loopErr == nil { + select { + case e := <-errorCh: + loopErr = e + default: + } + } + if loopErr != nil { + return 0, 0, loopErr + } + return written, nchunks, nil +} + +// NextTailChunkIdSql returns a SELECT for the next free tag=1 CdcTail chunk_id — +// the monotonic append position (= recency) the sinker frames at. It is +// GREATEST(MAX tail chunk_id, MAX tag=0 base recency) + 1, so an appended tail is +// always newer than every base under Index liveness, and the sequence never +// resets after a compaction folds the tail into a base. Mirrors bm25's +// NextTailChunkIdSql. +func NextTailChunkIdSql(cfg TableConfig) string { + tailMax := fmt.Sprintf("COALESCE((SELECT MAX(%s) FROM %s WHERE %s = %s AND %s = %d), 0)", + catalog.FullText2Index_TblCol_Storage_Chunk_Id, sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + catalog.FullText2Index_TblCol_Storage_Index_Id, sqlquote.String(vectorindex.CdcTailId), + catalog.FullText2Index_TblCol_Storage_Tag, int(vectorindex.Tag_CdcEvents)) + baseMax := fmt.Sprintf("COALESCE((SELECT MAX(%s) FROM %s), 0)", + catalog.FullText2Index_TblCol_Metadata_Recency, sqlquote.QualifiedIdent(cfg.DbName, cfg.MetadataTable)) + return fmt.Sprintf("SELECT GREATEST(%s, %s) + 1", tailMax, baseMax) +} + +// NextTailChunkId runs NextTailChunkIdSql — the next append position for the CDC +// sinker. +func NextTailChunkId(sqlproc *sqlexec.SqlProcess, cfg TableConfig) (int64, error) { + res, err := runSql(sqlproc, NextTailChunkIdSql(cfg)) + if err != nil { + return 0, err + } + defer res.Close() + for _, bat := range res.Batches { + if bat == nil || bat.RowCount() == 0 { + continue + } + return vector.GetFixedAtNoTypeCheck[int64](bat.Vecs[0], 0), nil + } + return 0, nil +} + +// StaleGenSqls returns the two queries whose results form the cache-freshness generation +// used by Fulltext2Search.IsStale: MAX(metadata.timestamp) (bumped by a REBUILD/MERGE that +// writes a new base model row) and MAX(storage.chunk_id) of the tag=1 CdcTail (bumped by a +// CDC append). A change in EITHER means the loaded index is stale. Two reads because +// timestamp and tag live in different tables (metadata has no tag; storage has no +// timestamp). The tail read is scoped to (CdcTailId, tag=1) — the exact CDC delta — so an +// unrelated base sub-index's higher chunk_id can't mask a fresh append. +func StaleGenSqls(cfg TableConfig) (tsSQL, tailSQL string) { + tsSQL = fmt.Sprintf("SELECT COALESCE(MAX(%s), 0) FROM %s", + catalog.FullText2Index_TblCol_Metadata_Timestamp, + sqlquote.QualifiedIdent(cfg.DbName, cfg.MetadataTable)) + tailSQL = fmt.Sprintf("SELECT COALESCE(MAX(%s), -1) FROM %s WHERE %s = %s AND %s = %d", + catalog.FullText2Index_TblCol_Storage_Chunk_Id, + sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + catalog.FullText2Index_TblCol_Storage_Index_Id, sqlquote.String(vectorindex.CdcTailId), + catalog.FullText2Index_TblCol_Storage_Tag, int(vectorindex.Tag_CdcEvents)) + return +} + +func resultScalarInt64(res executor.Result) int64 { + for _, bat := range res.Batches { + if bat == nil || bat.RowCount() == 0 { + continue + } + return vector.GetFixedAtNoTypeCheck[int64](bat.Vecs[0], 0) + } + return 0 +} + +// LoadGeneration reads the current (timestamp, tailChunk) generation using the caller's +// LIVE sqlproc/txn — called at load time so the captured generation reflects exactly the +// txn snapshot the cached index was built from. +func LoadGeneration(sqlproc *sqlexec.SqlProcess, cfg TableConfig) (ts int64, tail int64, err error) { + defer func() { + if r := recover(); r != nil { + ts, tail, err = 0, 0, moerr.NewInternalErrorNoCtx(fmt.Sprintf("LoadGeneration recovered: %v", r)) + } + }() + tsSQL, tailSQL := StaleGenSqls(cfg) + res, err := runSql(sqlproc, tsSQL) + if err != nil { + return 0, 0, err + } + ts = resultScalarInt64(res) + res.Close() + res, err = runSql(sqlproc, tailSQL) + if err != nil { + return 0, 0, err + } + tail = resultScalarInt64(res) + res.Close() + return ts, tail, nil +} + +// QueryGeneration reads the current (timestamp, tailChunk) generation in the BACKGROUND +// (housekeeping goroutine, no live sqlproc) via an executor-managed auto-commit txn, keyed +// by the CN UUID + tenant captured at load. Used by IsStale. +func QueryGeneration(ctx context.Context, cnUUID string, accountID uint32, cfg TableConfig) (ts int64, tail int64, err error) { + defer func() { + if r := recover(); r != nil { + ts, tail, err = 0, 0, moerr.NewInternalErrorNoCtx(fmt.Sprintf("QueryGeneration recovered: %v", r)) + } + }() + tsSQL, tailSQL := StaleGenSqls(cfg) + res, err := sqlexec.RunSqlAutoCommit(ctx, cnUUID, accountID, cfg.DbName, tsSQL) + if err != nil { + return 0, 0, err + } + ts = resultScalarInt64(res) + res.Close() + res, err = sqlexec.RunSqlAutoCommit(ctx, cnUUID, accountID, cfg.DbName, tailSQL) + if err != nil { + return 0, 0, err + } + tail = resultScalarInt64(res) + res.Close() + return ts, tail, nil +} + +// CountTailChunks returns the number of tag=1 CdcTail chunk rows — the idxcron +// tail-growth gate (fold once the delta is large enough). +func CountTailChunks(sqlproc *sqlexec.SqlProcess, cfg TableConfig) (int64, error) { + sql := fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE %s = %s AND %s = %d", + sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + catalog.FullText2Index_TblCol_Storage_Index_Id, sqlquote.String(vectorindex.CdcTailId), + catalog.FullText2Index_TblCol_Storage_Tag, int(vectorindex.Tag_CdcEvents)) + return scanInt64(sqlproc, sql) +} + +// SumBaseNrow sums metadata.nrow over the tag=0 bases — the base doc count for the +// dead-doc estimate (compared to the live source row count). +func SumBaseNrow(sqlproc *sqlexec.SqlProcess, cfg TableConfig) (int64, error) { + sql := fmt.Sprintf("SELECT COALESCE(SUM(%s), 0) FROM %s", + catalog.FullText2Index_TblCol_Metadata_Nrow, sqlquote.QualifiedIdent(cfg.DbName, cfg.MetadataTable)) + return scanInt64(sqlproc, sql) +} + +func scanInt64(sqlproc *sqlexec.SqlProcess, sql string) (int64, error) { + res, err := runSql(sqlproc, sql) + if err != nil { + return 0, err + } + defer res.Close() + for _, bat := range res.Batches { + if bat != nil && bat.RowCount() > 0 { + return vector.GetFixedAtNoTypeCheck[int64](bat.Vecs[0], 0), nil + } + } + return 0, nil +} + +// FrameChunkCount is how many MaxChunkSize storage rows a frame of frameLen bytes +// occupies (>= 1) — the streaming sinker advances chunk_id past each spilled +// segment by this without holding the framed bytes. Mirrors bm25's FrameChunkCount. +func FrameChunkCount(frameLen int) int64 { + n := int64((frameLen + vectorindex.MaxChunkSize - 1) / vectorindex.MaxChunkSize) + if n < 1 { + n = 1 + } + return n +} diff --git a/pkg/fulltext2/stream.go b/pkg/fulltext2/stream.go new file mode 100644 index 0000000000000..a21be9669820a --- /dev/null +++ b/pkg/fulltext2/stream.go @@ -0,0 +1,353 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "encoding/binary" + "math" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/vectorindex" +) + +// streamBatch is the max rows a streamSink buffers before flushing to emit +// (matches bm25's streamBatch). +const streamBatch = 8192 + +// streamSink batches (pk, score) results into a TYPED, box-free vectorindex.ColumnBuffer +// and flushes to emit in bounded chunks, so a no-LIMIT query returns every matching doc +// WITHOUT materializing them all and WITHOUT boxing a pk per doc. On an emit error it +// records it and stops (the walk checks stopped and bails). +type streamSink struct { + emit func(out *vectorindex.SearchOutput) error + keys *vectorindex.ColumnBuffer // pooled; nil until the first push after a flush + pkType types.T + scores []float32 + // includeCols is COLUMN-MAJOR: one nullable ColumnBuffer per FULL index INCLUDE column + // (segment-include order), each covering the whole batch — the box-free analogue of the + // old per-row [][]any. nil until the first push after a flush; len == len(includeTypes) + // when wantInclude. Not pooled (per-batch, potentially varied types); the consumer copies + // each out via AppendColumnBuffer and drops it. + includeCols []*vectorindex.ColumnBuffer + includeTypes []int32 // FULL index INCLUDE column types (drives includeCols alloc) + fixedW int // fixed pk width; 0 ⇒ varlena + err error + stopped bool + wantInclude bool // covered fast path: also carry each doc's INCLUDE values through emit +} + +// newStreamSink resolves the index's pk type (all segments share it). The batch buffer +// itself is fetched from the pool lazily on the first push. Callers create it only after +// the globalN==0 early-out, so segments is non-empty. wantInclude turns on the covered +// fast path: each pushed doc also carries its INCLUDE values (box-free) through emit. +func newStreamSink(idx *Index, wantInclude bool, emit func(out *vectorindex.SearchOutput) error) *streamSink { + pkType := idx.segments[0].PkType + w, fixed := fixedPkByteWidth(pkType) + if !fixed { + w = 0 + } + s := &streamSink{emit: emit, pkType: types.T(pkType), fixedW: w, wantInclude: wantInclude} + if wantInclude { + s.includeTypes = idx.segments[0].includeTypes + } + return s +} + +// ensure lazily fetches a pooled batch buffer of the sink's pk type, and (when the covered +// path is on) allocates one typed, nullable ColumnBuffer per FULL INCLUDE column. +func (s *streamSink) ensure() { + if s.keys == nil { + s.keys = GetColumnBuffer(s.pkType) + } + if s.wantInclude && s.includeCols == nil { + s.includeCols = make([]*vectorindex.ColumnBuffer, len(s.includeTypes)) + for c, t := range s.includeTypes { + s.includeCols[c] = &vectorindex.ColumnBuffer{Type: types.T(t)} + } + } +} + +// appendIncludes appends doc (seg, ord)'s INCLUDE values to the per-column buffers box-free. +// On a malformed segment it records the error and stops the sink so no torn batch is emitted. +func (s *streamSink) appendIncludes(seg *Segment, ord int64) { + for c := range s.includeCols { + if err := seg.appendIncludeTo(s.includeCols[c], c, ord); err != nil { + s.err = err + s.stopped = true + return + } + } +} + +// pushPk appends the pk at (seg, ord) to the typed batch WITHOUT boxing, decoding it +// straight from the segment docmap into ColumnBuffer.Data. +func (s *streamSink) pushPk(seg *Segment, ord int64, score float32) { + if s.stopped { + return + } + s.ensure() + seg.appendPkTo(s.keys, s.fixedW, ord) + s.scores = append(s.scores, score) + if s.wantInclude { + s.appendIncludes(seg, ord) + if s.stopped { + return + } + } + if s.keys.N >= streamBatch { + s.flush() + } +} + +// pushAny appends an already-boxed pk (the materialized-fallback path: NL phrase / full +// boolean) by re-encoding its concrete value into ColumnBuffer.Data — no NEW box. When the +// covered fast path is on, the INCLUDE values are pulled box-free from (seg, ord). +func (s *streamSink) pushAny(seg *Segment, ord int64, pk any, score float32) { + if s.stopped { + return + } + s.ensure() + appendAnyPk(s.keys, s.fixedW, pk) + s.scores = append(s.scores, score) + if s.wantInclude { + s.appendIncludes(seg, ord) + if s.stopped { + return + } + } + if s.keys.N >= streamBatch { + s.flush() + } +} + +func (s *streamSink) flush() { + if s.stopped || s.keys == nil || s.keys.N == 0 { + return + } + // Hand the batch to the consumer as a *SearchOutput — the SAME box-free container + // SearchInto fills on the LIMIT path, so both share one consumer (Dists is float32, + // matching the T_float32 score column). This SearchOutput is producer-owned per batch; + // the consumer recycles out.Keys (pooled) after copying and drops Dists/Include. + out := &vectorindex.SearchOutput{Keys: s.keys, Dists: s.scores, Include: s.includeCols} + if e := s.emit(out); e != nil { + s.err = e + s.stopped = true + PutColumnBuffer(s.keys) // emit didn't hand it off; recycle here + s.keys = nil + s.includeCols = nil + return + } + // Ownership of this batch passes to the consumer, which PutColumnBuffers the pk buffer + // back once it has copied Data out (the include buffers are not pooled, just dropped); + // the sink lazily Gets fresh ones on the next push. + s.keys = nil + s.scores = nil + s.includeCols = nil +} + +// appendPkTo decodes the pk at ord from seg's docmap into k.Data with no boxing: a +// fixed-width pk contributes its width bytes; a varlena pk its [u32 len][content] entry. +func (s *Segment) appendPkTo(k *vectorindex.ColumnBuffer, fixedW int, ord int64) { + if s.pks != nil { // build-side segment: re-encode the already-boxed value + appendAnyPk(k, fixedW, s.pks[ord]) + return + } + off := int(s.pkOffsets[ord]) + l := int(binary.LittleEndian.Uint32(s.pkRaw[off:])) + if fixedW > 0 { + k.Data = append(k.Data, s.pkRaw[off+4:off+4+fixedW]...) // width bytes, no len prefix + } else { + k.Data = append(k.Data, s.pkRaw[off:off+4+l]...) // [u32 len][content] verbatim + } + k.N++ +} + +// streamWAND does a heap-free document-at-a-time OR merge over one segment: it visits +// every matching doc in ord order, scores it (over the SHOULD terms present, on the +// GLOBAL corpus stats), and pushes it to the sink — no top-K heap, no WAND pruning, +// no full-result materialization. allow (liveness ∧ WHERE prefilter) drops +// non-admissible docs. The no-LIMIT case wants every match; ranking is done by the +// upstream ORDER BY score node. Mirrors bm25's streamInto. +func (s *Segment) streamWAND(clauses []clause, algo ScoreAlgo, gs *globalStats, allow Membership, sink *streamSink) { + avgDocLen := gs.avgdl(s) + iters := s.buildWandIters(clauses, algo, gs, avgDocLen) + defer releaseWandIters(iters) // recycle the per-cursor block buffers + if len(iters) == 0 { + return + } + for !sink.stopped { + minDoc := int64(math.MaxInt64) + for _, it := range iters { + if d := it.doc(); d < minDoc { + minDoc = d + } + } + if minDoc == math.MaxInt64 { + break // all cursors exhausted + } + if allowed(allow, minDoc) { + var score float32 + for _, it := range iters { + if it.doc() == minDoc { + // tf-aware, same as searchWAND / evalClause (fulltext2 keeps real tf). + score += it.weight * s.scoreTerm(algo, float64(it.tf()), it.idf2, minDoc, avgDocLen) + } + } + sink.pushPk(s, minDoc, score) + } + for _, it := range iters { + if it.doc() == minDoc { + it.idx++ + it.refresh() // keep the cached doc() in sync with idx + } + } + } +} + +// StreamQuery answers a no-LIMIT MATCH by streaming every matching (pk, score) to emit +// in bounded batches instead of materializing the whole result set (the ranking is +// done by the upstream ORDER BY score node). Every shape streams ONE SEGMENT AT A TIME +// with peak Go heap O(one segment), never O(globalN): a pure disjunction of single terms +// (the ranked-retrieval shape) streams heap-free via streamWAND; an NL phrase via +// streamPhrase; and a full boolean with MUST/MUST-NOT/mixed-phrase via streamBoolean +// (each segment's dense evalBoolean, freed before the next). All share the same sink — a +// uniform emit interface, and no separate paginated copy in the caller. +func (idx *Index) StreamQuery(pattern []byte, boolean bool, parser string, algo ScoreAlgo, filter *prefilter, wantInclude bool, emit func(out *vectorindex.SearchOutput) error) error { + if idx.globalN == 0 { + return nil + } + sink := newStreamSink(idx, wantInclude, emit) + p := normalizeParser(parser) + + // Resolve the query the same way SearchQuery does, but prefer the streaming + // disjunctive path when the resolved query is a pure OR of single terms. + var q BoolQuery + var haveQ bool + pat := string(pattern) + if boolean { + var err error + if q, err = buildBooleanQuery(pat, p); err != nil { + return err + } + haveQ = true + } + // NL mode (haveQ stays false) is an exact positional phrase, not a pure disjunction, + // so it falls through to the materializing SearchQuery below (same phrase path). + + if haveQ { + if terms, ok := disjunctiveTerms(q); ok { + return idx.streamDisjunction(terms, algo, filter, sink) + } + } else { + // NL mode = exact positional phrase. Stream it ONE SEGMENT AT A TIME (peak = + // one segment's phrase matches, freed before the next) instead of the old + // SearchQuery(globalN) fallback, whose bounded top-K sized to globalN + // materialized the whole live corpus for a low-selectivity phrase. + slots, err := phraseSlots(pat, p) + if err != nil { + return err + } + return idx.streamPhrase(slots, algo, filter, sink) + } + + // Boolean, non-disjunctive (MUST / MUST-NOT / mixed phrase): stream each live match + // through the sink one segment at a time via evalBoolean. Peak Go heap is O(one + // segment) (bounded by its capacity), NOT O(globalN) — so a low-selectivity boolean + // (e.g. +common on millions of rows) no longer materializes the whole live corpus as + // a top-K sized to globalN before emitting. Ranking is the upstream ORDER BY node's. + return idx.streamBoolean(q, algo, filter, sink) +} + +// streamPhrase emits every live phrase match (pk, score) through sink WITHOUT a global +// top-K heap or a globalN result slice — the streaming analogue of SearchPhrase for the +// no-LIMIT NL path. A phrase has ONE idf² (a positive constant, so it does not change +// rank but does set the absolute score to match the LIMIT path), which needs the total +// live df, so this makes two cheap passes: pass 1 counts the live phrase df; pass 2 +// scores + streams. Peak Go heap is O(one segment's matchPhrase hits) (freed before the +// next segment), NOT O(globalN). Mirrors SearchPhrase's scoring (partial × idf²) exactly. +func (idx *Index) streamPhrase(slots []phraseSlot, algo ScoreAlgo, filter *prefilter, sink *streamSink) error { + if idx.globalN == 0 || len(slots) == 0 { + return nil + } + // Pass 1: distinct live phrase df (filter-independent, matching SearchPhrase's df). + df := 0 + for si, seg := range idx.segments { + for _, h := range seg.matchPhrase(slots) { + if idx.isLive(si, h.ord) { + df++ + } + } + } + if df == 0 { + return nil + } + idf2 := float32(idfSquared(idx.globalN, df)) + + // Pass 2: score + stream each live, WHERE-admitted match in doc order per segment. + for si, seg := range idx.segments { + allow := mkAllow(seg, filter) + for _, h := range seg.matchPhrase(slots) { + if !idx.isLive(si, h.ord) { + continue + } + if allowed(allow, h.ord) { + partial := seg.scoreTerm(algo, float64(h.tf), 1.0, h.ord, idx.globalAvgDocLen) + sink.pushAny(seg, h.ord, seg.pk(h.ord), partial*idf2) + if sink.err != nil { + return sink.err + } + } + } + } + sink.flush() + return sink.err +} + +// StreamBagOfWords is the no-LIMIT IN BM25 MODE path: it tokenizes the pattern into a +// pure disjunction of its tokens (bm25-style bag-of-words) and streams the ranked hits +// heap-free via streamWAND — the position-free analogue of StreamQuery's disjunctive +// branch, but a CJK run is tokenized to OR terms instead of a positional phrase, so it +// works on a POSITION_FREE index. +func (idx *Index) StreamBagOfWords(pattern []byte, parser string, algo ScoreAlgo, filter *prefilter, wantInclude bool, emit func(out *vectorindex.SearchOutput) error) error { + if idx.globalN == 0 { + return nil + } + q, err := buildBagOfWordsQuery(string(pattern), normalizeParser(parser)) + if err != nil { + return err + } + terms, ok := disjunctiveTerms(q) + if !ok { + return nil // no resolvable tokens → nothing to stream + } + return idx.streamDisjunction(terms, algo, filter, newStreamSink(idx, wantInclude, emit)) +} + +// streamDisjunction streams a pure OR of terms heap-free across all segments (global +// corpus stats + per-segment liveness), pushing every (pk, score) into sink and +// flushing at the end. Shared by StreamQuery's boolean disjunctive branch and +// StreamBagOfWords' IN BM25 MODE path — a change to the disjunctive walk lands once. +func (idx *Index) streamDisjunction(terms []clause, algo ScoreAlgo, filter *prefilter, sink *streamSink) error { + gs := idx.newGlobalStats() + for si, seg := range idx.segments { + allow := andAllow(mkAllow(seg, filter), &livenessMembership{idx: idx, si: si}) + seg.streamWAND(terms, algo, gs, allow, sink) + if sink.err != nil { + return sink.err + } + } + sink.flush() + return sink.err +} diff --git a/pkg/fulltext2/tailbuild.go b/pkg/fulltext2/tailbuild.go new file mode 100644 index 0000000000000..68964cafa2309 --- /dev/null +++ b/pkg/fulltext2/tailbuild.go @@ -0,0 +1,279 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "fmt" + "os" + "path/filepath" +) + +// defaultTailCapacity floors the streaming segment cap when a non-positive +// capacity is passed (the sinker sources it from max_index_capacity, default 1M). +const defaultTailCapacity int64 = DefaultBuildCapacity + +// TailSegment locates one sealed CDC frame inside a PACKED spool file: Path is the spool file, +// Offset is the frame's start byte within it, and FrameLen its length. Many frames share one spool +// file (rolled at DefaultTailSpoolRollBytes), so a burst of tiny CDC frames costs a handful of files +// instead of one file per frame; persist reads exactly [Offset, Offset+FrameLen) via load_file. +type TailSegment struct { + Path string + Offset int64 + FrameLen int +} + +// TailBuilder streams CDC insert/upsert rows into capacity-capped positional segments and DELETEs +// into capacity-capped pk-only frames, spilling each sealed frame's bytes to a temp file the moment +// it fills — so the sinker's peak memory is ONE open segment's postings plus one open delete run, +// never the whole CDC stream. +// +// Frames are sealed, and returned by Finish, in CHRONOLOGICAL order (the order op-runs arrive), so +// chunk_id — assigned sequentially at persist and used as the segment recency — reflects operation +// order. This gives last-writer-wins across CDC blobs WITHOUT any unbounded per-session set: a +// later DELETE frame outranks an earlier INSERT segment of the same pk (delete wins), and a +// re-INSERT after a DELETE outranks it (re-insert wins). Switching op type seals the currently-open +// structure first (see AddBatch), which is what preserves that order; the producer flushes a new +// blob per op change, so this seals at blob granularity, not per event. Mirrors bm25's TailBuilder +// (positional Builder here). NOT safe for concurrent use; Cleanup() must be deferred to remove the +// temp files. +// +// Each sealed frame is APPENDED to a rolling packed spool file (rolled at rollCap), not written to +// its own file — so an alternating delete/insert stream (which seals one frame per op run) costs a +// bounded number of files, O(total frame bytes / rollCap), instead of one file per op run (which +// could reach hundreds of thousands over a 30-minute iteration and exhaust inodes). The frame +// ORDER, count, and per-frame chunk_id are unchanged — only the file storage is packed — so +// last-writer-wins recency is byte-for-byte identical to a file-per-frame layout. +type TailBuilder struct { + pkType int32 + capacity int64 // doc cap (max_index_capacity) + postingCap int64 // posting cap (max_postings_capacity); seal on whichever is hit first + tokenize func(string) []WordPos + dir string + seq int + delSeq int + cur *Builder // open INSERT segment (nil between insert runs) + delBatch []DeleteRecord // open DELETE run (sealed on an op switch to insert, at capacity, or at Finish) + frames []TailSegment // sealed frames (insert segs + delete frames) in chronological (recency) order + + // Packed spool: sealed frames are appended to curFile at curOff until it would exceed rollCap, + // then a fresh file is opened. curFile is closed on roll and in Finish/Cleanup so persist's + // load_file reads flushed bytes. fileSeq names the spool files. + rollCap int64 + curFile *os.File + curPath string + curOff int64 + fileSeq int + + opts []BuildOpt // carried into each per-segment Builder (e.g. WithPositionFree) + includeTypes []int32 // INCLUDE column schema, adopted from the first CDC batch +} + +// NewTailBuilder creates a streaming tail builder backed by a private temp dir under +// spillDir (the LOCAL fileservice's __fulltext2 dir; "" ⇒ the OS temp dir, for tests / +// no-fileservice callers). Pass WithPositionFree() so a position-free index's CDC tail +// stays position-free. capacity is max_index_capacity (docs), postingCap is +// max_postings_capacity (postings); each non-positive value falls back to its default. +func NewTailBuilder(pkType int32, capacity, postingCap int64, spillDir string, tokenize func(string) []WordPos, opts ...BuildOpt) (*TailBuilder, error) { + if capacity < 1 { + capacity = defaultTailCapacity + } + if postingCap < 1 { + postingCap = DefaultPostingCapacity + } + dir, err := os.MkdirTemp(spillDir, "ftv2tail") + if err != nil { + return nil, err + } + return &TailBuilder{pkType: pkType, capacity: capacity, postingCap: postingCap, tokenize: tokenize, dir: dir, rollCap: DefaultTailSpoolRollBytes, opts: opts}, nil +} + +// DefaultTailSpoolRollBytes caps one packed spool file; appending a frame that would exceed it +// starts a fresh file (a single frame larger than the cap gets its own file). Bounds a temp file's +// size while packing many small CDC frames into few files instead of one file per frame. +const DefaultTailSpoolRollBytes int64 = 128 << 20 // 128 MiB + +// writeFrame appends framed to the current rolling spool file — starting a new file when the +// current one would exceed rollCap — and returns the file path plus the frame's byte offset within +// it, so persist can address exactly [offset, offset+len) via load_file. A frame is never split +// across files, so load_file always sees a contiguous range. +func (t *TailBuilder) writeFrame(framed []byte) (string, int64, error) { + if t.curFile != nil && t.curOff > 0 && t.curOff+int64(len(framed)) > t.rollCap { + if err := t.curFile.Close(); err != nil { + return "", 0, err + } + t.curFile = nil + } + if t.curFile == nil { + path := filepath.Join(t.dir, fmt.Sprintf("spool-%d.frames", t.fileSeq)) + t.fileSeq++ + f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600) + if err != nil { + return "", 0, err + } + t.curFile, t.curPath, t.curOff = f, path, 0 + } + off := t.curOff + if _, err := t.curFile.Write(framed); err != nil { + return "", 0, err + } + t.curOff += int64(len(framed)) + return t.curPath, off, nil +} + +// AddBatch streams one decoded CDC batch: insert/upsert rows are tokenized into +// the open segment (sealed + spilled once it reaches `capacity` docs), deletes are +// collected. Same tokenizer as the search side, so build/query tokens match. +func (t *TailBuilder) AddBatch(cdc *Cdc) error { + // The CDC blob is self-describing: adopt its INCLUDE schema once so every tail segment + // this builder seals carries the docmap include section (WithIncludeTypes). A copy of + // t.opts avoids mutating the caller's slice. + if t.includeTypes == nil && len(cdc.IncludeTypes) > 0 { + t.includeTypes = cdc.IncludeTypes + t.opts = append(append([]BuildOpt(nil), t.opts...), WithIncludeTypes(t.includeTypes)) + } + // Process events IN ORDER, sealing at each op-type SWITCH so frames are produced — and later + // returned by Finish — in chronological order. Recency (chunk_id) then follows operation order + // across CDC blobs, giving last-writer-wins: switching to an insert seals the open delete run + // first (so a re-insert outranks the prior delete of that pk); switching to a delete seals the + // open insert segment first (so the delete outranks the prior insert). The producer flushes a + // new blob per op change, so this seals at blob granularity, not per event, and both open + // structures stay bounded to one capacity. + for i := range cdc.Events { + e := &cdc.Events[i] + switch e.Op { + case cdcInsert, cdcUpsert: + words := t.tokenize(e.Text) + // An INSERT of empty / zero-token text has nothing to index and no prior version to + // shadow, so skip it (matches the base build, which drops empty docs). An UPSERT of + // empty / NULL text MUST still emit a doc so it shadows the old indexed version — + // SetDoc writes a live 0-term doc for that. + if len(words) == 0 && e.Op == cdcInsert { + continue + } + if len(t.delBatch) > 0 { // op switch delete->insert: seal the delete run first (lower recency) + if err := t.sealDeletes(); err != nil { + return err + } + } + if t.cur == nil { + t.cur = NewBuilder(fmt.Sprintf("cdctail-%d", t.seq), t.pkType, t.opts...) + } + // REPLACE, not append: a repeated upsert of one pk within this open segment supersedes + // its earlier terms AND INCLUDE values (Add would merge). + t.cur.SetDoc(e.Pk, words, e.Include) + if ReachedSegmentCap(t.cur, t.capacity, t.postingCap) { + if err := t.seal(); err != nil { + return err + } + } + case cdcDelete: + if t.cur != nil { // op switch insert->delete: seal the insert segment first (lower recency) + if err := t.seal(); err != nil { + return err + } + } + t.delBatch = append(t.delBatch, DeleteRecord{Pk: e.Pk}) + if int64(len(t.delBatch)) >= t.capacity { + if err := t.sealDeletes(); err != nil { + return err + } + } + } + } + return nil +} + +// seal finalizes the open segment, frames it, and spills the framed bytes to a +// temp file. No-op with no open segment; an all-empty segment is dropped. +func (t *TailBuilder) seal() error { + if t.cur == nil { + return nil + } + seg, err := t.cur.Finish() + t.cur = nil + if err != nil { + return err + } + if seg.N == 0 { + return nil + } + framed, err := FrameSegment(seg) + if err != nil { + return err + } + t.seq++ + path, off, err := t.writeFrame(framed) + if err != nil { + return err + } + t.frames = append(t.frames, TailSegment{Path: path, Offset: off, FrameLen: len(framed)}) + return nil +} + +// sealDeletes frames the OPEN delete run and spills it to a temp file, appending it to the +// chronological frame list. Called on an op switch to insert, when the run reaches capacity, and +// at Finish — so tombstones stay bounded to one capacity's worth in the heap (never accumulate +// for the whole session). No-op when the run is empty. +func (t *TailBuilder) sealDeletes() error { + if len(t.delBatch) == 0 { + return nil + } + framed, err := FrameDeletes(t.pkType, t.delBatch) + if err != nil { + return err + } + t.delBatch = nil // free the run; the next delete re-grows a fresh slice + t.delSeq++ + path, off, err := t.writeFrame(framed) + if err != nil { + return err + } + t.frames = append(t.frames, TailSegment{Path: path, Offset: off, FrameLen: len(framed)}) + return nil +} + +// Finish seals the final open insert segment and delete run (in that order — at most one is open, +// since an op switch already sealed the other), CLOSES the current spool file so its bytes are +// flushed for load_file, then returns ALL frames in chronological (recency) order. chunk_id is +// assigned sequentially at persist, so recency = operation order and the last writer wins across +// CDC blobs (see AddBatch / the type doc). Each frame is a [Offset, Offset+FrameLen) range in its +// packed spool file, which the caller persists via load_file. Cleanup must be called afterwards. +func (t *TailBuilder) Finish() ([]TailSegment, error) { + if err := t.seal(); err != nil { + return nil, err + } + if err := t.sealDeletes(); err != nil { + return nil, err + } + if t.curFile != nil { + if err := t.curFile.Close(); err != nil { + return nil, err + } + t.curFile = nil + } + return t.frames, nil +} + +// Cleanup closes any open spool file and removes the temp dir with all packed spool files. Idempotent. +func (t *TailBuilder) Cleanup() { + if t.curFile != nil { + t.curFile.Close() + t.curFile = nil + } + if t.dir != "" { + os.RemoveAll(t.dir) + t.dir = "" + } +} diff --git a/pkg/fulltext2/termdict.go b/pkg/fulltext2/termdict.go new file mode 100644 index 0000000000000..1291c2e1a2a52 --- /dev/null +++ b/pkg/fulltext2/termdict.go @@ -0,0 +1,164 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + + "github.com/blevesearch/vellum" +) + +// termDict is the on-disk / loaded term dictionary of a segment: a vellum FST +// mapping each indexed term to a uint64 value (the term's posting-offset; df is +// read from the posting-list header). It is the compact, sorted realization of +// the backbone described in doc.go — the build side accumulates terms in a Go +// map (Segment.terms), then buildTermDictFST encodes the sorted keys into this +// FST for storage and query. +// +// vellum is byte-oriented, and UTF-8 byte order equals Unicode code-point order, +// so terms sorted with Go's ordinary string sort (Segment.sortedTerms) are in +// exactly the ascending byte order vellum's builder requires — Chinese / CJK +// terms need no special handling, and prefix iteration works on whole-token +// (character-boundary) prefixes. +type termDict struct { + fst *vellum.FST + data []byte // backing bytes; vellum.Load references (does not copy) this +} + +// buildTermDictFST encodes ascending (term → value) pairs into vellum FST bytes. +// terms must be in ascending byte order (Segment.sortedTerms) and unique, and +// len(values) must equal len(terms); values[i] is term[i]'s posting-offset. +func buildTermDictFST(terms []string, values []uint64) ([]byte, error) { + var buf bytes.Buffer + b, err := vellum.New(&buf, nil) + if err != nil { + return nil, err + } + for i, t := range terms { + if err = b.Insert([]byte(t), values[i]); err != nil { + return nil, err + } + } + if err = b.Close(); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// loadTermDict wraps FST bytes (as produced by buildTermDictFST) for query. The +// returned dict references data directly, so data must outlive the dict; Close +// releases the FST. +func loadTermDict(data []byte) (*termDict, error) { + fst, err := vellum.Load(data) + if err != nil { + return nil, err + } + return &termDict{fst: fst, data: data}, nil +} + +// get returns the value (posting-offset) for an exact term, or ok=false if the +// term is absent. +func (d *termDict) get(term string) (val uint64, ok bool, err error) { + return d.fst.Get([]byte(term)) +} + +// len returns the number of terms in the dictionary. +func (d *termDict) len() int { return d.fst.Len() } + +// prefixIter returns an ascending iterator over every term that begins with +// prefix (the `word*` enumeration). An empty prefix iterates the whole dict. The +// caller must Close the iterator. A nil iterator with ok=false means the prefix +// matched nothing. +func (d *termDict) prefixIter(prefix string) (it *vellum.FSTIterator, ok bool, err error) { + start := []byte(prefix) + it, err = d.fst.Iterator(start, prefixSuccessor(start)) + if err == vellum.ErrIteratorDone { + return nil, false, nil // no term with this prefix + } + if err != nil { + return nil, false, err + } + return it, true, nil +} + +// prefixSuccessor returns the smallest byte string strictly greater than every +// string having prefix as a prefix — i.e. the exclusive upper bound for a prefix +// range scan: copy the prefix, increment the last byte that is < 0xFF, and drop +// the trailing 0xFF bytes. Returns nil (an open upper bound → iterate to the end +// of the FST) when prefix is empty or all 0xFF, both of which have no finite +// successor. +func prefixSuccessor(prefix []byte) []byte { + end := make([]byte, len(prefix)) + copy(end, prefix) + for i := len(end) - 1; i >= 0; i-- { + if end[i] < 0xFF { + end[i]++ + return end[:i+1] + } + } + return nil +} + +// prefixTerms collects every term with the given prefix into an ascending slice +// (the `word*` expansion, materialized). Empty prefix returns all terms. +func (d *termDict) prefixTerms(prefix string) ([]string, error) { + it, ok, err := d.prefixIter(prefix) + if err != nil || !ok { + return nil, err + } + defer func() { _ = it.Close() }() + var out []string + for { + term, _ := it.Current() + out = append(out, string(term)) + if err := it.Next(); err == vellum.ErrIteratorDone { + break + } else if err != nil { + return nil, err + } + } + return out, nil +} + +// forEachTerm streams every term (ascending) through fn WITHOUT materializing the whole +// vocabulary as a []string the way prefixTerms("") does — so a large high-cardinality +// index's MERGE reconstruction (forEachPosting) doesn't spike O(vocabulary) of strings. +func (d *termDict) forEachTerm(fn func(term string) error) error { + it, ok, err := d.prefixIter("") + if err != nil || !ok { + return err + } + defer func() { _ = it.Close() }() + for { + term, _ := it.Current() + if err := fn(string(term)); err != nil { + return err + } + if err := it.Next(); err == vellum.ErrIteratorDone { + break + } else if err != nil { + return err + } + } + return nil +} + +// Close releases the FST. Safe on a nil dict. +func (d *termDict) Close() error { + if d == nil || d.fst == nil { + return nil + } + return d.fst.Close() +} diff --git a/pkg/fulltext2/wand.go b/pkg/fulltext2/wand.go new file mode 100644 index 0000000000000..79cf63fe1ccee --- /dev/null +++ b/pkg/fulltext2/wand.go @@ -0,0 +1,398 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "math" + + "github.com/matrixorigin/matrixone/pkg/vectorindex" +) + +// WAND (Weak-AND, Broder et al. 2003) disjunctive top-k for a pure OR of single +// terms. Each term posting iterator carries a max-impact upper bound (its +// largest possible weighted contribution); the algorithm keeps a top-k min-heap +// whose smallest score is the threshold θ, and skips any document whose summed +// upper bounds cannot reach θ — so most low-scoring documents are never scored. +// +// The result is the SAME top-k the full scan produces — the same set of documents +// and the same score for each; WAND only avoids work. It layers Block-Max WAND +// (Ding & Suel 2011) on top of classic term-level WAND: besides each term's +// whole-list max-impact, the per-block bounds (blockMaxTf/blockMinDocLn over +// BlockSize-doc blocks, derived by deriveTermStats) give a tighter score UB for the +// block covering the pivot, so a whole block region whose summed block-UB can't +// reach θ is skipped without scoring any doc in it. Block-skip only removes docs +// whose score UB is ≤ θ — which the `score > θ` insertion rule would reject anyway. +// +// Tie caveat: the ORDER of documents that share an equal score is unspecified (like +// bm25, which documents "ties arbitrary"). WAND sums a doc's term contributions in +// cursor order and the full scan in clause-map order, so the two can differ in the +// last float ULP and sort exactly-tied docs differently; and at the k-th boundary a +// tie may include one arbitrary member over another. The top-k SET (above the +// boundary) and the score multiset are identical either way. + +// wandIter is a term's posting cursor for WAND. It holds the termPostings for the +// resident Block-Max bounds (blockLastDoc/blockMaxTf/blockMinDocLn), plus a small +// PER-CURSOR decoded-block cache: docIDs/tfs are NOT resident on a loaded segment +// (they live block-compressed in the shared, read-only mmap), so the cursor decodes +// one block at a time into bDocs/bTfs. The cache is per-cursor, so concurrent queries +// over the same shared segment never race. WAND's block-skip means most blocks are +// never decoded. +type wandIter struct { + tp *termPostings + idx int + curBlk int // block index currently decoded into bDocs/bTfs (-1 = none) + blen int // valid entries in bDocs/bTfs + cur int64 // cached doc() for the current idx (math.MaxInt64 when exhausted) + bDocs []int64 // decoded docIDs of curBlk (cap BlockSize); backed by buf + bTfs []uint8 // decoded tfs of curBlk (cap BlockSize); backed by buf + buf *wandBuf // pooled backing for bDocs/bTfs; returned by releaseWandIters + idf2 float64 + weight float32 + maxImpact float32 // term-level upper bound of this term's weighted contribution +} + +func (it *wandIter) atEnd() bool { return it.idx >= it.tp.df() } + +// ensure decodes the block containing the cursor's current idx into bDocs/bTfs, if +// not already cached. Cheap (a field compare) when the cursor stays within a block. +func (it *wandIter) ensure() { + b := it.idx / BlockSize + if b != it.curBlk { + it.blen = it.tp.fillBlock(b, it.bDocs, it.bTfs) + it.curBlk = b + } +} + +// doc returns the cached current doc ord (math.MaxInt64 when exhausted, so an +// exhausted cursor sorts last). It is read many times per pivot iteration +// (insertion sort, pivot scan, blockMax, chooseSkip, alignment) while the cursor +// moves at most once, so cur is recomputed only on move (refresh) — avoiding the +// atEnd() check, the idx/BlockSize division in ensure, and the modulo per read. +func (it *wandIter) doc() int64 { return it.cur } + +// refresh recomputes cur after idx changes (construction/advance/skipTo). +func (it *wandIter) refresh() { + if it.atEnd() { + it.cur = math.MaxInt64 + return + } + it.ensure() + it.cur = it.bDocs[it.idx%BlockSize] +} + +// tf returns the current posting's capped term frequency. +func (it *wandIter) tf() uint8 { + it.ensure() + return it.bTfs[it.idx%BlockSize] +} + +// skipTo advances the cursor to the first doc >= d: locate the block via the +// RESIDENT blockLastDoc (no decode), then binary-search within that one block. +func (it *wandIter) skipTo(d int64) { + b := it.blockIndexAt(d) + if b >= it.tp.nblk() { + it.idx = it.tp.df() // past the last posting → exhausted + it.cur = math.MaxInt64 + return + } + if b != it.curBlk { + it.blen = it.tp.fillBlock(b, it.bDocs, it.bTfs) + it.curBlk = b + } + // d <= blockLastDoc[b] = bDocs[blen-1], so the lower bound is within the block + // and never moves the cursor backward (d >= current doc). Inline lower-bound + // binary search rather than sort.Search: avoids the per-call closure and the + // generic sort.Search frame in this hot skip path. + lo, hi := 0, it.blen + for lo < hi { + mid := int(uint(lo+hi) >> 1) + if it.bDocs[mid] < d { + lo = mid + 1 + } else { + hi = mid + } + } + it.idx = b*BlockSize + lo + it.cur = it.bDocs[lo] +} + +// blockIndexAt returns the index of the block (>= the current block) containing +// doc d — the first block whose last ord >= d. nblk() if d is past the cursor's +// last posting. Uses only the resident blockLastDoc (no block decode). +func (it *wandIter) blockIndexAt(d int64) int { + bl := it.tp.blockLastDoc + // blockLastDoc is ascending, so the first block with bl[b] >= d is a lower-bound + // binary search from the cursor's current block. The block-skip branch calls this + // per iters[0..pivot] per pivot step over a long posting list, where the old linear + // scan was O(nblk); binary search makes it O(log nblk) on the hot WAND loop. + lo := it.idx / BlockSize + hi := len(bl) + for lo < hi { + mid := int(uint(lo+hi) >> 1) + if bl[mid] < d { + lo = mid + 1 + } else { + hi = mid + } + } + return lo +} + +// blockMax is the weighted Block-Max score upper bound for the block containing +// doc d, in the SAME scorer as termMaxImpact (so it is ≤ the term-level bound). 0 +// if d is past the list. blockMaxTf (max tf in block) and blockMinDocLn (min doc +// length in block) give the largest bm25Factor achievable in the block. +func (it *wandIter) blockMax(d int64, algo ScoreAlgo, avgDocLen float64) float32 { + b := it.blockIndexAt(d) + if b >= len(it.tp.blockLastDoc) { + return 0 + } + if algo == BM25 { + return it.weight * float32(it.idf2*bm25Factor(float64(it.tp.blockMaxTf[b]), it.tp.blockMinDocLn[b], avgDocLen)) + } + return it.weight * float32(float64(it.tp.blockMaxTf[b])*it.idf2) // TfIdf +} + +// blockEndAt is the last ord of the block containing doc d (the upper edge of the +// region for which blockMax(d) is a valid bound). math.MaxInt64 if past the list. +func (it *wandIter) blockEndAt(d int64) int64 { + b := it.blockIndexAt(d) + if b >= len(it.tp.blockLastDoc) { + return math.MaxInt64 + } + return it.tp.blockLastDoc[b] +} + +// chooseSkip picks a cursor in [0..pivot] whose curDoc < target (so it makes +// progress), preferring the largest term max-impact (skip the heaviest list). The +// block-skip / align callers guarantee at least one such cursor exists. +func chooseSkip(iters []*wandIter, pivot int, target int64) int { + best := -1 + var bestImpact float32 + for i := 0; i <= pivot; i++ { + if iters[i].doc() < target && (best < 0 || iters[i].maxImpact > bestImpact) { + best = i + bestImpact = iters[i].maxImpact + } + } + if best < 0 { + best = 0 // defensive; should not happen + } + return best +} + +// topKHeap is the bounded top-k-by-score heap shared by the WAND and full boolean +// evaluators: a SoA FastMaxHeap keyed by ord (int64) with distance = -score, so keeping +// the k SMALLEST distances keeps the k HIGHEST scores with zero per-candidate allocation +// and no interface dispatch. Its root distance is the current threshold θ (the k-th best +// score, negated). Equal scores are equally relevant — ties are unspecified. +type topKHeap = vectorindex.FastMaxHeap[float32, int64] + +// newTopKHeap allocates the bounded heap's backing buffers. k is capped to maxDocs (the +// segment's live doc count) because the heap can never hold more than that many results: +// FastMaxHeap needs pre-sized buffers, so an absurd pushed LIMIT (e.g. ORDER BY score +// LIMIT 5e8) would otherwise eagerly allocate 2×k slices (~GBs) and OOM the CN, even on a +// tiny table. Bounding to maxDocs is the FastMaxHeap analogue of bm25's lazily-grown topK. +func newTopKHeap(k int, maxDocs int64) *topKHeap { + if int64(k) > maxDocs { + k = int(maxDocs) + } + if k < 0 { + k = 0 + } + return vectorindex.NewFastMaxHeap[float32, int64](k, make([]int64, k), make([]float32, k)) +} + +// searchWAND runs WAND over the disjunctive single-term SHOULD clauses and +// returns the top-k, score desc (ties by ascending ord). allow (nil = allow all) is +// the WHERE-clause prefilter: a doc is admitted to the top-k only if its ord passes, +// so the LIMIT bounds the FILTERED set. Block-skip stays valid — blockSum is a score +// upper bound over every doc in the region regardless of the filter, so a skipped +// region contains no admissible doc that could beat θ. +// buildWandIters makes a posting cursor for each present SHOULD term, with its +// max-impact bound under the given (global or local) stats. Shared by the top-k +// searchWAND and the no-LIMIT streamWAND. +func (s *Segment) buildWandIters(clauses []clause, algo ScoreAlgo, gs *globalStats, avgDocLen float64) []*wandIter { + iters := make([]*wandIter, 0, len(clauses)) + for _, c := range clauses { + pl, ok := s.lookup(c.terms[0]) + if !ok || pl.df() == 0 { + continue + } + idf2 := gs.idfFor(s, c.terms[0], pl) + b := getWandBuf() + it := &wandIter{ + tp: pl, + curBlk: -1, + bDocs: b.docs, + bTfs: b.tfs, + buf: b, + idf2: idf2, + weight: c.weight, + maxImpact: c.weight * s.termMaxImpact(algo, idf2, pl, avgDocLen), + } + it.refresh() // prime cur for idx=0 + iters = append(iters, it) + } + return iters +} + +func (s *Segment) searchWAND(clauses []clause, algo ScoreAlgo, k int, allow Membership, gs *globalStats) []Result { + avgDocLen := gs.avgdl(s) + + iters := s.buildWandIters(clauses, algo, gs, avgDocLen) + defer releaseWandIters(iters) // recycle the per-cursor block buffers + if len(iters) == 0 { + return nil + } + + h := newTopKHeap(k, s.N) + theta := float32(math.Inf(-1)) // until the heap holds k, accept everything + + for { + // Order cursors by current doc ascending (exhausted ones, doc()==sentinel, + // sort last). Insertion sort, NOT sort.Slice: the iters are nearly sorted + // between iterations (only the skipped cursor moved), so this is O(len) + // here; more importantly sort.Slice boxes the slice into an interface{} + // (runtime.convTslice) and heap-allocates the less closure on every call — + // in this hot per-pivot loop that alloc churn (plus the reflect Swapper) + // dominated the entire query CPU. + for i := 1; i < len(iters); i++ { + it := iters[i] + di := it.doc() + j := i - 1 + for j >= 0 && iters[j].doc() > di { + iters[j+1] = iters[j] + j-- + } + iters[j+1] = it + } + if iters[0].atEnd() { + break // all exhausted + } + + // Term-level WAND pivot: first cursor at which the cumulative max-impact + // reaches θ. + acc, pivot := float32(0), -1 + for i, it := range iters { + if it.atEnd() { + break + } + acc += it.maxImpact + if acc >= theta { + pivot = i + break + } + } + if pivot < 0 { + break // no remaining document can reach the threshold + } + pivotDoc := iters[pivot].doc() + + // Extend the pivot over every cursor also sitting on pivotDoc, so the + // block-max sum + skip bounds account for ALL of pivotDoc's contributors (a + // cursor past the term-UB pivot can still be at pivotDoc). Required for the + // block-skip bound to be a valid upper bound. + for pivot+1 < len(iters) && iters[pivot+1].doc() == pivotDoc { + pivot++ + } + + // Block-Max refinement: the summed per-block upper bounds of iters[0..pivot] + // for the blocks covering pivotDoc bound every doc in [pivotDoc, minBlockEnd]. + // If that can't exceed θ, skip the whole region instead of scoring pivotDoc. + // Skipped docs have score ≤ blockSum ≤ θ, which `score > θ` rejects anyway → + // same top-k SET, less work. (bm25 uses the identical `blockSum <= theta`.) + blockSum := float32(0) + for i := 0; i <= pivot; i++ { + blockSum += iters[i].blockMax(pivotDoc, algo, avgDocLen) + } + if blockSum <= theta { + next := int64(math.MaxInt64) + for i := 0; i <= pivot; i++ { + if e := iters[i].blockEndAt(pivotDoc); e < next { + next = e + } + } + next++ // first doc beyond the limiting block + if pivot+1 < len(iters) { + if nd := iters[pivot+1].doc(); nd < next { + next = nd // never skip past an unaccounted contributor + } + } + if next <= pivotDoc { + next = pivotDoc + 1 // guarantee forward progress + } + iters[chooseSkip(iters, pivot, next)].skipTo(next) + continue + } + + if iters[0].doc() == pivotDoc { + // All cursors before the pivot are already on pivotDoc. Score + admit it + // only if it passes the WHERE prefilter; the cursor advance always runs so a + // filtered-out pivot never stalls the walk. + if allowed(allow, pivotDoc) { + var score float32 + for _, it := range iters { + if it.doc() == pivotDoc { + // tf-aware, matching the full-scan evalClause scorer so WAND returns the + // identical ranking (fulltext2 keeps real tf; see termMaxImpact). + score += it.weight * s.scoreTerm(algo, float64(it.tf()), it.idf2, pivotDoc, avgDocLen) + } + } + // FastMaxHeap.Push fills until full, then admits iff -score < root + // distance (i.e. score > θ) with a single sift — the same replace-root + // behaviour, done internally. θ is the k-th best score = negated root. + h.Push(pivotDoc, -score) + if h.Full() { + _, negRoot, _ := h.Peek() + theta = -negRoot + } + } + for _, it := range iters { + if it.doc() == pivotDoc { + it.idx++ + it.refresh() + } + } + } else { + // Not aligned: move a lagging cursor (before the pivot) up to pivotDoc. + iters[chooseSkip(iters, pivot, pivotDoc)].skipTo(pivotDoc) + } + } + + return heapToResults(s, h) +} + +// termMaxImpact is the largest weighted-free score a term can contribute to any +// document, from its raw maxTf (and minDocLen for BM25's saturating factor). +func (s *Segment) termMaxImpact(algo ScoreAlgo, idf2 float64, pl *termPostings, avgDocLen float64) float32 { + if algo == BM25 { + return float32(idf2 * bm25Factor(float64(pl.maxTf), pl.minDocLen, avgDocLen)) + } + return float32(float64(pl.maxTf) * idf2) // TfIdf +} + +// heapToResults drains the bounded top-k heap into results ordered by score descending +// (equal scores unspecified). FastMaxHeap.Pop yields the largest distance (= lowest +// score) first, so filling back-to-front lands the output in score-desc order. +func heapToResults(s *Segment, h *topKHeap) []Result { + n := h.Len() + results := make([]Result, n) + for i := n - 1; i >= 0; i-- { + ord, negScore, _ := h.Pop() + results[i] = Result{Pk: s.pk(ord), Score: -negScore, Include: s.decodeInclude(ord)} + } + return results +} diff --git a/pkg/indexplugin/all/all.go b/pkg/indexplugin/all/all.go index 4487956f7efb5..091a5526e1248 100644 --- a/pkg/indexplugin/all/all.go +++ b/pkg/indexplugin/all/all.go @@ -65,6 +65,7 @@ package all import ( _ "github.com/matrixorigin/matrixone/pkg/fulltext/plugin" + _ "github.com/matrixorigin/matrixone/pkg/fulltext2/plugin" _ "github.com/matrixorigin/matrixone/pkg/vectorindex/hnsw/plugin" _ "github.com/matrixorigin/matrixone/pkg/vectorindex/ivfflat/plugin" ) diff --git a/pkg/indexplugin/async.go b/pkg/indexplugin/async.go new file mode 100644 index 0000000000000..8f98c28b4f68c --- /dev/null +++ b/pkg/indexplugin/async.go @@ -0,0 +1,58 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import "github.com/matrixorigin/matrixone/pkg/catalog" + +// Async resolution — one canonical entry point: IsAsync. +// +// An index's hidden tables are maintained either SYNCHRONOUSLY (updated in the +// same transaction as the source DML) or ASYNCHRONOUSLY (a background ISCP CDC +// pipeline catches up). Whether an index is async has two inputs, which used +// to be combined ad-hoc at every call site (and were easy to get wrong by +// checking only one): +// +// - algorithm identity — hnsw / bm25 / cagra / ivfpq / fulltext2 are ALWAYS +// async, declared in their static catalog SyncDescriptor().AlwaysAsync; +// - user opt-in — a normally-synchronous engine (classic fulltext, ivfflat) +// put into async via the `async` param (catalog.IndexParamAsync). +// +// These resolvers live here, not in catalog, because the first input needs the +// plugin registry (catalog sits below it). They read the STATIC SyncDescriptor +// (no algoParams) — the descriptor is a per-algorithm constant, so nothing here +// needs a per-index descriptor and no caller has to synthesize one. +// +// IsAsync is the union of both and is what callers should reach for; +// AlwaysAsync is exposed for the few generic sites (clone / CDC validity) that +// must distinguish "intrinsically async" from "user opted in". + +// AlwaysAsync reports whether the algorithm is intrinsically async (CDC-only) +// regardless of the user's async param: true for an identity-async algorithm +// (hnsw/bm25/cagra/ivfpq/fulltext2, via its static SyncDescriptor). An +// unregistered algo is never always-async. +func AlwaysAsync(algo, algoParams string) bool { + p, ok := Get(algo) + return ok && p.Catalog().SyncDescriptor().AlwaysAsync +} + +// IsAsync is the canonical async check: an index is asynchronously (CDC) +// maintained iff the algorithm is always-async (AlwaysAsync) OR the user opted +// in via the async param. Use this unless you specifically need one source. +func IsAsync(algo, algoParams string) (bool, error) { + if AlwaysAsync(algo, algoParams) { + return true, nil + } + return catalog.IndexParamAsync(algoParams) +} diff --git a/pkg/indexplugin/catalog/hooks.go b/pkg/indexplugin/catalog/hooks.go index 3567ae3b16cad..fe9dd564267f4 100644 --- a/pkg/indexplugin/catalog/hooks.go +++ b/pkg/indexplugin/catalog/hooks.go @@ -33,6 +33,15 @@ type Hooks interface { // callers index by name. HiddenTableTypes() []string + // IsVectorIndex reports the index KIND: true for an ANN vector index + // (HNSW / IVF-FLAT / IVF-PQ / CAGRA), false for a fulltext-family engine + // (classic fulltext, fulltext2). It is the static per-plugin classification + // behind indexplugin.IsVectorIndexAlgo, so the multi-table-vector-index + // gate is a capability the plugin declares — not an algorithm-name exception + // each call site has to keep in sync. A new fulltext-style engine returns + // false here and is classified correctly everywhere for free. + IsVectorIndex() bool + // ParamsFromTree extracts and validates the WITH(...) options from a // CREATE INDEX statement, returning the canonical params map that gets // JSON-encoded into mo_indexes. Replaces one switch arm of @@ -276,6 +285,13 @@ type SyncDescriptor struct { // false for cuvs algorithms (CAGRA, IVF-PQ) which have no "lists" // or training-sample concept — they always rebuild on cadence. IdxcronListsAware bool + + // IdxcronReindexOption is an extra REINDEX option keyword the executor + // inserts before FORCE_SYNC when building the cron-triggered ALTER — + // e.g. "MERGE" so a bm25 index runs incremental fold+tiered compaction + // instead of a full rebuild-from-source. Empty for the vector algorithms + // (plain rebuild); the executor omits it when unset. + IdxcronReindexOption string } // AlterTableCloneBehavior declares the per-hidden-table semantics diff --git a/pkg/indexplugin/compile/hooks.go b/pkg/indexplugin/compile/hooks.go index 05dff69aeba87..b5a08041e1d0b 100644 --- a/pkg/indexplugin/compile/hooks.go +++ b/pkg/indexplugin/compile/hooks.go @@ -155,8 +155,11 @@ type Hooks interface { // HandleReindex is the ALTER … REINDEX path. forceSync mirrors the // existing IVF-FLAT semantics (run synchronously inside the txn) and is - // ignored by algorithms that do not support it. - HandleReindex(ctx CompileContext, indexDefs map[string]*plan.IndexDef, forceSync bool) error + // ignored by algorithms that do not support it. merge requests incremental + // compaction (fold + tiered merge of already-built segments) instead of a + // full rebuild-from-source; only the bm25 index honors it, every other + // algorithm ignores it and rebuilds. + HandleReindex(ctx CompileContext, indexDefs map[string]*plan.IndexDef, forceSync, merge bool) error // RestoreInitSQL returns (startFromNow, initSQL) for the restored index's // CDC. initSQL rebuilds the index from the cloned rows — run post-commit by @@ -208,6 +211,13 @@ type ReindexParamUpdate struct { // Sourced from the parse tree (c.stmt) at the compile site, so no plan // proto field is needed to carry them. nil/empty means none specified. Params map[string]string + + // Merge is true when the reindex is a MERGE (compact the CDC tail into the + // base in place) rather than a full REBUILD (re-derive the base from the + // source table). A plugin uses it to reject params that a MERGE cannot honor + // — e.g. fulltext2 forbids changing POSITION_FREE on a MERGE, since a + // tail-into-base compaction cannot re-derive positions the base does not hold. + Merge bool } // MergeReindexParams is the shared body for a plugin's diff --git a/pkg/indexplugin/idxcron/hooks.go b/pkg/indexplugin/idxcron/hooks.go index 2430601391ecf..101378011d031 100644 --- a/pkg/indexplugin/idxcron/hooks.go +++ b/pkg/indexplugin/idxcron/hooks.go @@ -92,3 +92,13 @@ type Hooks interface { // IVF-FLAT, trivial-true for HNSW / fulltext. Updatable(in UpdatableInput) (ok bool, reason string, err error) } + +// ReindexOptioner is an OPTIONAL idxcron hook (checked via type assertion). A plugin that +// implements it chooses the REINDEX option per cron fire, OVERRIDING the static +// SyncDescriptor.IdxcronReindexOption. Fulltext uses it to return "MERGE" (incremental +// fold+tiered compaction) normally, but "" (a full REBUILD) once the dead-doc fraction is +// high enough that a rebuild reclaims more than it costs. Plugins that don't implement it +// keep their descriptor's fixed option. Called only when Updatable returned ok. +type ReindexOptioner interface { + ReindexOption(in UpdatableInput) (option string, err error) +} diff --git a/pkg/indexplugin/iscp/import.go b/pkg/indexplugin/iscp/import.go index 48befbf68559a..73151d27058ba 100644 --- a/pkg/indexplugin/iscp/import.go +++ b/pkg/indexplugin/iscp/import.go @@ -38,6 +38,7 @@ package iscp import ( _ "github.com/matrixorigin/matrixone/pkg/fulltext/plugin/iscp" + _ "github.com/matrixorigin/matrixone/pkg/fulltext2/plugin/iscp" _ "github.com/matrixorigin/matrixone/pkg/vectorindex/hnsw/plugin/iscp" _ "github.com/matrixorigin/matrixone/pkg/vectorindex/ivfflat/plugin/iscp" ) diff --git a/pkg/indexplugin/plugin.go b/pkg/indexplugin/plugin.go index c766b47e9e709..4f2fed387b56a 100644 --- a/pkg/indexplugin/plugin.go +++ b/pkg/indexplugin/plugin.go @@ -96,7 +96,7 @@ func All() []AlgoPlugin { // IsVectorIndexAlgo reports whether algo is a registered vector // index algorithm (HNSW, CAGRA, IVF-PQ, IVF-FLAT) — i.e. plugin- -// registered AND not the fulltext algorithm. Replaces the chain +// registered AND of the vector KIND. Replaces the chain // // catalog.IsIvfIndexAlgo(a) || catalog.IsHnswIndexAlgo(a) || // catalog.IsCagraIndexAlgo(a) || catalog.IsIvfpqIndexAlgo(a) @@ -104,12 +104,21 @@ func All() []AlgoPlugin { // at every site that needs to gate "is this a multi-table vector // index?". Use IsFullTextIndexAlgo for fulltext and IsPluginAlgo for // "registered with the plugin system, vector OR fulltext". +// +// Kind is classified by the plugin's static CAPABILITY (Catalog().IsVectorIndex()), +// not by an algo-name check: vector plugins (HNSW / IVF-FLAT / IVF-PQ / CAGRA) +// declare true, the fulltext-family plugins (classic fulltext AND fulltext2) +// declare false. This keeps fulltext2 correctly NON-vector without a second +// fulltext algo-name exception here; a future fulltext-style engine is classified +// right for free. (Previously this special-cased only classic fulltext by name, so +// a registered fulltext2 wrongly reported as a vector index and had to be excluded +// again ad hoc at call sites, e.g. compile/ddl.go.) func IsVectorIndexAlgo(algo string) bool { - if IsFullTextIndexAlgo(algo) { + p, ok := Get(algo) + if !ok { return false } - _, ok := Get(algo) - return ok + return p.Catalog().IsVectorIndex() } // IsFullTextIndexAlgo reports whether algo is the fulltext index diff --git a/pkg/iscp/executor.go b/pkg/iscp/executor.go index 9fc26870662e0..3ed42c1730369 100644 --- a/pkg/iscp/executor.go +++ b/pkg/iscp/executor.go @@ -32,6 +32,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/fileservice" "github.com/matrixorigin/matrixone/pkg/objectio" "github.com/matrixorigin/matrixone/pkg/taskservice" @@ -85,6 +86,7 @@ func ISCPTaskExecutorFactory( attachToTask func(context.Context, uint64, taskservice.ActiveRoutine) error, cdUUID string, mp *mpool.MPool, + rootFS fileservice.FileService, ) func(ctx context.Context, task task.Task) (err error) { return func(ctx context.Context, task task.Task) (err error) { var exec *ISCPTaskExecutor @@ -108,6 +110,10 @@ func ISCPTaskExecutorFactory( if err != nil { return } + // Publish the CN root FileService onto the executor BEFORE Start() (hence + // before RegisterExecutorRuntime and any worker iteration), so index CDC + // consumers can resolve the LOCAL SSD spill dir via GetExecutorRuntime. + exec.rootFS = rootFS defer exec.terminateLifetime() if err = attachToTask(ctx, task.GetID(), exec); err != nil { return diff --git a/pkg/iscp/fulltext2_consumer.go b/pkg/iscp/fulltext2_consumer.go new file mode 100644 index 0000000000000..7348cc458987b --- /dev/null +++ b/pkg/iscp/fulltext2_consumer.go @@ -0,0 +1,148 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iscp + +import ( + "context" + "time" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/fulltext2" + "github.com/matrixorigin/matrixone/pkg/logutil" + veccache "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" +) + +// RunFulltext2 is the ISCP consumer loop for the fulltext2 positional index — the +// direct analogue of RunWand. It STREAMS each flush's CDC blob into a fulltext2 +// TailBuilder (tokenizing insert rows per the index parser into capacity-capped +// segments, spilling each sealed segment to a temp file as it fills), so peak +// memory is one open segment, not the whole stream. On channel close it appends +// the spilled segments (+ one delete batch, delete-first) as tag=1 CdcTail frames +// at the next chunk_id, in one txn, advancing the watermark. +// +// CDC/txn-coupled: not exercised by package unit tests (needs a live mo_ctl + CDC +// pipeline); the engine primitives it calls (Cdc, TailBuilder, NextTailChunkId, +// TailFileInsertSqls) are unit-tested in pkg/fulltext2. +func RunFulltext2(c *IndexConsumer, ctx context.Context, errch chan error, r DataRetriever) { + w, ok := c.sqlWriter.(*Fulltext2SqlWriter) + if !ok { + errch <- moerr.NewInternalError(ctx, "fulltext2 iscp Run: unexpected writer type") + return + } + + // Parser-aware tokenize (ngram/gojieba/json) so build and query tokens match. + tokenize, err := fulltext2.CdcTokenizer(w.cfg.Parser) + if err != nil { + errch <- err + return + } + var bopts []fulltext2.BuildOpt + if w.cfg.PositionFree { + bopts = append(bopts, fulltext2.WithPositionFree()) + } + // Route the CDC tail's segment spills onto the LOCAL (SSD) fileservice's __fulltext2 + // dir — the same fast mount the sync build uses — so tail mmap page faults come off + // the SSD, not /tmp. The CN root FileService is published on the ISCP executor by + // ISCPTaskExecutorFactory; resolve it by this CN's UUID. spillDir="" (no LOCAL + // attached / executor not found) falls back to the OS temp dir, unchanged behavior. + var spillDir string + if exec, ok := GetExecutorRuntime(c.cnUUID); ok { + spillDir = fulltext2.LocalSpillDir(ctx, exec.rootFS) + } + tb, err := fulltext2.NewTailBuilder(w.pkType, w.capacity, w.postingCap, spillDir, tokenize, bopts...) + if err != nil { + errch <- err + return + } + defer tb.Cleanup() + + datatype := r.GetDataType() + nevents := 0 + + for { + select { + case <-ctx.Done(): + return + case e := <-errch: + errch <- e + return + case blob, ok := <-c.sqlBufSendCh: + if !ok { + segs, ferr := tb.Finish() + if ferr != nil { + errch <- ferr + return + } + err = sqlexec.RunTxnWithSqlContext(ctx, c.cnEngine, c.cnTxnClient, c.cnUUID, r.GetAccountID(), time.Hour, nil, nil, + func(sqlproc *sqlexec.SqlProcess, cbdata any) (err error) { + startChunk, err := fulltext2.NextTailChunkId(sqlproc, w.cfg) + if err != nil { + return err + } + // The frames are already on disk (TailBuilder spilled them into packed spool + // files): INSERT them via load_file, batched at maxInsertTuples rows per + // statement ACROSS frames — so a burst of tiny frames costs ~totalChunks/maxInsertTuples + // RunSql round-trips in this one txn, not one INSERT per frame. chunk_ids stay + // contiguous in frame order, so recency is unchanged. + sqls, chunkID := fulltext2.TailFramesInsertSqls(w.cfg, startChunk, segs) + for _, s := range sqls { + res, e := sqlexec.RunSql(sqlproc, s) + if e != nil { + return e + } + res.Close() + } + // Per-flush-cycle sink summary: Debug, not Info — a continuously-ingesting index + // flushes often, so this would flood production logs at Info (see the 46 GB + // stdout incident). Available at Debug when diagnosing CDC ingest. + logutil.Debugf("[ftv2-sink] db=%s index=%s type=%d events=%d frames=%d chunk_id=%d..%d", + w.cfg.DbName, w.cfg.IndexTable, datatype, nevents, len(segs), startChunk, chunkID) + if datatype == ISCPDataType_Tail { + sqlctx := sqlproc.SqlCtx + return r.UpdateWatermark(sqlproc.GetContext(), sqlctx.GetService(), sqlctx.Txn()) + } + return nil + }) + if err != nil { + errch <- err + return + } + // Evict the cached search index so the next query reloads tag=0 + the + // freshly-appended tag=1 frames, instead of serving the warm (stale) + // cache until its idle TTL. Only when frames were actually written. + // NOTE: this eviction is LOCAL to this CN — cross-CN cache coherence is + // a known cache-layer gap deferred to a follow-up PR (see the Decision + // block on veccache.VectorIndexCache.Remove). + if len(segs) > 0 { + veccache.Cache.Remove(w.cfg.IndexTable) + logutil.Debugf("[ftv2-sink] evicted search cache for index=%s", w.cfg.IndexTable) // per-flush: Debug, not Info + } + return + } + + cdc, derr := fulltext2.DecodeCdc(blob) + if derr != nil { + errch <- derr + return + } + nevents += len(cdc.Events) + if aerr := tb.AddBatch(cdc); aerr != nil { + errch <- aerr + return + } + } + } +} diff --git a/pkg/iscp/fulltext2_sqlwriter.go b/pkg/iscp/fulltext2_sqlwriter.go new file mode 100644 index 0000000000000..3fce4efc5cbe0 --- /dev/null +++ b/pkg/iscp/fulltext2_sqlwriter.go @@ -0,0 +1,396 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iscp + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/sqlquote" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/fulltext2" + indexplugin "github.com/matrixorigin/matrixone/pkg/indexplugin" + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/txn/client" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" + "github.com/matrixorigin/matrixone/pkg/vm/engine" +) + +const defaultFulltext2Capacity int64 = 1000000 +const defaultFulltext2PostingCapacity int64 = 8_000_000 + +// Fulltext2SqlWriter is the ISCP sink adapter for the fulltext2 positional index. +// Like WandSqlWriter it is model-building: it accumulates one flush's CDC rows +// into a binary fulltext2.Cdc blob (ToSql), which RunFulltext2 decodes, tokenizes +// (per the index parser), and turns into tag=1 CdcTail frames. Binary (typed pk) +// because a fulltext2 pk is `any` (int64 OR varchar). +type Fulltext2SqlWriter struct { + cfg fulltext2.TableConfig // DbName + ftv2_index (storage) + ftv2_meta (metadata) + parser + pkType int32 // types.T of the source primary key + pkPos int32 // pk column index in the extracted row + textPos []int32 // indexed text columns (all idxdef.Parts) — multi-column joins with '\n' + textTypes []int32 // types.T of each textPos column (to detect T_datalink), textPos-aligned + // INCLUDE columns: their positions in the extracted row + types.T (column order), so a + // CDC insert/upsert carries their actual values into the tail segment's docmap. + includePos []int32 + includeTypes []int32 + capacity int64 // max docs per delta segment (max_index_capacity) + postingCap int64 // max postings per delta segment (max_postings_capacity) + + // cnEngine/cnTxnClient/cnUUID are the CDC consumer's handles (set by NewIndexConsumer), + // used to open a short txn with a FULL sqlproc so a DATALINK column can be resolved to + // its file CONTENT during CDC — catalog-backed stage:// resolution + PDF/DOCX text + // extraction, exactly as the sync build does (fulltext2_create GetPlainText). A minimal + // FileService-only proc is NOT enough: stage:// needs catalog/txn. nil (e.g. unit tests) + // ⇒ datalink columns index the URL string. + cnEngine engine.Engine + cnTxnClient client.TxnClient + cnUUID string + datalinkPos bool // any textPos column is a datalink + // srcAccountID is the SOURCE tenant that owns the indexed table (r.GetAccountID()), + // set per-iteration by IndexConsumer.Consume. A datalink stage:// is a per-account + // catalog object, so its resolution MUST run under this tenant — NOT the consumer + // ctx's tenant, which ISCP forces to System_Account (iteration.go), which would look + // the stage up in the wrong tenant's mo_stages. + srcAccountID uint32 + + cdc *fulltext2.Cdc + ndata int + last string +} + +// datalinkText resolves a datalink column to its file's plain text (PDF/DOCX extracted), +// exactly as the sync build does in fulltext2_create.rowTerms, so a datalink column is +// indexed by CONTENT consistently whether a row arrives at CREATE or via CDC. It opens a +// short txn to get a FULL sqlproc, then resolves via SELECT load_text(cast(url as datalink)) +// run by the internal SQL executor (which has a real proc: FileService + catalog for +// stage://). The bare background sqlproc has Proc==nil, so calling datalink.GetPlainText +// directly panics — hence the SQL round-trip. load_text (not load_file) returns the +// EXTRACTED plain text (PDF/DOCX parsed), the same text the synchronous build indexes, so +// CDC and build tokenize identically. +// +// On any resolution error the error is PROPAGATED (not swallowed) — parity with the sync +// build (fulltext2_create returns the error on a bad datalink). The ISCP consumer then +// retries the iteration without advancing the watermark, so a transient failure self-heals +// and a permanently-bad datalink surfaces as a stalled-job error rather than silently +// indexing the URL string (which would bake wrong terms into the index forever). +func (w *Fulltext2SqlWriter) datalinkText(ctx context.Context, rawurl string) (out []byte, err error) { + if w.cnEngine == nil { + // no resolver context attached (unit tests) — index the URL string. + return []byte(rawurl), nil + } + // A panic in the resolution path must not crash the CDC consumer goroutine (that would + // stall ALL maintenance for the index); convert it to an error so the iteration retries. + defer func() { + if r := recover(); r != nil { + out = nil + err = moerr.NewInternalErrorNoCtx(fmt.Sprintf("fulltext2 datalink resolve panic for %q: %v", rawurl, r)) + } + }() + // Resolve under the SOURCE tenant (srcAccountID), NOT GetAccountId(ctx): ISCP forces the + // consumer ctx tenant to System_Account (iteration.go), so a tenant-owned stage://name/... + // would otherwise be looked up in the system tenant's mo_stages and fail (or resolve an + // unrelated same-named system stage). runTxnWithSqlContext sets the txn tenant from the + // account we pass, overriding the ctx tenant. + err = runTxnWithSqlContext(ctx, w.cnEngine, w.cnTxnClient, w.cnUUID, w.srcAccountID, time.Minute, nil, nil, + func(sqlproc *sqlexec.SqlProcess, _ any) error { + sql := fmt.Sprintf("SELECT load_text(cast(%s as datalink))", sqlquote.String(rawurl)) + res, e := sqlexec.RunSql(sqlproc, sql) + if e != nil { + return e + } + defer res.Close() + for _, b := range res.Batches { + if b == nil || len(b.Vecs) == 0 || b.Vecs[0] == nil || b.Vecs[0].Length() == 0 { + continue + } + if b.Vecs[0].IsNull(0) { + continue // empty content → no tokens (matches the sync build's empty-doc skip) + } + content := b.Vecs[0].GetBytesAt(0) + out = append([]byte(nil), content...) // copy out before the txn (and its mpool) unwinds + return nil + } + return nil + }) + if err != nil { + return nil, err + } + return out, nil +} + +var _ IndexSqlWriter = (*Fulltext2SqlWriter)(nil) + +// NewFulltext2SqlWriter resolves the pk / text columns, the storage+metadata +// tables, the parser, and the capacity from the fulltext2 index def. +func NewFulltext2SqlWriter(algo string, jobID JobID, info *ConsumerInfo, tabledef *plan.TableDef, indexdef []*plan.IndexDef) (IndexSqlWriter, error) { + idxdef := indexdef[0] + + var storage, meta string + for _, idx := range indexdef { + switch idx.IndexAlgoTableType { + case catalog.FullText2Index_TblType_Storage: + storage = idx.IndexTableName + case catalog.FullText2Index_TblType_Metadata: + meta = idx.IndexTableName + } + } + if storage == "" || meta == "" { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 sink: storage/metadata hidden tables not found") + } + if len(idxdef.Parts) == 0 { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 sink: index has no source column") + } + + pkPos := tabledef.Name2ColIndex[tabledef.Pkey.PkeyColName] + pkTyp := tabledef.Cols[pkPos].Typ + textPos := make([]int32, 0, len(idxdef.Parts)) + textTypes := make([]int32, 0, len(idxdef.Parts)) + datalinkPos := false + for _, p := range idxdef.Parts { + ci := tabledef.Name2ColIndex[p] + textPos = append(textPos, ci) + ty := tabledef.Cols[ci].Typ.Id + textTypes = append(textTypes, ty) + if types.T(ty) == types.T_datalink { + datalinkPos = true + } + } + + // parser + capacity from algo_params (the sinker runs in an internal ISCP proc + // with no live resolver — resolve captured session_vars directly, like bm25). + flat := map[string]string{} + if m, e := catalog.IndexParamsStringToMap(idxdef.IndexAlgoParams); e == nil { + flat = m + } + var resolve indexplugin.ResolveVarFunc + if sv, e := catalog.IndexParamsSessionVars(idxdef.IndexAlgoParams); e == nil && len(sv) > 0 { + if md, e2 := sqlexec.NewMetadataFromJson(string(sv)); e2 == nil && md != nil { + // Soft resolver: AlgoParamInt treats absent as "use default", so a var + // the blob doesn't hold must return (nil, nil), not a logged error. + resolve = md.ResolveVariableSoft + } + } + capacity, err := indexplugin.AlgoParamInt(flat[catalog.IndexAlgoParamMaxIndexCapacity], resolve, "fulltext_max_index_capacity", defaultFulltext2Capacity) + if err != nil { + return nil, err + } + postingCap, err := indexplugin.AlgoParamInt(flat[catalog.IndexAlgoParamMaxPostingsCapacity], resolve, "fulltext_max_postings_capacity", defaultFulltext2PostingCapacity) + if err != nil { + return nil, err + } + + // INCLUDE columns (reload-safe from algo_params): resolve each to its row position + + // type so CDC carries its value into the tail docmap. cdc.IncludeTypes travels in the + // blob so RunFulltext2's TailBuilder decodes it without an external schema. + var includePos, includeTypes []int32 + if raw := flat[catalog.IncludedColumns]; raw != "" { + names, perr := catalog.ParseIncludeColumnsValue(raw) + if perr != nil { + return nil, perr + } + includePos = make([]int32, len(names)) + includeTypes = make([]int32, len(names)) + for i, name := range names { + ci, ok := tabledef.Name2ColIndex[name] + if !ok { + return nil, moerr.NewInternalErrorNoCtx("fulltext2 sink: INCLUDE column " + name + " not found") + } + includePos[i] = ci + includeTypes[i] = tabledef.Cols[ci].Typ.Id + } + } + + cdc := fulltext2.NewCdc(int32(pkTyp.Id)) + cdc.IncludeTypes = includeTypes + return &Fulltext2SqlWriter{ + cfg: fulltext2.TableConfig{DbName: info.DBName, IndexTable: storage, MetadataTable: meta, Parser: flat["parser"], PositionFree: flat[catalog.IndexAlgoParamPositionFree] == "true"}, + pkType: int32(pkTyp.Id), + pkPos: pkPos, + textPos: textPos, + textTypes: textTypes, + includePos: includePos, + includeTypes: includeTypes, + datalinkPos: datalinkPos, + capacity: capacity, + postingCap: postingCap, + cdc: cdc, + }, nil +} + +// rowInclude reads this row's INCLUDE column values (column order) out of the extracted +// row, copying them out before the txn's mpool unwinds. nil when the index has no INCLUDE +// columns; a NULL value stays nil. +func (w *Fulltext2SqlWriter) rowInclude(row []any) []any { + if len(w.includePos) == 0 { + return nil + } + out := make([]any, len(w.includePos)) + for i, pos := range w.includePos { + out[i] = ftCopyPk(row[pos]) + } + return out +} + +func (w *Fulltext2SqlWriter) CheckLastOp(op string) bool { return len(w.last) == 0 || w.last == op } +func (w *Fulltext2SqlWriter) Empty() bool { return w.cdc.Len() == 0 } +func (w *Fulltext2SqlWriter) Full() bool { return w.ndata >= MAX_CDC_DATA_SIZE } +func (w *Fulltext2SqlWriter) ToSql() ([]byte, error) { return w.cdc.Encode() } + +func (w *Fulltext2SqlWriter) Reset() { + w.cdc = fulltext2.NewCdc(w.pkType) + w.cdc.IncludeTypes = w.includeTypes + w.ndata = 0 + w.last = "" +} + +func (w *Fulltext2SqlWriter) Insert(ctx context.Context, row []any) error { + w.last = vectorindex.CDC_INSERT + text, err := w.rowText(ctx, row) + if err != nil { + return err + } + pk := ftCopyPk(row[w.pkPos]) + inc := w.rowInclude(row) + w.cdc.Insert(pk, text, inc) + // Count the ACTUAL retained bytes (text + varlena pk + INCLUDE values), not a flat +16, so + // Full() bounds the real serialized batch size — a batch of wide-VARCHAR pk/INCLUDE rows + // used to blow far past MAX_CDC_DATA_SIZE before flushing. + w.ndata += len(text) + ftRowDataBytes(pk, inc) + 16 + return nil +} + +func (w *Fulltext2SqlWriter) Upsert(ctx context.Context, row []any) error { + w.last = vectorindex.CDC_UPSERT + text, err := w.rowText(ctx, row) + if err != nil { + return err + } + pk := ftCopyPk(row[w.pkPos]) + inc := w.rowInclude(row) + w.cdc.Upsert(pk, text, inc) + w.ndata += len(text) + ftRowDataBytes(pk, inc) + 16 + return nil +} + +func (w *Fulltext2SqlWriter) Delete(ctx context.Context, row []any) error { + // a delete row carries only the pk in position 0 (mirrors HnswSqlWriter). + w.last = vectorindex.CDC_DELETE + pk := ftCopyPk(row[0]) + w.cdc.Delete(pk) + w.ndata += ftAnyBytes(pk) + 16 + return nil +} + +// ftAnyBytes is the retained byte size of a boxed pk / INCLUDE value: content length for a +// varlena value, a small fixed estimate for a scalar, 0 for SQL NULL. ftRowDataBytes sums a +// row's pk + INCLUDE payload. Both feed w.ndata so Full() bounds the true batch data size +// (matching the build-side Builder.dataBytes accounting). +func ftAnyBytes(v any) int { + switch x := v.(type) { + case nil: + return 0 + case string: + return len(x) + case []byte: + return len(x) + default: + return 8 + } +} + +func ftRowDataBytes(pk any, include []any) int { + n := ftAnyBytes(pk) + for _, v := range include { + n += ftAnyBytes(v) + } + return n +} + +// rowText joins the indexed columns with '\n' (matching fulltext2_create's build +// tokenization). If ANY indexed column is NULL the doc yields no tokens — the +// whole-doc-skip the create-TVF's rowTerms does — so CREATE and CDC tokenize a +// row identically (a doc's searchability must not depend on which path indexed +// it). For a json parser each column is flattened to its leaf values PER COLUMN +// (FlattenJSONColumn), exactly as rowTerms does, so CdcTokenizer then just ngrams the +// flattened text (it no longer re-flattens). A datalink column is resolved to its file +// CONTENT via load_text (datalinkText) — parity with the sync build's GetPlainText, so +// identical values index identically whether seen at CREATE or via CDC; a resolution error +// is propagated (the CDC iteration retries), matching the sync build. +func (w *Fulltext2SqlWriter) rowText(ctx context.Context, row []any) (string, error) { + for _, pos := range w.textPos { + if row[pos] == nil { + return "", nil + } + } + isJSON := fulltext2.IsJSONParser(w.cfg.Parser) + jsonValue := fulltext2.IsJSONValueParser(w.cfg.Parser) + var b strings.Builder + for i, pos := range w.textPos { + if i > 0 { + b.WriteByte('\n') + } + if isJSON { + // json_value keeps each value as a separable '\n'-joined whole token (matched + // exactly); json space-joins for ngram. CdcTokenizer then tokenizes to match. + var ft []byte + var err error + if jsonValue { + ft, err = fulltext2.FlattenJSONValueColumn(row[pos]) + } else { + ft, err = fulltext2.FlattenJSONColumn(row[pos]) + } + if err != nil { + return "", err + } + b.Write(ft) + } else if types.T(w.textTypes[i]) == types.T_datalink { + // Resolve the datalink to its file CONTENT (parity with the sync build). Any + // resolution error is propagated so the CDC iteration retries (see datalinkText); + // with no resolver attached (unit tests) it indexes the URL string. + dt, err := w.datalinkText(ctx, ftRowText(row[pos])) + if err != nil { + return "", err + } + b.Write(dt) + } else { + b.WriteString(ftRowText(row[pos])) + } + } + return b.String(), nil +} + +func ftRowText(v any) string { + switch t := v.(type) { + case []byte: + return string(t) + case string: + return t + default: + return "" + } +} + +func ftCopyPk(v any) any { + if b, ok := v.([]byte); ok { + return append([]byte(nil), b...) + } + return v +} diff --git a/pkg/iscp/index_consumer.go b/pkg/iscp/index_consumer.go index 2572eae5e194c..42297ba8853a8 100644 --- a/pkg/iscp/index_consumer.go +++ b/pkg/iscp/index_consumer.go @@ -126,6 +126,16 @@ func NewIndexConsumer(cnUUID string, return nil, err } + // Give the fulltext2 sink the CN engine/txn handles so it can open a short txn with a + // FULL sqlproc to resolve DATALINK columns to file CONTENT during CDC (stage:// + + // PDF/DOCX extraction) — parity with the sync build. Done here because the write path + // itself has no proc. Only when the index actually has a datalink column. + if w, ok := sqlwriter.(*Fulltext2SqlWriter); ok && w.datalinkPos { + w.cnEngine = cnEngine + w.cnTxnClient = cnTxnClient + w.cnUUID = cnUUID + } + c := &IndexConsumer{cnUUID: cnUUID, cnEngine: cnEngine, cnTxnClient: cnTxnClient, @@ -530,6 +540,13 @@ func (c *IndexConsumer) Consume(ctx context.Context, r DataRetriever) error { datatype := r.GetDataType() + // Give the fulltext2 writer the SOURCE tenant so it resolves a DATALINK stage:// under + // the account that owns the table — the consumer ctx tenant is System_Account here + // (iteration.go), which is the wrong tenant for a per-account stage lookup. + if w, ok := c.sqlWriter.(*Fulltext2SqlWriter); ok { + w.srcAccountID = r.GetAccountID() + } + // create thread to poll sql var wg sync.WaitGroup wg.Add(1) @@ -561,12 +578,26 @@ func (c *IndexConsumer) Consume(ctx context.Context, r DataRetriever) error { return nil } +// valueRepr picks the value representation the paired writer needs: the fulltext2 +// writer binary-encodes the pk (encodePk), so it needs native Go values (a datetime/ +// time/timestamp/decimal/uuid pk delivered as its SQL-display string would make +// encodePk's native type assertion panic and crash the consumer goroutine, permanently +// stalling index maintenance); every other writer builds SQL text and needs the +// SQL-display string (the historical default). +func (c *IndexConsumer) valueRepr() ValueRepr { + switch c.sqlWriter.(type) { + case *Fulltext2SqlWriter: + return ReprNative + } + return ReprSQLString +} + func (c *IndexConsumer) sinkSnapshot(ctx context.Context, errch chan error, upsertBatch *AtomicBatch) error { var err error for _, bat := range upsertBatch.Batches { for i := 0; i < batchRowCount(bat); i++ { - if err = extractRowFromEveryVector(ctx, bat, i, c.rowdata); err != nil { + if err = extractRowFromEveryVector(ctx, bat, i, c.rowdata, c.valueRepr()); err != nil { return err } @@ -642,7 +673,7 @@ func (c *IndexConsumer) sinkTail(ctx context.Context, errch chan error, upsertBa func (c *IndexConsumer) sinkInsert(ctx context.Context, errch chan error, upsertIter *atomicBatchRowIter) (err error) { // get row from the batch - if err = upsertIter.Row(ctx, c.rowdata); err != nil { + if err = upsertIter.Row(ctx, c.rowdata, c.valueRepr()); err != nil { return err } @@ -675,7 +706,7 @@ func (c *IndexConsumer) sinkInsert(ctx context.Context, errch chan error, upsert func (c *IndexConsumer) sinkDelete(ctx context.Context, errch chan error, deleteIter *atomicBatchRowIter) (err error) { // get row from the batch - if err = deleteIter.Row(ctx, c.rowdelete); err != nil { + if err = deleteIter.Row(ctx, c.rowdelete, c.valueRepr()); err != nil { return err } diff --git a/pkg/iscp/iteration.go b/pkg/iscp/iteration.go index 8a7c11be341fc..671e4876314de 100644 --- a/pkg/iscp/iteration.go +++ b/pkg/iscp/iteration.go @@ -18,6 +18,7 @@ import ( "bytes" "context" "encoding/base64" + "encoding/json" "errors" "fmt" "strings" @@ -1038,6 +1039,11 @@ func ProcessInitSQL( 0) txnOp, err := cnTxnClient.New(ctx, nowTs, createByOpt) if txnOp != nil { + // Commit only when every InitSQL statement succeeded; roll back on any + // error so a multi-statement InitSQL (postings-populate + WAND build) is + // atomic — a mid-sequence failure must not leave the earlier statements + // committed (which an ISCP retry would then re-apply). finishISCPTransaction + // runs that commit/rollback under a detached 5-minute timeout context. defer func() { err = finishISCPTransaction(ctx, txnOp, err) }() @@ -1087,17 +1093,45 @@ func ProcessInitSQL( } sqlctx := sqlexec.NewSqlContext(ctx, cnUUID, txnOp, accountId, resolver) sqlproc := sqlexec.NewSqlProcessWithContext(sqlctx) - result, err := sqlexec.RunSql(sqlproc, sql) - if err != nil { - return + // InitSQL is a JSON array of statements (the multi-statement form), or a JSON + // string / raw single statement (backward-compat). ISCP has no multi-statement + // executor, so run each in sequence within this txn. + for _, stmt := range splitInitSQL(sql) { + if stmt == "" { + continue + } + res, e := sqlexec.RunSql(sqlproc, stmt) + if e != nil { + err = e + return + } + res.Close() } - defer result.Close() if err = ctx.Err(); err != nil { return } return } +// splitInitSQL parses a decoded InitSQL payload into individual statements. The +// canonical form is a JSON array of statements; a JSON string is one statement; +// anything that isn't valid JSON is treated as a single raw statement so +// pre-existing InitSQLs (e.g. "SELECT 1", cagra/ivfpq builds) keep working. +func splitInitSQL(s string) []string { + if s == "" { + return nil + } + var arr []string + if json.Unmarshal([]byte(s), &arr) == nil { + return arr + } + var one string + if json.Unmarshal([]byte(s), &one) == nil { + return []string{one} + } + return []string{s} +} + func finishISCPTransaction(ctx context.Context, txnOp client.TxnOperator, err error) error { if txnOp == nil { return err diff --git a/pkg/iscp/mock_consumer.go b/pkg/iscp/mock_consumer.go index 90a68c6739e00..ea7c22662369d 100644 --- a/pkg/iscp/mock_consumer.go +++ b/pkg/iscp/mock_consumer.go @@ -326,7 +326,7 @@ func (s *interalSqlConsumer) sinkSnapshot(ctx context.Context, bat *AtomicBatch) s.preRowType = UpsertRow } // step1: get row from the batch - if err = extractRowFromEveryVector(ctx, bat, i, s.insertRow); err != nil { + if err = extractRowFromEveryVector(ctx, bat, i, s.insertRow, ReprSQLString); err != nil { return err } @@ -407,7 +407,7 @@ func (s *interalSqlConsumer) sinkInsert(ctx context.Context, insertIter *atomicB } // step1: get row from the batch - if err = insertIter.Row(ctx, s.insertRow); err != nil { + if err = insertIter.Row(ctx, s.insertRow, ReprSQLString); err != nil { return } @@ -431,7 +431,7 @@ func (s *interalSqlConsumer) sinkDelete(ctx context.Context, deleteIter *atomicB } // step1: get row from the batch - if err = deleteIter.Row(ctx, s.deleteRow); err != nil { + if err = deleteIter.Row(ctx, s.deleteRow, ReprSQLString); err != nil { return } diff --git a/pkg/iscp/types.go b/pkg/iscp/types.go index 05547806ff6a1..11f78f8bebc49 100644 --- a/pkg/iscp/types.go +++ b/pkg/iscp/types.go @@ -25,6 +25,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/fileservice" "github.com/matrixorigin/matrixone/pkg/pb/txn" "github.com/matrixorigin/matrixone/pkg/txn/client" "github.com/matrixorigin/matrixone/pkg/vm/engine" @@ -137,6 +138,12 @@ type ISCPTaskExecutor struct { cnUUID string txnEngine engine.Engine cnTxnClient client.TxnClient + // rootFS is the CN's root FileService (the one that resolves the LOCAL SSD + // sub-service). Set by ISCPTaskExecutorFactory before Start(). Index CDC + // consumers that spill to disk (e.g. fulltext2's tail) read it via + // GetExecutorRuntime(cnUUID) to route scratch onto the fast LOCAL mount + // instead of the OS temp dir. May be nil (tests / no LOCAL attached). + rootFS fileservice.FileService iscpLogWm types.TS prevISCPTableID uint64 @@ -289,7 +296,7 @@ const ( type RowIterator interface { Next() bool - Row(ctx context.Context, row []any) error + Row(ctx context.Context, row []any, repr ValueRepr) error Close() } @@ -411,13 +418,14 @@ func (iter *atomicBatchRowIter) Next() bool { return iter.iter.Next() } -func (iter *atomicBatchRowIter) Row(ctx context.Context, row []any) error { +func (iter *atomicBatchRowIter) Row(ctx context.Context, row []any, repr ValueRepr) error { batchRow := iter.iter.Item() return extractRowFromEveryVector( ctx, batchRow.Src, batchRow.Offset, row, + repr, ) } diff --git a/pkg/iscp/util.go b/pkg/iscp/util.go index b6361ee18cdb0..586448da03216 100644 --- a/pkg/iscp/util.go +++ b/pkg/iscp/util.go @@ -46,6 +46,21 @@ import ( "github.com/matrixorigin/matrixone/pkg/vm/engine" ) +// ValueRepr selects how extractRowFromVector renders the types whose native Go +// value and SQL-display string differ (temporal / decimal / uuid). A consumer that +// builds SQL text needs the display string; a consumer that binary-encodes the value +// (the WAND retrieval index) needs the native value so it can serialize it exactly. +type ValueRepr int + +const ( + // ReprSQLString is the historical behavior: datetime/time/timestamp/decimal/uuid + // come out as their SQL-display string (feeds convertColIntoSql). Default. + ReprSQLString ValueRepr = iota + // ReprNative returns those types as their native Go value (types.Datetime, + // types.Decimal128, types.Uuid, ...) so a binary encoder round-trips them exactly. + ReprNative +) + // extractRowFromEveryVector gets the j row from the every vector and outputs the row // bat columns layout: // 1. data: user defined cols | cpk (if needed) | commit-ts @@ -56,6 +71,7 @@ func extractRowFromEveryVector( dataSet *batch.Batch, rowIndex int, row []any, + repr ValueRepr, ) error { for i := 0; i < len(row); i++ { vec := dataSet.Vecs[i] @@ -68,7 +84,7 @@ func extractRowFromEveryVector( rowIndex = 0 } - if err := extractRowFromVector(ctx, vec, i, row, rowIndex); err != nil { + if err := extractRowFromVector(ctx, vec, i, row, rowIndex, repr); err != nil { return err } rowIndex = rowIndexBackup @@ -77,7 +93,7 @@ func extractRowFromEveryVector( } // extractRowFromVector gets the rowIndex row from the i vector -func extractRowFromVector(ctx context.Context, vec *vector.Vector, i int, row []any, rowIndex int) error { +func extractRowFromVector(ctx context.Context, vec *vector.Vector, i int, row []any, rowIndex int, repr ValueRepr) error { if vec.IsConstNull() || vec.GetNulls().Contains(uint64(rowIndex)) { row[i] = nil return nil @@ -135,25 +151,49 @@ func extractRowFromVector(ctx context.Context, vec *vector.Vector, i int, row [] case types.T_date: row[i] = vector.GetFixedAtWithTypeCheck[types.Date](vec, rowIndex) case types.T_datetime: - scale := vec.GetType().Scale - row[i] = vector.GetFixedAtWithTypeCheck[types.Datetime](vec, rowIndex).String2(scale) + if repr == ReprNative { + row[i] = vector.GetFixedAtWithTypeCheck[types.Datetime](vec, rowIndex) + } else { + scale := vec.GetType().Scale + row[i] = vector.GetFixedAtWithTypeCheck[types.Datetime](vec, rowIndex).String2(scale) + } case types.T_time: - scale := vec.GetType().Scale - row[i] = vector.GetFixedAtWithTypeCheck[types.Time](vec, rowIndex).String2(scale) + if repr == ReprNative { + row[i] = vector.GetFixedAtWithTypeCheck[types.Time](vec, rowIndex) + } else { + scale := vec.GetType().Scale + row[i] = vector.GetFixedAtWithTypeCheck[types.Time](vec, rowIndex).String2(scale) + } case types.T_timestamp: - scale := vec.GetType().Scale - //TODO:get the right timezone - //timeZone := ses.GetTimeZone() - timeZone := time.UTC - row[i] = vector.GetFixedAtWithTypeCheck[types.Timestamp](vec, rowIndex).String2(timeZone, scale) + if repr == ReprNative { + row[i] = vector.GetFixedAtWithTypeCheck[types.Timestamp](vec, rowIndex) + } else { + scale := vec.GetType().Scale + //TODO:get the right timezone + //timeZone := ses.GetTimeZone() + timeZone := time.UTC + row[i] = vector.GetFixedAtWithTypeCheck[types.Timestamp](vec, rowIndex).String2(timeZone, scale) + } case types.T_decimal64: - scale := vec.GetType().Scale - row[i] = vector.GetFixedAtWithTypeCheck[types.Decimal64](vec, rowIndex).Format(scale) + if repr == ReprNative { + row[i] = vector.GetFixedAtWithTypeCheck[types.Decimal64](vec, rowIndex) + } else { + scale := vec.GetType().Scale + row[i] = vector.GetFixedAtWithTypeCheck[types.Decimal64](vec, rowIndex).Format(scale) + } case types.T_decimal128: - scale := vec.GetType().Scale - row[i] = vector.GetFixedAtWithTypeCheck[types.Decimal128](vec, rowIndex).Format(scale) + if repr == ReprNative { + row[i] = vector.GetFixedAtWithTypeCheck[types.Decimal128](vec, rowIndex) + } else { + scale := vec.GetType().Scale + row[i] = vector.GetFixedAtWithTypeCheck[types.Decimal128](vec, rowIndex).Format(scale) + } case types.T_uuid: - row[i] = vector.GetFixedAtWithTypeCheck[types.Uuid](vec, rowIndex).String() + if repr == ReprNative { + row[i] = vector.GetFixedAtWithTypeCheck[types.Uuid](vec, rowIndex) + } else { + row[i] = vector.GetFixedAtWithTypeCheck[types.Uuid](vec, rowIndex).String() + } case types.T_Rowid: row[i] = vector.GetFixedAtWithTypeCheck[types.Rowid](vec, rowIndex) case types.T_Blockid: diff --git a/pkg/monlp/tokenizer/jieba.go b/pkg/monlp/tokenizer/jieba.go index c36bbe5a4ce19..9f4d3724b09e4 100644 --- a/pkg/monlp/tokenizer/jieba.go +++ b/pkg/monlp/tokenizer/jieba.go @@ -69,10 +69,14 @@ func newJiebaChecked(paths [5]string) (*gojieba.Jieba, error) { // - true at query time: HMM new-word discovery broadens recall for terms // not in the dictionary. func SharedJiebaTokenizer(useHmm bool) (*JiebaTokenizer, error) { - paths := jiebaDictPaths() + // jiebaDictPaths() resolves the dictionary directory via filepath.EvalSymlinks + + // os.Stat syscalls; it is ONLY needed to construct the singleton, so it must run + // inside the Once. Computing it before the Once made every call — one per document + // at index-build time (DocTokenizer) and one per query — re-walk the filesystem to + // resolve a constant path, which profiled at ~23% of a large CREATE FULLTEXT2 build. if useHmm { sharedJiebaHmmOnce.Do(func() { - j, err := newJiebaChecked(paths) + j, err := newJiebaChecked(jiebaDictPaths()) if err != nil { sharedJiebaHmmErr = err return @@ -86,7 +90,7 @@ func SharedJiebaTokenizer(useHmm bool) (*JiebaTokenizer, error) { return sharedJiebaHmm, sharedJiebaHmmErr } sharedJiebaNoHmmOnce.Do(func() { - j, err := newJiebaChecked(paths) + j, err := newJiebaChecked(jiebaDictPaths()) if err != nil { sharedJiebaNoHmmErr = err return diff --git a/pkg/monlp/tokenizer/jieba_dict.go b/pkg/monlp/tokenizer/jieba_dict.go index dcdde13bc0368..ff78a0af3e26b 100644 --- a/pkg/monlp/tokenizer/jieba_dict.go +++ b/pkg/monlp/tokenizer/jieba_dict.go @@ -50,6 +50,12 @@ func jiebaDictPaths() [5]string { } } +// JiebaDictDir returns the resolved directory holding jieba.dict.utf8 (respecting +// MO_JIEBA_DICT_DIR and the build/vendored fallbacks), or "" if none is found. +// Exposed so callers that read the raw dictionary (e.g. building an FST from the +// full vocabulary) locate it the same way the tokenizer does. +func JiebaDictDir() string { return resolveJiebaDictDir() } + func dictDirOK(d string) bool { if d == "" { return false diff --git a/pkg/monlp/tokenizer/word_id.go b/pkg/monlp/tokenizer/word_id.go new file mode 100644 index 0000000000000..f6838e28b61df --- /dev/null +++ b/pkg/monlp/tokenizer/word_id.go @@ -0,0 +1,104 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tokenizer + +import ( + "bufio" + "os" + "strings" + "sync" +) + +// Word-id support for the WAND retrieval index. +// +// jieba.dict.utf8 is a fixed list of ~349k dictionary words ("word freq POS" +// per line). Each word's line index is a stable, process-wide word id, shared +// by every index — so dictionary words need no per-index term storage. Tokens +// NOT in the dictionary (English words, numbers, user-dict / HMM-discovered +// tokens) return ok=false; the caller assigns them per-index overflow ids in a +// range above DictWordIDLimit. + +// DictWordIDLimit is the exclusive upper bound of global (dictionary) word ids. +// Out-of-dictionary overflow ids must be assigned at or above this value so the +// two id spaces never collide, regardless of the dictionary's exact size. +const DictWordIDLimit = int32(1) << 24 // 16,777,216 (dict has ~349k entries) + +var ( + wordIDOnce sync.Once + wordIDMap map[string]int32 + wordIDErr error +) + +func loadWordIDMap() { + path := jiebaDictPaths()[0] // jieba.dict.utf8 + f, err := os.Open(path) + if err != nil { + wordIDErr = err + return + } + defer f.Close() + + m := make(map[string]int32, 400000) + var id int32 + sc := bufio.NewScanner(f) + sc.Buffer(make([]byte, 0, 64*1024), 1024*1024) + for sc.Scan() { + line := sc.Text() + if line == "" { + id++ + continue + } + // "word freq POS" — the word is the first space-separated field. + word := line + if sp := strings.IndexByte(line, ' '); sp >= 0 { + word = line[:sp] + } + // First occurrence wins (the line index is the stable id); the dict has + // a few case variants but no exact-duplicate words. + if _, exists := m[word]; !exists { + m[word] = id + } + id++ + } + if err := sc.Err(); err != nil { + wordIDErr = err + return + } + if id >= DictWordIDLimit { + // Defensive: the dict grew past the reserved global id space. + wordIDErr = errDictTooLarge + return + } + wordIDMap = m +} + +var errDictTooLarge = &dictError{"jieba dictionary exceeds DictWordIDLimit"} + +type dictError struct{ msg string } + +func (e *dictError) Error() string { return e.msg } + +// WordID returns the global word id of a jieba-dictionary word (its line index +// in jieba.dict.utf8). ok is false for out-of-dictionary tokens, which the +// caller maps to per-index overflow ids (>= DictWordIDLimit). The dictionary is +// loaded once on first call and shared process-wide. +func WordID(word string) (id int32, ok bool, err error) { + wordIDOnce.Do(loadWordIDMap) + if wordIDErr != nil { + return 0, false, wordIDErr + } + id, ok = wordIDMap[word] + return id, ok, nil +} diff --git a/pkg/sql/colexec/index_metadata.go b/pkg/sql/colexec/index_metadata.go index fb3b7c29cc9df..3746bcec174b7 100644 --- a/pkg/sql/colexec/index_metadata.go +++ b/pkg/sql/colexec/index_metadata.go @@ -93,7 +93,7 @@ func indexMetadataType(unique bool, algo string) string { return INDEX_TYPE_UNIQUE case catalog.IsRTreeIndexAlgo(algo): return INDEX_TYPE_SPATIAL - case catalog.IsFullTextIndexAlgo(algo): + case catalog.IsFullTextIndexAlgo(algo) || catalog.IsFullText2IndexAlgo(algo): return INDEX_TYPE_FULLTEXT default: return INDEX_TYPE_MULTIPLE diff --git a/pkg/sql/colexec/table_function/fulltext2_compact.go b/pkg/sql/colexec/table_function/fulltext2_compact.go new file mode 100644 index 0000000000000..ceb9e771df735 --- /dev/null +++ b/pkg/sql/colexec/table_function/fulltext2_compact.go @@ -0,0 +1,143 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// fulltext2_compact is the standalone MERGE-compaction TVF (mirrors bm25_compact): +// SELECT * FROM fulltext2_compact(db, store, meta, capacity). It folds the tag=1 +// CdcTail into the tag=0 base and drops dead docs in one txn, driving +// fulltext2.CompactSegments. No input rows; its own output is discarded. +package table_function + +import ( + "strconv" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/fulltext2" + "github.com/matrixorigin/matrixone/pkg/sql/colexec" + veccache "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" + "github.com/matrixorigin/matrixone/pkg/vm" + "github.com/matrixorigin/matrixone/pkg/vm/process" +) + +type fulltext2CompactState struct { + inited bool + tblcfg fulltext2.TableConfig + capacity int64 + postingCap int64 + batch *batch.Batch +} + +func (u *fulltext2CompactState) reset(tf *TableFunction, proc *process.Process) { + if u.batch != nil { + u.batch.CleanOnlyData() + } +} + +func (u *fulltext2CompactState) call(tf *TableFunction, proc *process.Process) (vm.CallResult, error) { + if u.batch != nil { + u.batch.CleanOnlyData() + } + return vm.CancelResult, nil +} + +func (u *fulltext2CompactState) free(tf *TableFunction, proc *process.Process, pipelineFailed bool, err error) { + if u.batch != nil { + u.batch.Clean(proc.Mp()) + } +} + +func (u *fulltext2CompactState) start(tf *TableFunction, proc *process.Process, nthRow int, analyzer process.Analyzer) error { + if u.inited { + return nil + } + // Validate EVERY provided arg is a string const — including the optional + // position_free (5th) and posting_capacity (6th) — so a user-issued direct call + // with a non-varchar optional arg errors cleanly instead of UnsafeGetStringAt + // reinterpreting a fixed-width vector as a Varlena (panic/garbage). + for i := 0; i < len(tf.ctr.argVecs); i++ { + v := tf.ctr.argVecs[i] + if v.GetType().Oid != types.T_varchar { + return moerr.NewInvalidInput(proc.Ctx, "fulltext2_compact: args (db, store, meta, capacity[, position_free[, posting_capacity]]) must be strings") + } + if !v.IsConst() { + return moerr.NewInternalError(proc.Ctx, "fulltext2_compact: args must be string constants") + } + } + u.tblcfg = fulltext2.TableConfig{ + DbName: tf.ctr.argVecs[0].UnsafeGetStringAt(0), + IndexTable: tf.ctr.argVecs[1].UnsafeGetStringAt(0), + MetadataTable: tf.ctr.argVecs[2].UnsafeGetStringAt(0), + } + if u.tblcfg.DbName == "" || u.tblcfg.IndexTable == "" || u.tblcfg.MetadataTable == "" { + return moerr.NewInternalError(proc.Ctx, "fulltext2_compact: db/store/meta must be non-empty") + } + c, err := strconv.ParseInt(tf.ctr.argVecs[3].UnsafeGetStringAt(0), 10, 64) + if err != nil { + return moerr.NewInvalidInput(proc.Ctx, "fulltext2_compact: capacity must be an integer") + } + u.capacity = c + // Optional 5th arg: position_free ("true"/"false"). Absent ⇒ positional, so a + // position-free index's MERGE rebuilds position-free (no re-added positions). + if len(tf.ctr.argVecs) > 4 { + if pv := tf.ctr.argVecs[4]; pv != nil && pv.IsConst() && pv.Length() > 0 { + u.tblcfg.PositionFree = pv.UnsafeGetStringAt(0) == "true" + } + } + // Optional 6th arg: posting_capacity (max_postings_capacity). Absent ⇒ 0, floored + // to DefaultPostingCapacity in CompactSegments so the fresh base stays memory-bounded. + if len(tf.ctr.argVecs) > 5 { + if cv := tf.ctr.argVecs[5]; cv != nil && cv.IsConst() && cv.Length() > 0 { + if pc, perr := strconv.ParseInt(cv.UnsafeGetStringAt(0), 10, 64); perr == nil { + u.postingCap = pc + } + } + } + u.batch = tf.createResultBatch() + u.inited = true + return nil +} + +// end runs the compaction after the (empty) input is consumed — one txn folds the +// tail into the base and drops dead docs. +func (u *fulltext2CompactState) end(tf *TableFunction, proc *process.Process) error { + if !u.inited { + return nil + } + sqlproc := sqlexec.NewSqlProcess(proc) + if _, err := fulltext2.CompactSegments(sqlproc, u.tblcfg, u.capacity, u.postingCap); err != nil { + return err + } + // The tag=0 base changed (tail folded into a fresh merged base) — evict any cached + // search index so the next query reloads the merged base instead of the stale one + // held until the TTL. + veccache.Cache.Remove(u.tblcfg.IndexTable) + return nil +} + +func fulltext2CompactPrepare(proc *process.Process, arg *TableFunction) (tvfState, error) { + // 5th arg (position_free) and 6th arg (posting_capacity) are optional so a + // position-free index's MERGE rebuilds position-free and older 4/5-arg callers + // stay valid (positional, default posting cap). + if len(arg.Args) < 4 || len(arg.Args) > 6 { + return nil, moerr.NewInvalidInput(proc.Ctx, "fulltext2_compact: expects 4 to 6 args (db, store, meta, capacity[, position_free[, posting_capacity]])") + } + var err error + st := &fulltext2CompactState{} + arg.ctr.executorsForArgs, err = colexec.NewExpressionExecutorsFromPlanExpressions(proc, arg.Args) + arg.ctr.argVecs = make([]*vector.Vector, len(arg.Args)) + return st, err +} diff --git a/pkg/sql/colexec/table_function/fulltext2_create.go b/pkg/sql/colexec/table_function/fulltext2_create.go new file mode 100644 index 0000000000000..7b360fcde9fb4 --- /dev/null +++ b/pkg/sql/colexec/table_function/fulltext2_create.go @@ -0,0 +1,364 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// fulltext2_create is the execution-side build TVF for the fulltext2 index (the +// positional analogue of bm25_create). CROSS APPLY'd over the source table by the +// compile layer, it tokenizes each row in execution — resolving datalink columns +// (fetch + PDF/DOCX plain text) and applying the ngram/gojieba/json parser — Add's +// the ordered terms to a fulltext2.Builder, and at end() splits the corpus into +// capacity-bounded tag=0 base segments and persists their chunk rows. Its own +// output is a single discarded status row (mirrors bm25_create / hnsw_create). +package table_function + +import ( + "bytes" + "fmt" + "time" + + "github.com/bytedance/sonic" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/datalink" + "github.com/matrixorigin/matrixone/pkg/fulltext2" + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" + "github.com/matrixorigin/matrixone/pkg/sql/colexec" + veccache "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" + "github.com/matrixorigin/matrixone/pkg/vm" + "github.com/matrixorigin/matrixone/pkg/vm/process" +) + +var fulltext2_runSql = sqlexec.RunSql + +// fulltext2CreateState builds a positional fulltext2 index from the source rows. +// argVecs: [0]=cfg(JSON const), [1]=pk, [2..]=indexed text columns. +type fulltext2CreateState struct { + inited bool + tblcfg fulltext2.TableConfig + // Streaming base build: only ONE open segment is held in memory. It is sealed + + // persisted (and its postings freed) the moment it reaches `capacity` docs, so + // peak build memory is one capacity-bounded segment rather than the whole corpus. + cur *fulltext2.Builder // current open segment (nil after a seal, recreated on the next row) + capacity int64 // docs per sealed segment (floored to DefaultBuildCapacity) + postingCap int64 // postings per sealed segment (floored to DefaultPostingCapacity) + pkType int32 + bopts []fulltext2.BuildOpt + uid string // per-build-unique sub-index id prefix (IndexTable:ts) + ts int64 + segIdx int // next sub-index id == count of segments sealed so far + basesCleared bool // DeleteAllBases run once, before the first sealed segment + batch *batch.Batch +} + +func (u *fulltext2CreateState) reset(tf *TableFunction, proc *process.Process) { + if u.batch != nil { + u.batch.CleanOnlyData() + } +} + +func (u *fulltext2CreateState) call(tf *TableFunction, proc *process.Process) (vm.CallResult, error) { + u.batch.CleanOnlyData() + if u.batch.RowCount() == 0 { + return vm.CancelResult, nil + } + return vm.CallResult{Status: vm.ExecNext, Batch: u.batch}, nil +} + +func (u *fulltext2CreateState) free(tf *TableFunction, proc *process.Process, pipelineFailed bool, err error) { + if u.batch != nil { + u.batch.Clean(proc.Mp()) + } +} + +func fulltext2CreatePrepare(proc *process.Process, arg *TableFunction) (tvfState, error) { + var err error + st := &fulltext2CreateState{} + arg.ctr.executorsForArgs, err = colexec.NewExpressionExecutorsFromPlanExpressions(proc, arg.Args) + arg.ctr.argVecs = make([]*vector.Vector, len(arg.Args)) + return st, err +} + +// start tokenizes source row nthRow and Add's its ordered terms to the builder. +func (u *fulltext2CreateState) start(tf *TableFunction, proc *process.Process, nthRow int, analyzer process.Analyzer) (err error) { + if !u.inited { + cfgVec := tf.ctr.argVecs[0] + if cfgVec.GetType().Oid != types.T_varchar { + return moerr.NewInvalidInput(proc.Ctx, "fulltext2_create: first argument (config) must be a string") + } + if !cfgVec.IsConst() { + return moerr.NewInternalError(proc.Ctx, "fulltext2_create: config must be a string constant") + } + cfgstr := cfgVec.UnsafeGetStringAt(0) + if len(cfgstr) == 0 { + return moerr.NewInternalError(proc.Ctx, "fulltext2_create: config is empty") + } + if err = sonic.Unmarshal([]byte(cfgstr), &u.tblcfg); err != nil { + return err + } + pkVec := tf.ctr.argVecs[1] + u.pkType = int32(pkVec.GetType().Oid) + if u.tblcfg.PositionFree { + u.bopts = append(u.bopts, fulltext2.WithPositionFree()) + } + if len(u.tblcfg.IncludeTypes) > 0 { + u.bopts = append(u.bopts, fulltext2.WithIncludeTypes(u.tblcfg.IncludeTypes)) + } + // Floor both caps so a segment is sealed+spilled every ~capacity docs OR + // ~postingCap postings even when the WITH options are unset, keeping build + // memory bounded regardless of doc size (a long-doc corpus seals on postings). + u.capacity = u.tblcfg.Capacity + if u.capacity <= 0 { + u.capacity = fulltext2.DefaultBuildCapacity + } + u.postingCap = u.tblcfg.PostingCapacity + if u.postingCap <= 0 { + u.postingCap = fulltext2.DefaultPostingCapacity + } + // A per-build-unique id prefix (index table + build ts) keeps concurrent / + // repeated builds from colliding on sub-index ids (mirrors bm25 / HNSW). + u.ts = time.Now().UnixMicro() + u.uid = fmt.Sprintf("%s:%d", u.tblcfg.IndexTable, u.ts) + u.cur = fulltext2.NewBuilder(u.uid, u.pkType, u.bopts...) + u.batch = tf.createResultBatch() + u.inited = true + } + + u.batch.CleanOnlyData() + + pkVec := tf.ctr.argVecs[1] + if pkVec.IsNull(uint64(nthRow)) { + return nil + } + terms, err := u.rowTerms(tf, proc, nthRow) + if err != nil { + return err + } + if len(terms) == 0 { + return nil + } + // Feed this doc's tokens contiguously and in order, each with its BYTE position + // (positional phrase queries match by byte offset), mirroring the base build. + pk := vector.GetAny(pkVec, nthRow, false) + for _, w := range terms { + if aerr := u.cur.Add(w.Word, w.Pos, pk); aerr != nil { + return aerr + } + } + // Attach this row's INCLUDE column values (actual values → segment docmap for + // prefilter + coverage). Only for docs the Add loop created, i.e. non-empty text + // (a NULL/empty-text row is skipped above), consistent with the CDC/build empty-doc skip. + if len(u.tblcfg.IncludeTypes) > 0 { + u.cur.SetInclude(pk, u.rowInclude(tf, nthRow)) + } + // A document's tokens are fed contiguously, so once the open segment reaches + // capacity DISTINCT docs (or its posting cap) the current doc is complete: seal + + // persist it as a tag=0 base and start a fresh segment, freeing the sealed postings. + if fulltext2.ReachedSegmentCap(u.cur, u.capacity, u.postingCap) { + if err := u.sealSegment(proc); err != nil { + return err + } + u.cur = fulltext2.NewBuilder(u.uid, u.pkType, u.bopts...) + } + return nil +} + +// sealSegment finalizes the open builder into one tag=0 base sub-index and persists +// it, then drops it so its postings are freed. Prior bases are cleared once, before +// the first sealed segment, so a REBUILD reusing this TVF stays idempotent (the tag=1 +// tail is untouched). A no-op when the open segment holds no docs. +func (u *fulltext2CreateState) sealSegment(proc *process.Process) (err error) { + if u.cur == nil || u.cur.NumDocs() == 0 { + u.cur = nil + return nil + } + seg, err := u.cur.Finish() + u.cur = nil + if err != nil { + return err + } + sqlproc := sqlexec.NewSqlProcess(proc) + if !u.basesCleared { + for _, s := range fulltext2.DeleteAllBasesSqls(u.tblcfg) { + res, e := fulltext2_runSql(sqlproc, s) + if e != nil { + return e + } + res.Close() + } + u.basesCleared = true + } + seg.Id = fulltext2.SubIndexId(u.uid, u.segIdx) + u.segIdx++ + sqls, cleanup, err := seg.ToInsertSqls(sqlproc, u.tblcfg, u.ts, 0 /* tag=0 base */) + if err != nil { + return err + } + defer cleanup() // remove the spill file once its INSERTs have run — bounds temp disk too + for _, s := range sqls { + res, e := fulltext2_runSql(sqlproc, s) + if e != nil { + return e + } + res.Close() + } + return nil +} + +// rowInclude reads row nthRow's INCLUDE column values (the trailing argVecs after the text +// columns) in column order. A NULL value stays nil. A varchar value is a view into the +// batch vector, which is recycled across batches while the builder accumulates — so copy +// []byte out; scalar types are returned by value and need no copy. +func (u *fulltext2CreateState) rowInclude(tf *TableFunction, nthRow int) []any { + n := len(u.tblcfg.IncludeTypes) + if n == 0 { + return nil + } + argVecs := tf.ctr.argVecs + start := len(argVecs) - n + out := make([]any, n) + for j := 0; j < n; j++ { + v := argVecs[start+j] + if v.IsNull(uint64(nthRow)) { + continue // NULL + } + val := vector.GetAny(v, nthRow, false) + if b, ok := val.([]byte); ok { + val = append([]byte(nil), b...) // copy: the batch vector is recycled across batches + } + out[j] = val + } + return out +} + +// rowTerms tokenizes source row nthRow (columns argVecs[2..]) into ordered terms, +// applying the index's parser. datalink columns are resolved to plain text and +// json columns to their flattened values; a NULL column yields no tokens (matches +// the classic tokenizer's per-row NULL handling). +func (u *fulltext2CreateState) rowTerms(tf *TableFunction, proc *process.Process, nthRow int) ([]fulltext2.WordPos, error) { + argVecs := tf.ctr.argVecs + // Text columns are argVecs[2 : textEnd); the trailing len(IncludeTypes) args are INCLUDE + // columns (their values are stored verbatim, NOT tokenized). A NULL in any TEXT column + // yields no tokens (empty doc); a NULL INCLUDE column is fine (stored as NULL). + textEnd := len(argVecs) - len(u.tblcfg.IncludeTypes) + for i := 2; i < textEnd; i++ { + if argVecs[i].IsNull(uint64(nthRow)) { + return nil, nil + } + } + + jsonValue := fulltext2.IsJSONValueParser(u.tblcfg.Parser) + var content bytes.Buffer + if fulltext2.IsJSONParser(u.tblcfg.Parser) { + for i := 2; i < textEnd; i++ { + binary := argVecs[i].GetType().Oid == types.T_json + var raw []byte + if binary { + raw = argVecs[i].GetRawBytesAt(nthRow) + } else { + raw = []byte(argVecs[i].GetStringAt(nthRow)) + } + // json: ngram over space-joined values; json_value: each value is one whole + // atomic token, so keep them '\n'-separated for jsonValueTokenize below. + var ft []byte + var err error + if jsonValue { + ft, err = fulltext2.FlattenJSONLines(raw, binary) + } else { + ft, err = fulltext2.FlattenJSON(raw, binary) + } + if err != nil { + return nil, err + } + if content.Len() > 0 { + content.WriteByte('\n') + } + content.Write(ft) + } + } else { + for i := 2; i < textEnd; i++ { + if content.Len() > 0 { + content.WriteByte('\n') + } + data := argVecs[i].GetStringAt(nthRow) + if types.T(tf.Args[i].Typ.Id) == types.T_datalink { + dl, err := datalink.NewDatalink(data, proc) + if err != nil { + return nil, err + } + b, err := dl.GetPlainText(proc) + if err != nil { + return nil, err + } + content.Write(b) + } else { + content.WriteString(data) + } + } + } + if content.Len() == 0 { + return nil, nil + } + + // json_value indexes each whole '\n'-separated value as one atomic token (no ngram). + if jsonValue { + return fulltext2.JSONValueTokenize(content.Bytes()), nil + } + + // json is indexed as ngram over its flattened values (SimpleTokenizer). + var tok tokenizer.Tokenizer + if fulltext2.IsJSONParser(u.tblcfg.Parser) { + tok = tokenizer.NewSimpleTokenizer() + } else { + t, err := fulltext2.DocTokenizer(u.tblcfg.Parser) + if err != nil { + return nil, err + } + tok = t + } + + // token count is unknown up front; estimate from input length to avoid regrowth. + terms := make([]fulltext2.WordPos, 0, content.Len()/4) + for t, terr := range tok.Tokenize(content.Bytes()) { + if terr != nil { + return nil, terr + } + slen := t.TokenBytes[0] + terms = append(terms, fulltext2.WordPos{Word: string(t.TokenBytes[1 : slen+1]), Pos: t.BytePos}) + } + return terms, nil +} + +// end seals the final open segment (the trailing docs below capacity). Base +// clearing and per-segment persistence already happened incrementally as segments +// filled during the feed, so peak memory never exceeded one open segment. +func (u *fulltext2CreateState) end(tf *TableFunction, proc *process.Process) error { + if !u.inited { + return nil + } + if err := u.sealSegment(proc); err != nil { + return err + } + // Nothing was persisted (empty corpus): leave existing tag=0 bases untouched, + // mirroring the prior zero-doc no-op. + if u.segIdx == 0 { + return nil + } + // A fresh tag=0 was written (CREATE build, or a REBUILD reusing this TVF) — evict + // any cached search index so the next query reloads the new base(s) instead of the + // stale one held until the TTL. Local to this CN's cache. + veccache.Cache.Remove(u.tblcfg.IndexTable) + return nil +} diff --git a/pkg/sql/colexec/table_function/fulltext2_search.go b/pkg/sql/colexec/table_function/fulltext2_search.go new file mode 100644 index 0000000000000..1342f29671640 --- /dev/null +++ b/pkg/sql/colexec/table_function/fulltext2_search.go @@ -0,0 +1,455 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package table_function + +import ( + "context" + "fmt" + + "github.com/bytedance/sonic" + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/types" + "strings" + + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/fulltext2" + "github.com/matrixorigin/matrixone/pkg/sql/colexec" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + veccache "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" + "github.com/matrixorigin/matrixone/pkg/vm" + "github.com/matrixorigin/matrixone/pkg/vm/process" +) + +// fulltext2SearchState answers a MATCH over a fulltext2 index: it loads the +// index's segments (base + CDC tail) once via the shared VectorIndexCache and reuses +// them across queries (evicted on CDC append / compaction / rebuild), runs the WAND +// positional query, and emits (doc_id, score) rows; the top-k is bounded by the +// pushed LIMIT, and a pushed-down WHERE prefilter is applied inside the walk. +type fulltext2SearchState struct { + inited bool + tblcfg fulltext2.TableConfig + limit uint64 + offset int + filterBytes []byte // serialized docfilter membership (WHERE-clause prefilter), if any + batch *batch.Batch + + // LIMIT (non-streaming) path: SearchInto fills this caller-owned, box-free result — pk + // (out.Keys), scores (out.Dists), covered INCLUDE cols (out.Include). Held on the state + // and Reset per query by SearchInto, so a warm query allocates nothing for its results + // (the alloc-free twin of the old keys []any / distances []float64). call() pages it via + // u.offset with AppendColumnBufferRange. + out *vectorindex.SearchOutput + + // Streaming no-LIMIT path (u.limit == 0): rather than materialize every matching + // doc, a producer goroutine runs the search with an Emit callback that hands bounded + // batches (box-free *vectorindex.SearchOutput, the SAME shape SearchInto fills) to + // streamCh; call() drains one batch per invocation and the upstream ORDER BY score + // node ranks them. cancel stops the producer (and releases the cache read lock it + // holds) if the consumer aborts early. Mirrors bm25_search. + streaming bool + streamCh chan *vectorindex.SearchOutput + errCh chan error + cancel context.CancelFunc + done bool + + // Covered fast path (Phase 6): the TVF outputs pk/score/INCLUDE columns straight from + // the index (no base-table JOIN). Column pruning can drop ANY unreferenced output + // column — doc_id when the pk isn't projected, score when the ORDER BY is on another + // column, an unprojected include col — and COMPACT the batch, so the runtime must NOT + // assume fixed positions. It maps each SURVIVING result vector by its coldef NAME + // (u.batch.Attrs): "doc_id" <- pk, "score" <- score, everything else <- the include + // column of that name. pkVecIdx/scoreVecIdx are the batch vector indices for doc_id / + // score (-1 when pruned); includeOut lists the surviving include output vectors. + // includeNames (== includeOut names) drives rt.RequestedIncludeColumns. The non-streaming + // (LIMIT) include values arrive box-free in u.out.Include (one ColumnBuffer per FULL index + // include column, segment order, indexed by includeOut.segPos) — the streaming path uses + // ft2StreamBatch.includes; both share one consumer. + pkVecIdx int + scoreVecIdx int + includeOut []ft2IncludeOut + includeNames []string +} + +// ft2IncludeOut maps one surviving INCLUDE output vector to its source: vecIdx is the +// position in u.batch.Vecs; name is the column name (non-streaming IncludeResult lookup); +// segPos is the column's position in the FULL index include list (= segment / decodeInclude +// order), used to pull the value from the streaming path's segment-ordered include slice. +type ft2IncludeOut struct { + vecIdx int + segPos int + name string +} + +func (u *fulltext2SearchState) end(tf *TableFunction, proc *process.Process) error { return nil } + +func (u *fulltext2SearchState) reset(tf *TableFunction, proc *process.Process) { + u.stopStream() + if u.batch != nil { + u.batch.CleanOnlyData() + } + u.offset = 0 + u.filterBytes = nil + u.streaming = false + u.errCh = nil + u.done = false + // u.out is kept and REUSED across queries (SearchInto Resets its buffers per query) so + // the LIMIT path is alloc-free after warmup; includeColumns is stable (cfg-derived). +} + +// stopStream cancels the producer goroutine (if streaming) and drains streamCh until +// the producer closes it, so no goroutine — nor the cache read-lock it holds — leaks +// past this query. Idempotent; a no-op when not streaming. +func (u *fulltext2SearchState) stopStream() { + if u.cancel == nil { + return + } + u.cancel() + if u.streamCh != nil { + for b := range u.streamCh { // drain to the producer's close() + fulltext2.PutColumnBuffer(b.Keys) // recycle drained (unconsumed) batches + } + } + u.cancel = nil + u.streamCh = nil +} + +func (u *fulltext2SearchState) free(tf *TableFunction, proc *process.Process, pipelineFailed bool, err error) { + u.stopStream() + if u.batch != nil { + u.batch.Clean(proc.Mp()) + } +} + +func (u *fulltext2SearchState) call(tf *TableFunction, proc *process.Process) (vm.CallResult, error) { + u.batch.CleanOnlyData() + + if u.streaming { + if u.done { + return vm.CancelResult, nil + } + select { + case b, ok := <-u.streamCh: + if !ok { + // producer finished; surface any search error (sent before close). + u.done = true + u.cancel = nil + if e := <-u.errCh; e != nil { + return vm.CancelResult, e + } + return vm.CancelResult, nil + } + // b ownership (its pooled Keys buffer) was received from the producer; recycle it + // on EVERY exit from here (incl. the append error paths in appendOutputRange), else + // a mid-stream mpool failure leaks the pooled buffer. Safe to defer: the append + // copies into u.batch, so u.batch never aliases b.Keys. + defer fulltext2.PutColumnBuffer(b.Keys) + // Write the whole emitted batch (rows [0, N)) — pk / score / each include col, + // name-driven — via the SAME consumer the LIMIT path uses. + if err := u.appendOutputRange(b, 0, b.Keys.N, proc); err != nil { + return vm.CancelResult, err + } + u.batch.SetRowCount(b.Keys.N) + return vm.CallResult{Status: vm.ExecNext, Batch: u.batch}, nil + case <-proc.Ctx.Done(): + return vm.CancelResult, proc.Ctx.Err() + } + } + + // start() bailed before running SearchInto (e.g. a NULL/empty pattern) — u.out is nil, or + // (on a reused operator) was emptied in start()'s reset. Either way no results this row → + // signal end of stream. (Before the SearchOutput migration this was a nil u.keys slice.) + if u.out == nil || u.out.Keys == nil { + return vm.CancelResult, nil + } + + // LIMIT (non-streaming) path: page the box-free SearchInto result (u.out) into this + // batch. pk / score / each include col are bulk-appended via AppendColumnBufferRange over + // the [start, start+n) rows — no per-row boxing, no reflection append; identical shape to + // the streaming consumer above. + nkeys := u.out.Keys.N + start := u.offset + n := nkeys - start + if n > 8192 { + n = 8192 + } + if n < 0 { + n = 0 + } + // Page rows [start, start+n) of the box-free SearchInto result (u.out) into this batch + // via the SAME consumer the streaming path uses. + if err := u.appendOutputRange(u.out, start, n, proc); err != nil { + return vm.CancelResult, err + } + u.offset += n + u.batch.SetRowCount(n) + if u.batch.RowCount() == 0 { + return vm.CancelResult, nil + } + return vm.CallResult{Status: vm.ExecNext, Batch: u.batch}, nil +} + +// appendOutputRange writes rows [start, start+n) of a box-free SearchOutput (pk / score / +// covered INCLUDE cols) into u.batch, name-driven so column pruning can't misalign the +// columns. It is the ONE consumer shared by both result paths: the streaming path passes the +// per-batch emitted output with start=0, n=Keys.N; the LIMIT path pages the whole-result output +// by u.offset. pk and each include col bulk-append via AppendColumnBufferRange (no per-row +// boxing); score narrows float32 per row. Any mpool failure surfaces as the error so the caller +// never SetRowCounts a batch whose vectors disagree in length. A segPos out of range (should not +// happen on the covered path) fills SQL NULLs to keep the batch column-aligned. +func (u *fulltext2SearchState) appendOutputRange(out *vectorindex.SearchOutput, start, n int, proc *process.Process) error { + mp := proc.Mp() + if u.pkVecIdx >= 0 { + if err := vectorindex.AppendColumnBufferRange(out.Keys, u.batch.Vecs[u.pkVecIdx], start, n, mp); err != nil { + return err + } + } + if u.scoreVecIdx >= 0 { + vec := u.batch.Vecs[u.scoreVecIdx] + for i := start; i < start+n; i++ { + if err := vector.AppendFixed[float32](vec, out.Dists[i], false, mp); err != nil { + return err + } + } + } + for _, ic := range u.includeOut { + vec := u.batch.Vecs[ic.vecIdx] + if ic.segPos >= 0 && ic.segPos < len(out.Include) { + if err := vectorindex.AppendColumnBufferRange(out.Include[ic.segPos], vec, start, n, mp); err != nil { + return err + } + } else { + for j := 0; j < n; j++ { + if err := vector.AppendAny(vec, nil, true, mp); err != nil { + return err + } + } + } + } + return nil +} + +func fulltext2SearchPrepare(proc *process.Process, arg *TableFunction) (tvfState, error) { + var err error + st := &fulltext2SearchState{} + arg.ctr.executorsForArgs, err = colexec.NewExpressionExecutorsFromPlanExpressions(proc, arg.Args) + if err != nil { + return nil, err + } + arg.ctr.argVecs = make([]*vector.Vector, len(arg.Args)) + // Resolve the pushed-down LIMIT. Prepare runs at EXECUTE time (after '?' parameters are + // bound), so evalLimitExpression evaluates a literal OR a prepared `LIMIT ?` parameter — + // unlike the old literal-only path, which left st.limit==0 for a parameter and silently + // fell back to the unbounded stream (losing the pushed top-k bound). Mirrors classic + // fulltext (fulltextIndexScanPrepare); default 0 = no pushed limit. + if st.limit, err = evalLimitExpression(proc, arg.Limit, 0); err != nil { + return nil, err + } + return st, nil +} + +// start runs one query. argVecs: [0]=cfg(json const), [1]=pattern(varchar). +func (u *fulltext2SearchState) start(tf *TableFunction, proc *process.Process, nthRow int, analyzer process.Analyzer) (err error) { + if !u.inited { + cfgVec := tf.ctr.argVecs[0] + if cfgVec.GetType().Oid != types.T_varchar || !cfgVec.IsConst() { + return moerr.NewInvalidInput(proc.Ctx, "fulltext2_search: first argument (config) must be a string constant") + } + cfgstr := cfgVec.UnsafeGetStringAt(0) + if len(cfgstr) == 0 { + return moerr.NewInternalError(proc.Ctx, "fulltext2_search: config is empty") + } + if err = sonic.Unmarshal([]byte(cfgstr), &u.tblcfg); err != nil { + return err + } + patVec := tf.ctr.argVecs[1] + if patVec.GetType().Oid != types.T_varchar { + return moerr.NewInvalidInput(proc.Ctx, "fulltext2_search: second argument (pattern) must be a string") + } + u.batch = tf.createResultBatch() + // Name-driven output map. createResultBatch built one vector per SURVIVING plan + // coldef (column pruning may have dropped/compacted doc_id, score, and/or any + // unprojected INCLUDE col), so classify each result vector by its coldef NAME + // rather than by a fixed position: the RESERVED __mo_ft_doc_id -> pk, __mo_ft_score + // -> score, everything else -> the INCLUDE column of that name (mapped to its + // position in the full index include list = segment / decodeInclude order). The pk/ + // score outputs use reserved names (catalog.FullText2Search_OutCol_*) precisely so an + // INCLUDE column named "doc_id"/"score" can't collide with this classification. + u.pkVecIdx, u.scoreVecIdx = -1, -1 + u.includeOut = nil + u.includeNames = nil + for vi, name := range u.batch.Attrs { + switch { + case strings.EqualFold(name, catalog.FullText2Search_OutCol_DocId): + u.pkVecIdx = vi + case strings.EqualFold(name, catalog.FullText2Search_OutCol_Score): + u.scoreVecIdx = vi + default: + segPos := -1 + for p, full := range u.tblcfg.IncludeColumns { + if strings.EqualFold(full, name) { + segPos = p + break + } + } + u.includeOut = append(u.includeOut, ft2IncludeOut{vecIdx: vi, segPos: segPos, name: name}) + u.includeNames = append(u.includeNames, name) + } + } + u.inited = true + } + + u.stopStream() + u.offset = 0 + u.streaming = false + u.done = false + // u.out is kept and REUSED across queries (SearchInto Resets it per query), but EMPTY it + // here too: a subsequent early-return below (NULL/empty pattern) skips SearchInto, so + // without this a reused operator would page the PREVIOUS query's results as this row's + // output. Emptying leaves call() with 0 rows on the bailed path. (Pre-SearchOutput this + // was a nil u.keys slice.) + if u.out != nil { + if u.out.Keys != nil { + u.out.Keys.Reset() + } + u.out.Dists = u.out.Dists[:0] + u.out.Include = u.out.Include[:0] + } + u.batch.CleanOnlyData() + + patVec := tf.ctr.argVecs[1] + if patVec.IsNull(uint64(nthRow)) { + return nil + } + pattern := patVec.GetStringAt(nthRow) + + // Prefilter pushdown: when the WHERE clause is pushed down as a unique-join-keys + // runtime filter, wait for it and build the docfilter membership bytes — the same + // mechanism bm25_search / fulltext_index_scan use. Applied INSIDE the WAND walk so + // the returned top-K is already filtered (no over-fetch). + if u.filterBytes == nil && len(tf.RuntimeFilterSpecs) > 0 { + res, ferr := waitFulltextMembershipFilter(proc, tf.RuntimeFilterSpecs) + if ferr != nil { + return ferr + } + if res != nil { + u.filterBytes = res.membershipFilterBytes + } + } + + // Run the query through the shared VectorIndexCache: the index (base + CDC tail) + // is loaded ONCE and reused across queries, evicted on CDC append / compaction / + // rebuild (fulltext2_{create,compact} + the CDC consumer call Cache.Remove). + // Before caching, every MATCH reloaded the whole index (~1s at 50K); now warm + // queries are ~ms, matching bm25. Build+query tokenize identically — both use the + // index's parser (carried in tblcfg). + sp := sqlexec.NewSqlProcess(proc) + veccache.Cache.Once() + + // mode (argVecs[2], a query const): boolean → operator query, else NL phrase. + var mode int64 + if mv := tf.ctr.argVecs[2]; mv != nil && mv.Length() > 0 { + mode = vector.GetFixedAtNoTypeCheck[int64](mv, 0) + } + // Optional INCLUDE/pk prefilter predicate JSON (argVecs[3], a query const): the planner + // peels a WHERE predicate on INCLUDE columns / the pk into this ivfpq-aligned JSON, which + // the engine evaluates against the stored per-doc values inside the WAND walk. Absent on a + // direct fulltext2_search(...) call (3 args) or when nothing was peeled. + var includePreds []byte + if len(tf.ctr.argVecs) > 3 { + if pv := tf.ctr.argVecs[3]; pv != nil && pv.Length() > 0 && !pv.IsNull(0) { + if s := pv.GetStringAt(0); len(s) > 0 { + includePreds = []byte(s) + } + } + } + + newsearch := fulltext2.NewFulltext2Search(u.tblcfg) + q := fulltext2.Fulltext2Query{ + Pattern: []byte(pattern), + Boolean: mode == int64(tree.FULLTEXT_BOOLEAN), + BagOfWords: mode == int64(tree.FULLTEXT_BM25), + Algo: fulltext2ScoreAlgo(proc), + FilterBytes: u.filterBytes, + IncludePredsJSON: includePreds, + } + + if u.limit == 0 { + // No pushed LIMIT: STREAM every matching doc in bounded batches (no top-K heap, + // no materialization of the whole result set). A producer goroutine runs the + // search with an Emit callback that hands batches to streamCh; call() drains one + // per invocation and the upstream ORDER BY score node ranks. cancel/ctx let + // reset()/free() stop the producer and release the cache read-lock it holds. + u.streaming = true + u.streamCh = make(chan *vectorindex.SearchOutput, 4) + u.errCh = make(chan error, 1) + ctx, cancel := context.WithCancel(proc.Ctx) + u.cancel = cancel + rt := vectorindex.RuntimeConfig{Emit: func(out *vectorindex.SearchOutput) error { + select { + case u.streamCh <- out: + return nil + case <-ctx.Done(): + return ctx.Err() + } + }} + // Covered fast path: signal the stream to carry each doc's INCLUDE values. The gate in + // Fulltext2Search.Search is len(RequestedIncludeColumns) > 0; the streaming path carries + // the includes through Emit's 3rd arg, not through a result buffer. + if len(u.includeNames) > 0 { + rt.RequestedIncludeColumns = u.includeNames + } + go func() { + _, _, serr := veccache.Cache.Search(sp, u.tblcfg.IndexTable, newsearch, q, rt) + u.errCh <- serr // buffered(1): send before close so call() reads it after drain + close(u.streamCh) + }() + return nil + } + + // With a pushed LIMIT: WAND top-K, filled box-free into the caller-owned u.out via + // SearchInto (pk/scores/includes as reusable ColumnBuffers) — no []any keys. u.out is + // pooled on the state and Reset per query by SearchInto, so a warm query allocates nothing + // for its results; call() pages u.out via u.offset. + rt := vectorindex.RuntimeConfig{Limit: uint(u.limit)} + if len(u.includeNames) > 0 { + // Request the covered INCLUDE columns; SearchInto fills u.out.Include (box-free, + // column-major, whole result set), which call() pages by segPos. + rt.RequestedIncludeColumns = u.includeNames + } + if u.out == nil { + u.out = &vectorindex.SearchOutput{} + } + return veccache.Cache.SearchInto(sp, u.tblcfg.IndexTable, newsearch, q, rt, u.out) +} + +// fulltext2ScoreAlgo resolves the relevance formula from fulltext2's OWN session +// variable ft2_relevancy_algorithm, which defaults to BM25 (distinct from classic +// fulltext's ft_relevancy_algorithm, default TF-IDF). Only an explicit +// SET ft2_relevancy_algorithm='TF-IDF' drops to TF-IDF; on any resolve error the +// BM25 default stands. +func fulltext2ScoreAlgo(proc *process.Process) fulltext2.ScoreAlgo { + algo := fulltext2.BM25 + val, err := proc.GetResolveVariableFunc()(fulltext2.Fulltext2RelevancyAlgo, true, false) + if err == nil && val != nil { + if fmt.Sprintf("%v", val) == fulltext2.Fulltext2RelevancyAlgo_tfidf { + algo = fulltext2.TfIdf + } + } + return algo +} diff --git a/pkg/sql/colexec/table_function/hnsw_create.go b/pkg/sql/colexec/table_function/hnsw_create.go index 3a3d4d7da347b..1851b91be5f11 100644 --- a/pkg/sql/colexec/table_function/hnsw_create.go +++ b/pkg/sql/colexec/table_function/hnsw_create.go @@ -57,33 +57,44 @@ type hnswCreateState struct { } func (u *hnswCreateState) end(tf *TableFunction, proc *process.Process) error { + sqlproc := sqlexec.NewSqlProcess(proc) - var ( - sqls []string - err error - ) + // The TVF owns clear+rebuild (the compile layer no longer pre-deletes on the sync path). + // tblcfg is populated in prepare (const-fold) so it is available even on a zero-row rebuild. + // Guard anyway: without table names we cannot clear (a bare "DELETE FROM " with an empty + // identifier is invalid), so skip rather than run malformed SQL. + if u.tblcfg.MetadataTable == "" || u.tblcfg.IndexTable == "" { + return nil + } + + // Clear the old index unconditionally — even when nothing was built (REBUILD to zero docs must + // still empty the index), so the DELETEs run before the early-out on a nil builder. Cross-CN + // cache freshness is a per-model checksum multiset (see hnsw generation.go), so the metadata + // timestamp plays no role in freshness — plain wall-clock is fine here. + sqls := hnsw.ClearIndexSqls(u.tblcfg) + ts := time.Now().UnixMicro() switch u.idxcfg.Usearch.Quantization { case usearch.F32: - if u.buildf32 == nil { - return nil - } - sqls, err = u.buildf32.ToInsertSql(time.Now().UnixMicro()) - if err != nil { - return err + if u.buildf32 != nil { + insertSqls, err := u.buildf32.ToInsertSql(ts) + if err != nil { + return err + } + sqls = append(sqls, insertSqls...) } case usearch.F64: - if u.buildf64 == nil { - return nil - } - sqls, err = u.buildf64.ToInsertSql(time.Now().UnixMicro()) - if err != nil { - return err + if u.buildf64 != nil { + insertSqls, err := u.buildf64.ToInsertSql(ts) + if err != nil { + return err + } + sqls = append(sqls, insertSqls...) } } for _, s := range sqls { - res, err := hnsw_runSql(sqlexec.NewSqlProcess(proc), s) + res, err := hnsw_runSql(sqlproc, s) if err != nil { return err } @@ -129,10 +140,26 @@ func hnswCreatePrepare(proc *process.Process, arg *TableFunction) (tvfState, err st := &hnswCreateState{} arg.ctr.executorsForArgs, err = colexec.NewExpressionExecutorsFromPlanExpressions(proc, arg.Args) + if err != nil { + return nil, err + } arg.ctr.argVecs = make([]*vector.Vector, len(arg.Args)) - return st, err + // Parse the IndexTableConfig (arg 0, a constant string) up front by const-folding it, so end() + // can clear the hidden tables and read the generation floor even on a ZERO-row rebuild — start() + // normally parses it but never runs when the source is empty, yet a REBUILD to zero docs must + // still clear the old index. Best-effort: on any failure st.tblcfg stays zero and end() skips + // the clear (start() re-parses it for the non-empty path). + if len(arg.ctr.executorsForArgs) > 0 { + if vec, e := arg.ctr.executorsForArgs[0].Eval(proc, []*batch.Batch{batch.EmptyForConstFoldBatch}, nil); e == nil && + vec != nil && vec.IsConst() && !vec.IsConstNull() && vec.GetType().Oid == types.T_varchar { + if cfgstr := vec.UnsafeGetStringAt(0); len(cfgstr) > 0 { + _ = sonic.Unmarshal([]byte(cfgstr), &st.tblcfg) + } + } + } + return st, err } // start calling tvf on nthRow and put the result in u.batch. Note that current tokenize impl will diff --git a/pkg/sql/colexec/table_function/table_function.go b/pkg/sql/colexec/table_function/table_function.go index a36b16c7f16ef..b861d6670712e 100644 --- a/pkg/sql/colexec/table_function/table_function.go +++ b/pkg/sql/colexec/table_function/table_function.go @@ -172,6 +172,10 @@ func (tableFunction *TableFunction) Prepare(proc *process.Process) error { tblArg.ctr.state, err = fulltextIndexScanPrepare(proc, tblArg) case "fulltext_index_tokenize": tblArg.ctr.state, err = fulltextIndexTokenizePrepare(proc, tblArg) + case "fulltext2_create": + tblArg.ctr.state, err = fulltext2CreatePrepare(proc, tblArg) + case "fulltext2_compact": + tblArg.ctr.state, err = fulltext2CompactPrepare(proc, tblArg) case "stage_list": tblArg.ctr.state, err = stageListPrepare(proc, tblArg) case "moplugin_table": @@ -184,6 +188,8 @@ func (tableFunction *TableFunction) Prepare(proc *process.Process) error { tblArg.ctr.state, err = ivfCreatePrepare(proc, tblArg) case "ivf_search": tblArg.ctr.state, err = ivfSearchPrepare(proc, tblArg) + case "fulltext2_search": + tblArg.ctr.state, err = fulltext2SearchPrepare(proc, tblArg) case "parse_jsonl_data": tblArg.ctr.state, err = parseJsonlDataPrepare(proc, tblArg) case "parse_jsonl_file": diff --git a/pkg/sql/compile/alter.go b/pkg/sql/compile/alter.go index 0c1514a0e059b..b4e55bfe7855e 100644 --- a/pkg/sql/compile/alter.go +++ b/pkg/sql/compile/alter.go @@ -2165,7 +2165,11 @@ func cloneUnaffectedIndexes( continue } - async, err := catalog.IsIndexAsync(oriIdxTblNames.IndexAlgoParams) + // IsAsync is the canonical resolution: identity-always-async + // (HNSW/CAGRA/IVF-PQ), fulltext VERSION=2, or the per-index async param. + // Used for both the whole-index skip below and the per-hidden-table + // SkipWhenAsync skip further down. + async, err := indexplugin.IsAsync(oriIdxTblNames.IndexAlgo, oriIdxTblNames.IndexAlgoParams) if err != nil { return err } @@ -2175,8 +2179,7 @@ func cloneUnaffectedIndexes( // policies: // - SkipWholeIndex: skip the entire index when async. Algorithms that // leave every hidden table empty at CREATE and rebuild all of them - // via CDC from ts=0 (HNSW / CAGRA / IVF-PQ / fulltext). HNSW is - // AlwaysAsync; the others gate on the per-index async param. + // via CDC from ts=0 (HNSW / CAGRA / IVF-PQ / fulltext). // - DeleteBeforeClone + SkipWhenAsync (per hidden table): IVF-FLAT is // the only case today. All three hidden tables get DELETE'd (the // CREATE on the temp table already seeded them), entries are @@ -2186,15 +2189,12 @@ func cloneUnaffectedIndexes( var cloneBehavior catalogplugin.AlterTableCloneBehavior if !oriIdxTblNames.Unique { if p, ok := indexplugin.Get(oriIdxTblNames.IndexAlgo); ok { - d := p.Catalog().SyncDescriptor() cloneBehavior = p.Catalog().AlterTableCloneBehavior() // Whole-index skip is an EXPLICIT policy (SkipWholeIndex), not // inferred from UsesCDC — a CDC algorithm can still need its model // tables cloned (IVF-FLAT clones metadata + centroids and only // CDC-rebuilds entries via the per-hidden-table policy below). - // HNSW is AlwaysAsync; CAGRA / IVF-PQ / fulltext gate on the - // per-index async param. - if (d.AlwaysAsync || async) && cloneBehavior.SkipWholeIndex { + if async && cloneBehavior.SkipWholeIndex { logutil.Infof("cloneUnaffectedIndex: skip whole async index %v\n", oriIdxTblNames) continue } diff --git a/pkg/sql/compile/ddl.go b/pkg/sql/compile/ddl.go index f7dc15ca85726..6fb5fbe9b5aef 100644 --- a/pkg/sql/compile/ddl.go +++ b/pkg/sql/compile/ddl.go @@ -519,7 +519,13 @@ func reindexSpecifiedParams(stmt tree.Statement, indexName string) map[string]st addInt(catalog.IndexAlgoParamKmeansTrainPercent, opt.KmeansTrainPercent) addInt(catalog.IndexAlgoParamKmeansMaxIteration, opt.KmeansMaxIteration) addInt(catalog.IndexAlgoParamMaxIndexCapacity, opt.MaxIndexCapacity) + addInt(catalog.IndexAlgoParamMaxPostingsCapacity, opt.MaxPostingsCapacity) addInt(catalog.IndexAlgoParamQuantizerTrainLimit, opt.QuantizerTrainLimit) + // position_free is emitted as an explicit true/false when specified (tri-state: + // absence ⇒ "keep current"), so a fulltext2 REINDEX can turn it on OR off. + if opt.PositionFreeSet { + m[catalog.IndexAlgoParamPositionFree] = strconv.FormatBool(opt.PositionFree) + } // quantization is normalized to lowercase here (matching the CREATE INDEX // path) so case-sensitive consumers (GPU build switch / quantizer) behave // identically; the per-backend VALUE check (which names a given algorithm @@ -1039,7 +1045,7 @@ func (s *Scope) AlterTableInplace(c *Compile) error { alterIndex = indexDef indexAlgo := catalog.ToLower(alterIndex.IndexAlgo) - if !indexplugin.IsVectorIndexAlgo(indexAlgo) { + if !indexplugin.IsVectorIndexAlgo(indexAlgo) && !catalog.IsFullText2IndexAlgo(indexAlgo) { return moerr.NewInternalError(c.proc.Ctx, "invalid index algo type for alter reindex") } // Each algorithm's plugin owns parameter-update @@ -1061,6 +1067,7 @@ func (s *Scope) AlterTableInplace(c *Compile) error { newParamsMap, err := p.Compile().ValidateReindexParams(oldParams, compileplugin.ReindexParamUpdate{ Params: reindexSpecifiedParams(c.stmt, constraintName), + Merge: tableAlterIndex.Merge, }) if err != nil { return err @@ -1112,7 +1119,7 @@ func (s *Scope) AlterTableInplace(c *Compile) error { if cctx == nil { cctx = newPluginCompileCtx(s, c, tblId, extra, dbSource, qry.Database, oTableDef, nil) } - err = p.Compile().HandleReindex(cctx, multiTableIndex.IndexDefs, tableAlterIndex.ForceSync) + err = p.Compile().HandleReindex(cctx, multiTableIndex.IndexDefs, tableAlterIndex.ForceSync, tableAlterIndex.Merge) } if err != nil { diff --git a/pkg/sql/compile/iscp_util.go b/pkg/sql/compile/iscp_util.go index 3009fec58263a..ae0b031f9e414 100644 --- a/pkg/sql/compile/iscp_util.go +++ b/pkg/sql/compile/iscp_util.go @@ -83,14 +83,10 @@ func checkValidIndexCdcByIndexdef(idx *plan.IndexDef) (bool, error) { // Plugin-registered algorithms (vector + fulltext) describe their // CDC participation via SyncDescriptor(). if p, ok := indexplugin.Get(idx.IndexAlgo); ok { - d := p.Catalog().SyncDescriptor() - if !d.UsesCDC { + if !p.Catalog().SyncDescriptor().UsesCDC { return false, nil } - if d.AlwaysAsync { - return true, nil - } - return catalog.IsIndexAsync(idx.IndexAlgoParams) + return indexplugin.IsAsync(idx.IndexAlgo, idx.IndexAlgoParams) } return false, nil diff --git a/pkg/sql/compile/util.go b/pkg/sql/compile/util.go index 8da3f53d06d4a..b5bb095a95e2e 100644 --- a/pkg/sql/compile/util.go +++ b/pkg/sql/compile/util.go @@ -89,7 +89,7 @@ func indexMetadataType(unique bool, algo string) string { return INDEX_TYPE_UNIQUE case catalog.IsRTreeIndexAlgo(algo): return INDEX_TYPE_SPATIAL - case catalog.IsFullTextIndexAlgo(algo): + case catalog.IsFullTextIndexAlgo(algo) || catalog.IsFullText2IndexAlgo(algo): return INDEX_TYPE_FULLTEXT default: return INDEX_TYPE_MULTIPLE diff --git a/pkg/tools/checkpointtool/table_dump.go b/pkg/tools/checkpointtool/table_dump.go index 5ba60feaa0989..ff641af9a6a1d 100644 --- a/pkg/tools/checkpointtool/table_dump.go +++ b/pkg/tools/checkpointtool/table_dump.go @@ -529,7 +529,11 @@ func renderCreateTableDDLFullWithForeignKeysAndClusterBy(tableName string, cols sb.WriteString(" ") fullText := isFullTextKey(key) if fullText { - sb.WriteString("FULLTEXT ") + if isFullText2Key(key) { + sb.WriteString("FULLTEXT2 ") + } else { + sb.WriteString("FULLTEXT ") + } } else if key.Unique { sb.WriteString("UNIQUE ") } @@ -4855,7 +4859,15 @@ func renderCreateIndexStatement(tableName string, info *indexDDLInfo) (string, e sb.WriteString(" ADD ") fullText := isFullTextIndex(info) if fullText { - sb.WriteString("FULLTEXT ") + // fulltext2 is a DISTINCT engine (WAND positional / position-free), not classic + // fulltext — emit FULLTEXT2 so a checkpoint-restore rebuild keeps the engine + // instead of silently downgrading it to classic FULLTEXT (which ignores the + // positional/position-free build and its capacity/scheduling options). + if catalog.IsFullText2IndexAlgo(info.algo) { + sb.WriteString("FULLTEXT2 ") + } else { + sb.WriteString("FULLTEXT ") + } } else if strings.EqualFold(info.indexType, "UNIQUE") { sb.WriteString("UNIQUE ") } @@ -4887,11 +4899,22 @@ func isFullTextIndex(info *indexDDLInfo) bool { if info == nil { return false } - return catalog.IsFullTextIndexAlgo(info.algo) || strings.EqualFold(info.indexType, "FULLTEXT") + return catalog.IsFullTextIndexAlgo(info.algo) || catalog.IsFullText2IndexAlgo(info.algo) || + strings.EqualFold(info.indexType, "FULLTEXT") } func isFullTextKey(key TableUniqueKey) bool { - return catalog.IsFullTextIndexAlgo(key.Algo) + // Both classic FULLTEXT and FULLTEXT2 render as a fulltext-family clause (no KEY/USING + // suffix, WITH PARSER + options). Recognizing only classic here would emit + // `KEY ... USING fulltext2` for a FULLTEXT2 constraint reconstructed from mo_tables, + // which restore routes to BuildSecondaryIndexDefs — that rejects fulltext2 (it needs + // BuildFullTextIndexDefs), breaking checkpoint restore. The keyword itself is chosen by + // isFullText2Key at the render site. + return catalog.IsFullTextIndexAlgo(key.Algo) || catalog.IsFullText2IndexAlgo(key.Algo) +} + +func isFullText2Key(key TableUniqueKey) bool { + return catalog.IsFullText2IndexAlgo(key.Algo) } func appendIndexAlgorithmDDL(sb *strings.Builder, algo string) { @@ -4904,10 +4927,12 @@ func appendIndexAlgorithmDDL(sb *strings.Builder, algo string) { func appendIndexTrailingOptionsDDL(sb *strings.Builder, fullText bool, algoParams, comment string) error { if strings.TrimSpace(algoParams) != "" { + paramMap, err := catalog.IndexParamsStringToMap(algoParams) + if err != nil { + return err + } if fullText { - if params, err := catalog.IndexParamsStringToMap(algoParams); err != nil { - return err - } else if parser := strings.TrimSpace(params["parser"]); parser != "" { + if parser := strings.TrimSpace(paramMap["parser"]); parser != "" { sb.WriteString(" WITH PARSER ") sb.WriteString(parser) } @@ -4919,6 +4944,30 @@ func appendIndexTrailingOptionsDDL(sb *strings.Builder, fullText bool, algoParam if strings.TrimSpace(params) != "" { sb.WriteString(params) } + // INCLUDE columns are a first-class index option that IndexParamsToStringList + // deliberately omits (they belong to the DDL / SHOW CREATE surface). Render them here — + // the same appendIndexTrailingOptionsDDL feeds both DDL sources (mo_tables constraint and + // mo_indexes rows) — so a checkpoint-restore rebuild preserves the covering/prefilter + // contract. Without this a `FULLTEXT2 ft(body) INCLUDE(status)` was restored as + // `FULLTEXT2 ft(body)`, silently dropping the covered fast path. Uses the same + // comma-joined encoding + parser the rest of the system reads (catalog.IncludedColumns / + // ParseIncludeColumnsValue), in persisted order. + if joined := strings.TrimSpace(paramMap[catalog.IncludedColumns]); joined != "" { + cols, err := catalog.ParseIncludeColumnsValue(joined) + if err != nil { + return err + } + if len(cols) > 0 { + sb.WriteString(" INCLUDE (") + for i, c := range cols { + if i > 0 { + sb.WriteString(", ") + } + sb.WriteString(quoteDDLIdent(c)) + } + sb.WriteString(")") + } + } } if comment != "" { sb.WriteString(" COMMENT ") diff --git a/pkg/vectorindex/brute_force/brute_force.go b/pkg/vectorindex/brute_force/brute_force.go index 5aacf4a2d8730..778b82eb918a2 100644 --- a/pkg/vectorindex/brute_force/brute_force.go +++ b/pkg/vectorindex/brute_force/brute_force.go @@ -299,10 +299,6 @@ func (idx *UsearchBruteForceIndex[T]) Search(proc *sqlexec.SqlProcess, _queries return } -func (idx *UsearchBruteForceIndex[T]) UpdateConfig(sif cache.VectorIndexSearchIf) error { - return nil -} - func (idx *UsearchBruteForceIndex[T]) Destroy() { if idx.deallocator != nil { idx.deallocator.Deallocate() @@ -317,10 +313,6 @@ func (idx *GoBruteForceIndex[T, R]) Load(sqlproc *sqlexec.SqlProcess) error { return nil } -func (idx *GoBruteForceIndex[T, R]) UpdateConfig(sif cache.VectorIndexSearchIf) error { - return nil -} - func (idx *GoBruteForceIndex[T, R]) Destroy() { } @@ -500,3 +492,15 @@ func (idx *GoBruteForceIndex[T, R]) Search(proc *sqlexec.SqlProcess, _queries an return retKeys64, retDistances, nil } + +// SearchInto is not yet implemented for this algo (box-free LIMIT path); it will migrate +// from the []any Search per the SearchOutput plan. Mirrors fulltext2's SearchFloat32 stub. +func (idx *UsearchBruteForceIndex[T]) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return moerr.NewInternalErrorNoCtx("SearchInto not supported") +} + +// SearchInto is not yet implemented for this algo (box-free LIMIT path); it will migrate +// from the []any Search per the SearchOutput plan. Mirrors fulltext2's SearchFloat32 stub. +func (idx *GoBruteForceIndex[T, R]) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return moerr.NewInternalErrorNoCtx("SearchInto not supported") +} diff --git a/pkg/vectorindex/brute_force/gpu.go b/pkg/vectorindex/brute_force/gpu.go index 65b80f2c8b17d..9d7b308d55743 100644 --- a/pkg/vectorindex/brute_force/gpu.go +++ b/pkg/vectorindex/brute_force/gpu.go @@ -215,10 +215,6 @@ func (idx *GpuAdhocBruteForceIndex[T]) Search(proc *sqlexec.SqlProcess, _queries return } -func (idx *GpuAdhocBruteForceIndex[T]) UpdateConfig(sif cache.VectorIndexSearchIf) error { - return nil -} - func (idx *GpuAdhocBruteForceIndex[T]) Destroy() { idx.dataset = nil } @@ -424,12 +420,20 @@ func (idx *GpuBruteForceIndex[T]) Search(proc *sqlexec.SqlProcess, _queries any, return } -func (idx *GpuBruteForceIndex[T]) UpdateConfig(sif cache.VectorIndexSearchIf) error { - return nil -} - func (idx *GpuBruteForceIndex[T]) Destroy() { if idx.index != nil { idx.index.Destroy() } } + +// SearchInto is not yet implemented for this algo (box-free LIMIT path); it will migrate +// from the []any Search per the SearchOutput plan. Mirrors fulltext2's SearchFloat32 stub. +func (idx *GpuAdhocBruteForceIndex[T]) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return moerr.NewInternalErrorNoCtx("SearchInto not supported") +} + +// SearchInto is not yet implemented for this algo (box-free LIMIT path); it will migrate +// from the []any Search per the SearchOutput plan. Mirrors fulltext2's SearchFloat32 stub. +func (idx *GpuBruteForceIndex[T]) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return moerr.NewInternalErrorNoCtx("SearchInto not supported") +} diff --git a/pkg/vectorindex/cache/cache.go b/pkg/vectorindex/cache/cache.go index 464aa55bad7c3..40fd18e966408 100644 --- a/pkg/vectorindex/cache/cache.go +++ b/pkg/vectorindex/cache/cache.go @@ -24,10 +24,16 @@ import ( "time" "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/logutil" "github.com/matrixorigin/matrixone/pkg/vectorindex" "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" ) +// stalenessCheckEveryNTicks runs the IsStale freshness sweep every Nth HouseKeeping tick. +// The ticker is VectorIndexCacheTTL/2 (2.5m), so N=4 ≈ a 10-minute cross-CN freshness +// cadence — the bound on how long a remote CN can serve a stale index before it ages out. +const stalenessCheckEveryNTicks = 4 + /* VectorIndexCache is the generalized cache structure for various algorithm types that share the VectorIndexSearchIf interface. Implement the VectorIndexSearchIf such as HnswSearch to able to use VectorIndexCache. @@ -62,20 +68,55 @@ type VectorIndexSearchIf interface { // outKeys and outDists must be pre-allocated to nQueries*rt.Limit elements. // GPU implementations write float32 distances directly; CPU implementations convert on write. SearchFloat32(proc *sqlexec.SqlProcess, query any, rt vectorindex.RuntimeConfig, outKeys []int64, outDists []float32) error + // SearchInto is the box-free, alloc-free-after-warmup twin of Search: instead of + // returning keys as []any (a heap box per key), it fills a CALLER-OWNED SearchResult — + // pk column, scores, and covered-INCLUDE columns, all as reusable ColumnBuffers/slices + // the caller pools and Resets across queries, so a warm query allocates nothing for its + // results. It is the arbitrary-pk generalization of SearchFloat32 (whose outKeys []int64 + // cannot hold varchar/uuid/decimal pks). The callee Resets out before filling; on return + // out.Keys.N is the result count, len(out.Dists) == out.Keys.N, and out.Include (when + // rt.RequestedIncludeColumns is set) holds one buffer per FULL index INCLUDE column. + // Implemented by fulltext2; the vector algos stub it "not supported" until each migrates + // (mirrors how fulltext2 stubs SearchFloat32). + SearchInto(proc *sqlexec.SqlProcess, query any, rt vectorindex.RuntimeConfig, out *vectorindex.SearchOutput) error Load(*sqlexec.SqlProcess) error - UpdateConfig(VectorIndexSearchIf) error Destroy() } +// StaleChecker is an OPTIONAL capability an algo's search impl may implement (currently +// fulltext2). It reports whether the loaded index has fallen behind the persisted index — +// e.g. a CDC append / REBUILD applied on ANOTHER CN, which the local process-scoped Remove +// never sees. HouseKeeping calls it periodically and force-expires stale entries so the next +// search reloads; this is how cross-CN cache coherence is maintained by PULL (each CN checks +// its own entries) with no invalidation broadcast. Impls MUST run their own short background +// txn (the check fires on the housekeeping goroutine, not the search path) and MUST return an +// error rather than "stale" on a transient failure, so a meta-read blip can't trigger a +// reload storm. An impl that cannot determine freshness returns (false, nil). +// +// DESIGN DECISION — EVENTUAL consistency, not immediate (won't-fix, by design): Search serves a +// warmed entry directly and does NOT validate freshness on the query path (that would put a meta +// read on every search — the perf floor this cache exists to avoid). Coherence is the periodic +// PULL sweep only: it runs every stalenessCheckEveryNTicks ticks of the TTL/2 ticker and evicts a +// stale entry on the NEXT housekeeping pass, so after a writer commits on another CN a warm remote +// entry can keep answering the pre-CDC/MERGE/REBUILD snapshot for up to ~10–12.5 minutes before it +// is reloaded. This is intentional: the contract is that a stale entry is EVENTUALLY removed, not +// that reads are correct the instant the writer commits. Do NOT "fix" it by validating on the +// search path or by broadcasting invalidations; if a caller ever needs read-your-writes across CNs, +// that is a separate, opt-in requirement (or disable this cache for that index in multi-CN). +type StaleChecker interface { + IsStale() (bool, error) +} + // base VectorIndex Search structure for VectorIndexSearchIf (see HnswSearch) type VectorIndexSearch struct { Mutex sync.RWMutex ExpireAt atomic.Int64 LastUpdate atomic.Int64 Status atomic.Int32 // 0 - NOT INIT, 1 - LOADED, 2 - marked as outdated, 3 - DESTROYED, 4 or above ERRCODE - Outdated atomic.Bool Algo VectorIndexSearchIf - Cond *sync.Cond // NOTE: this is RWCond. Wait() will use mutex.RLock() and mutex.RUnlock() + Cond *sync.Cond // NOTE: this is RWCond. Wait() will use mutex.RLock() and mutex.RUnlock() + stale atomic.Bool // set by the IsStale freshness check; reclaimed next sweep. Separate from + // ExpireAt so a concurrent Search's extend() (sliding TTL) can't un-mark a stale entry. } func (s *VectorIndexSearch) Destroy() { @@ -117,6 +158,15 @@ func (s *VectorIndexSearch) Expired() bool { return (ts > 0 && ts < now) } +// markStale flags this entry for reclamation by the NEXT HouseKeeping sweep. Used by the +// IsStale freshness check to schedule eviction of a stale index without evicting inline (the +// removal always goes through the single expired/stale-sweep path, keeping Search pure-read). +// A dedicated flag (not ExpireAt) so a concurrent Search's extend() sliding TTL cannot +// un-mark a hot stale entry. +func (s *VectorIndexSearch) markStale() { + s.stale.Store(true) +} + func (s *VectorIndexSearch) extend(update bool) { now := time.Now() if update { @@ -144,16 +194,33 @@ func (s *VectorIndexSearch) Search(sqlproc *sqlexec.SqlProcess, newalgo VectorIn } } - // if error mark as outdated - err = s.Algo.UpdateConfig(newalgo) - if err != nil { - s.Outdated.Store(true) - } - + // The cached index's configuration is immutable for its lifetime (a config change + // evicts the entry via Cache.Remove), so there is nothing to refresh from newalgo + // here. Search is therefore pure-read under the shared read lock — no mutation of the + // cached algo, so concurrent searches on one entry cannot race on its config. s.extend(false) return s.Algo.Search(sqlproc, query, rt) } +// SearchInto mirrors Search but routes to the box-free SearchInto (caller-owned out +// SearchResult). Same shared-read-lock / status discipline. +func (s *VectorIndexSearch) SearchInto(sqlproc *sqlexec.SqlProcess, query any, rt vectorindex.RuntimeConfig, out *vectorindex.SearchOutput) error { + s.Cond.L.Lock() + defer s.Cond.L.Unlock() + for s.Status.Load() == 0 { + s.Cond.Wait() + } + status := s.Status.Load() + if status >= STATUS_DESTROYED { + if status == STATUS_DESTROYED { + return moerr.NewInvalidStateNoCtx("Index destroyed") + } + return moerr.NewInternalErrorNoCtx("Load index error") + } + s.extend(false) + return s.Algo.SearchInto(sqlproc, query, rt, out) +} + // implementation of VectorIndexCache type VectorIndexCache struct { IndexMap sync.Map @@ -164,6 +231,8 @@ type VectorIndexCache struct { started atomic.Bool exited atomic.Bool once sync.Once + hkTicks int // HouseKeeping tick counter, gates the IsStale sweep cadence + staleChecking atomic.Bool // single-flight guard for the async freshness sweep } func NewVectorIndexCache() *VectorIndexCache { @@ -199,8 +268,16 @@ func (c *VectorIndexCache) serve() { c.Destroy() return case <-c.ticker.C: - // delete expired index + // delete expired index (fast, no SQL) — always runs synchronously so TTL + // reclamation and shutdown never wait on a freshness read. c.HouseKeeping() + c.hkTicks++ + if c.hkTicks%stalenessCheckEveryNTicks == 0 && c.staleChecking.CompareAndSwap(false, true) { + go func() { + defer c.staleChecking.Store(false) + c.checkStale() + }() + } } } }() @@ -218,7 +295,7 @@ func (c *VectorIndexCache) HouseKeeping() { c.IndexMap.Range(func(key, value any) bool { algo := value.(*VectorIndexSearch) - if algo.Expired() || algo.Outdated.Load() { + if algo.Expired() || algo.stale.Load() { expiredkeys = append(expiredkeys, key.(string)) } return true @@ -230,6 +307,49 @@ func (c *VectorIndexCache) HouseKeeping() { algo := value.(*VectorIndexSearch) algo.Destroy() algo = nil + logutil.Debugf("[veccache] evicted expired/stale index %s from cache", k) + } + } +} + +// checkStale asks every loaded StaleChecker entry whether it is stale and marks the stale ones +// (the next HouseKeeping sweep reclaims them; Search stays pure-read). Runs on its own +// goroutine off the ticker (see serve), single-flighted, because each IsStale opens a short +// background txn — so a slow/stalled executor delays only the next freshness sweep, never TTL +// eviction or shutdown. The IsStale calls are collected out of the IndexMap.Range callback so a +// slow meta read never holds up the map iteration, and the loop bails on shutdown. +func (c *VectorIndexCache) checkStale() { + type staleEntry struct { + s *VectorIndexSearch + sc StaleChecker + key any + } + entries := make([]staleEntry, 0, 16) + c.IndexMap.Range(func(key, value any) bool { + algo := value.(*VectorIndexSearch) + if algo.Status.Load() != STATUS_LOADED { + return true // skip loading/errored/destroyed entries + } + if sc, ok := algo.Algo.(StaleChecker); ok { + entries = append(entries, staleEntry{algo, sc, key}) + } + return true + }) + for _, e := range entries { + // Bail promptly on shutdown so a K-entry sweep of ≤1-min SQL reads can't keep this + // goroutine (and any resources it pins) alive long after Destroy. + if c.exited.Load() { + return + } + stale, err := e.sc.IsStale() + if err != nil { + // A query error usually means the index was dropped/rebuilt out from under us — + // IsStale returns stale=true so the dead entry is reclaimed; log the cause. + logutil.Warnf("[veccache] IsStale for index %v errored (treating as stale): %v", e.key, err) + } + if stale { + logutil.Infof("[veccache] index %v is stale — marking for eviction on next sweep", e.key) + e.s.markStale() } } } @@ -282,7 +402,39 @@ func (c *VectorIndexCache) Search(sqlproc *sqlexec.SqlProcess, key string, newal } } +// SearchInto is the box-free twin of Search: it fills the caller-owned out SearchResult +// (pk/scores/includes as reusable ColumnBuffers) instead of returning boxed []any keys. +// Same LoadOrStore / Load / ErrInvalidState-retry discipline as Search. +func (c *VectorIndexCache) SearchInto(sqlproc *sqlexec.SqlProcess, key string, newalgo VectorIndexSearchIf, + query any, rt vectorindex.RuntimeConfig, out *vectorindex.SearchOutput) error { + for { + s := &VectorIndexSearch{Algo: newalgo} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + value, loaded := c.IndexMap.LoadOrStore(key, s) + algo := value.(*VectorIndexSearch) + if !loaded { + if err := algo.Load(sqlproc); err != nil { + c.IndexMap.Delete(key) + return err + } + } + err := algo.SearchInto(sqlproc, query, rt, out) + if err != nil { + if moerr.IsMoErrCode(err, moerr.ErrInvalidState) { + continue // index destroyed by Remove()/HouseKeeping — retry + } + return err + } + return nil + } +} + // remove key from cache +// Remove drops a cached index by key so the next Search reloads it. Callers use +// it after a mutation (CDC append, CREATE/REBUILD/MERGE) makes the cached copy +// stale. It is LOCAL to this process — a prompt local optimization only; cross-CN +// coherence is handled by the pull-based freshness check (StaleChecker/IsStale via +// HouseKeeping), which evicts a remote CN's warm-but-stale entry on its own. func (c *VectorIndexCache) Remove(key string) { value, loaded := c.IndexMap.LoadAndDelete(key) if loaded { diff --git a/pkg/vectorindex/cachegen/cachegen.go b/pkg/vectorindex/cachegen/cachegen.go new file mode 100644 index 0000000000000..2f826bcc87fa2 --- /dev/null +++ b/pkg/vectorindex/cachegen/cachegen.go @@ -0,0 +1,118 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package cachegen provides the VectorIndexCache freshness-check (IsStale) generation queries +// for CDC-maintained cuvs/hnsw indexes. It lives in its own package — not pkg/vectorindex — so +// that importing pkg/vectorindex/sqlexec (which pulls in vm/engine → objectio → catalog) does +// not create an import cycle back through pkg/vectorindex under the GPU build. +package cachegen + +import ( + "context" + "fmt" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/sqlquote" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/util/executor" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" +) + +// indirected for unit tests. +var ( + runSql = sqlexec.RunSql + runSqlAutoCommit = sqlexec.RunSqlAutoCommit +) + +// CdcGenerationSqls returns the (timestamp, tail-chunk) generation queries shared by every +// CDC-maintained cuvs/hnsw index, for the VectorIndexCache cross-CN freshness check (IsStale): +// - MAX(metadata.timestamp): a REBUILD/MERGE writes a new model row with a fresh timestamp. +// - MAX(storage.chunk_id) of the (vectorindex.CdcTailId, tag=1) event tail: a CDC append bumps it. +// +// A change in EITHER means the loaded index is stale. Two reads because timestamp and tag live +// in different tables (metadata has no tag; storage has no timestamp). The tail read is scoped +// to (vectorindex.CdcTailId, tag=1) — the exact CDC delta, which every cuvs index writes under activeIndexId +// == vectorindex.CdcTailId — so an unrelated base sub-index's higher chunk_id can't mask a fresh append. An +// index with no event tail (hnsw rebuilds the model instead) simply reads a constant -1 for the +// tail half; MAX(timestamp) then catches all of its changes. The hidden-table column names +// ("timestamp","chunk_id","index_id","tag") are uniform across these index types. +func CdcGenerationSqls(tblcfg vectorindex.IndexTableConfig) (tsSQL, tailSQL string) { + tsSQL = fmt.Sprintf("SELECT COALESCE(MAX(timestamp), 0) FROM %s", + sqlquote.QualifiedIdent(tblcfg.DbName, tblcfg.MetadataTable)) + tailSQL = fmt.Sprintf("SELECT COALESCE(MAX(chunk_id), -1) FROM %s WHERE index_id = %s AND tag = %d", + sqlquote.QualifiedIdent(tblcfg.DbName, tblcfg.IndexTable), sqlquote.String(vectorindex.CdcTailId), int(vectorindex.Tag_CdcEvents)) + return +} + +func genScalarInt64(res executor.Result) int64 { + for _, bat := range res.Batches { + if bat != nil && bat.RowCount() > 0 { + return vector.GetFixedAtNoTypeCheck[int64](bat.Vecs[0], 0) + } + } + return 0 +} + +// LoadCdcGeneration reads the current (timestamp, tailChunk) generation using the caller's LIVE +// sqlproc/txn — called at load time so the captured generation reflects exactly the txn snapshot +// the cached index was built from. Panics from the internal SQL executor (e.g. no executor in a +// unit-test proc) are converted to an error, since generation capture is best-effort — the +// caller degrades to genValid=false (IsStale becomes a no-op) rather than failing the load. +func LoadCdcGeneration(sqlproc *sqlexec.SqlProcess, tblcfg vectorindex.IndexTableConfig) (ts int64, tail int64, err error) { + defer func() { + if r := recover(); r != nil { + ts, tail, err = 0, 0, moerr.NewInternalErrorNoCtx(fmt.Sprintf("LoadCdcGeneration recovered: %v", r)) + } + }() + tsSQL, tailSQL := CdcGenerationSqls(tblcfg) + res, err := runSql(sqlproc, tsSQL) + if err != nil { + return 0, 0, err + } + ts = genScalarInt64(res) + res.Close() + res, err = runSql(sqlproc, tailSQL) + if err != nil { + return 0, 0, err + } + tail = genScalarInt64(res) + res.Close() + return ts, tail, nil +} + +// QueryCdcGeneration reads the current (timestamp, tailChunk) generation in the BACKGROUND +// (housekeeping goroutine, no live sqlproc) via an executor-managed auto-commit txn, keyed by +// the CN UUID + tenant captured at load. Used by an index's IsStale. +func QueryCdcGeneration(ctx context.Context, cnUUID string, accountID uint32, tblcfg vectorindex.IndexTableConfig) (ts int64, tail int64, err error) { + defer func() { + if r := recover(); r != nil { + ts, tail, err = 0, 0, moerr.NewInternalErrorNoCtx(fmt.Sprintf("QueryCdcGeneration recovered: %v", r)) + } + }() + tsSQL, tailSQL := CdcGenerationSqls(tblcfg) + res, err := runSqlAutoCommit(ctx, cnUUID, accountID, tblcfg.DbName, tsSQL) + if err != nil { + return 0, 0, err + } + ts = genScalarInt64(res) + res.Close() + res, err = runSqlAutoCommit(ctx, cnUUID, accountID, tblcfg.DbName, tailSQL) + if err != nil { + return 0, 0, err + } + tail = genScalarInt64(res) + res.Close() + return ts, tail, nil +} diff --git a/pkg/vectorindex/cagra/plugin/compile/compile.go b/pkg/vectorindex/cagra/plugin/compile/compile.go index ea7af87d7d8ce..a02fca052b44d 100644 --- a/pkg/vectorindex/cagra/plugin/compile/compile.go +++ b/pkg/vectorindex/cagra/plugin/compile/compile.go @@ -59,7 +59,7 @@ type Hooks struct{} // (pkg/sql/compile/ddl_index_algo.go:732). // // The sync-vs-async branch is driven by the index's `async` -// IndexAlgoParam (catalog.IsIndexAsync). Default (key missing or +// IndexAlgoParam (catalog.IndexParamAsync). Default (key missing or // "false"): forceSync=true — cagra_create runs inline in the user's // CREATE INDEX txn before the CDC task is registered. Explicit // async="true": forceSync=false — the build SQL is stashed as @@ -69,7 +69,7 @@ func (h Hooks) HandleCreateIndex(ctx compileplugin.CompileContext, indexDefs map if !ok || metaDef == nil { return h.handleCreate(ctx, indexDefs, true) } - async, err := catalog.IsIndexAsync(metaDef.IndexAlgoParams) + async, err := catalog.IndexParamAsync(metaDef.IndexAlgoParams) if err != nil { return err } @@ -80,7 +80,7 @@ func (h Hooks) HandleCreateIndex(ctx compileplugin.CompileContext, indexDefs map // forceSync. The idxcron background reindex executor passes // forceSync=true so the build happens synchronously inside the txn // before the CDC task picks up forward changes. Mirrors IVF-FLAT. -func (h Hooks) HandleReindex(ctx compileplugin.CompileContext, indexDefs map[string]*plan.IndexDef, forceSync bool) error { +func (h Hooks) HandleReindex(ctx compileplugin.CompileContext, indexDefs map[string]*plan.IndexDef, forceSync bool, _ bool) error { return h.handleCreate(ctx, indexDefs, forceSync) } diff --git a/pkg/vectorindex/cagra/plugin/runtime/runtime.go b/pkg/vectorindex/cagra/plugin/runtime/runtime.go index 7938ad91edf45..701bde25ac7e0 100644 --- a/pkg/vectorindex/cagra/plugin/runtime/runtime.go +++ b/pkg/vectorindex/cagra/plugin/runtime/runtime.go @@ -106,6 +106,8 @@ func (CatalogHooks) SupportedVectorTypes() []types.T { return []types.T{types.T_array_float32, types.T_array_float16} } +func (CatalogHooks) IsVectorIndex() bool { return true } + // SupportedPrimaryKeyTypes: requires an int64 primary key. func (CatalogHooks) SupportedPrimaryKeyTypes() []types.T { return []types.T{types.T_int64} } @@ -229,6 +231,9 @@ func (CatalogHooks) ParamsFromTree(idx *tree.Index) (map[string]string, error) { if idx.IndexOption.Hour > 0 { res[catalog.Hour] = strconv.FormatInt(idx.IndexOption.Hour, 10) } + if idx.IndexOption.Second > 0 { + res[catalog.Second] = strconv.FormatInt(idx.IndexOption.Second, 10) + } if len(idx.IndexOption.Quantization) > 0 { quantize := catalog.ToLower(idx.IndexOption.Quantization) diff --git a/pkg/vectorindex/cagra/search_gpu.go b/pkg/vectorindex/cagra/search_gpu.go index 7a759e20599ae..d1b83b5d1eabd 100644 --- a/pkg/vectorindex/cagra/search_gpu.go +++ b/pkg/vectorindex/cagra/search_gpu.go @@ -17,12 +17,14 @@ package cagra import ( + "context" "math" + "time" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/cuvs" "github.com/matrixorigin/matrixone/pkg/vectorindex" - "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" + "github.com/matrixorigin/matrixone/pkg/vectorindex/cachegen" cuvscdc "github.com/matrixorigin/matrixone/pkg/vectorindex/cuvs" "github.com/matrixorigin/matrixone/pkg/vectorindex/metric" "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" @@ -39,6 +41,15 @@ type CagraSearch[B, Q cuvs.VectorType] struct { Overflow cuvs.BruteForceOverflow[B] // CDC insert overflow; nil when no overflow records exist Devices []int ThreadsSearch int64 + + // Generation captured at Load for the cross-CN cache freshness check (IsStale): a + // REBUILD/MERGE bumps MAX(metadata.timestamp); a CDC append bumps the (CdcTailId,tag=1) + // tail chunk_id. cnUUID/accountID re-query in the background. genValid=false ⇒ no-op. + cnUUID string + accountID uint32 + loadedTs int64 + loadedTail int64 + genValid bool } func NewCagraSearch[B, Q cuvs.VectorType](idxcfg vectorindex.IndexConfig, tblcfg vectorindex.IndexTableConfig, devices []int) *CagraSearch[B, Q] { @@ -189,7 +200,39 @@ func (s *CagraSearch[B, Q]) Load(sqlproc *sqlexec.SqlProcess) (err error) { return err } s.MultiIndex, err = s.buildMultiIndex() - return err + if err != nil { + return err + } + // Capture the generation + durable handles for IsStale (same txn as the load). On capture + // failure genValid stays false and IsStale reports the entry as uncheckable-hence-stale + // (evict + reload to retry capture) rather than pinning it forever — see IsStale. + s.cnUUID = sqlproc.GetService() + if acc, e := sqlproc.GetAccountID(); e == nil { + if ts, tail, e2 := cachegen.LoadCdcGeneration(sqlproc, s.Tblcfg); e2 == nil { + s.accountID, s.loadedTs, s.loadedTail, s.genValid = acc, ts, tail, true + } + } + return nil +} + +// IsStale reports whether the loaded index has fallen behind the persisted one (REBUILD bumps +// the metadata timestamp; a CDC append bumps the tag=1 tail chunk_id), for the VectorIndexCache +// cross-CN freshness check. Runs on the housekeeping goroutine via a background auto-commit txn. +// A query error ⇒ (true, err): the index was likely dropped/rebuilt, reclaim the dead entry. +// No captured generation (capture failed at load, or no service to re-query) ⇒ (true, nil): the +// entry cannot self-check freshness, so evict it to force a reload that retries capture, rather +// than pinning a hot entry (whose TTL keeps sliding on every search) to serve stale data forever. +func (s *CagraSearch[B, Q]) IsStale() (bool, error) { + if !s.genValid || s.cnUUID == "" { + return true, nil + } + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + ts, tail, err := cachegen.QueryCdcGeneration(ctx, s.cnUUID, s.accountID, s.Tblcfg) + if err != nil { + return true, err + } + return ts != s.loadedTs || tail != s.loadedTail, nil } // loadCdcTail loads the tag=1 event-log rows persisted by CDC under the @@ -456,7 +499,8 @@ func (s *CagraSearch[B, Q]) Destroy() { s.Indexes = nil } -// UpdateConfig implements cache.VectorIndexSearchIf. -func (s *CagraSearch[B, Q]) UpdateConfig(newalgo cache.VectorIndexSearchIf) error { - return nil +// SearchInto is not yet implemented for this algo (box-free LIMIT path); it will migrate +// from the []any Search per the SearchOutput plan. Mirrors fulltext2's SearchFloat32 stub. +func (s *CagraSearch[B, Q]) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return moerr.NewInternalErrorNoCtx("SearchInto not supported") } diff --git a/pkg/vectorindex/columnbuffer.go b/pkg/vectorindex/columnbuffer.go new file mode 100644 index 0000000000000..4d945a8903cec --- /dev/null +++ b/pkg/vectorindex/columnbuffer.go @@ -0,0 +1,193 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vectorindex + +import ( + "encoding/binary" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" +) + +// ColumnBuffer is a batch of primary keys in a TYPED, box-free columnar form, used by +// the fulltext2 no-LIMIT streaming path (RuntimeConfig.Emit). A pk column has ONE type, +// so every key in a batch is uniformly fixed-width or varlena and Type alone +// disambiguates Data: +// - fixed-width type (int/uint/temporal/decimal): Data is N contiguous width-byte +// little-endian values (no length prefix; width is implied by Type). +// - varlena type (varchar/blob/json/uuid): Data is N [u32 len][content] entries. +// +// Neither form boxes into any, and Data is a copy (not a view into the segment mmap), +// so an in-flight batch survives segment eviction. +type ColumnBuffer struct { + Type types.T + Data []byte // fixed: N×width bytes; varlena: [u32 len][content] entries + N int // element count + // Nulls is the per-element SQL-NULL flag, used only by NULLABLE columns (fulltext2 + // INCLUDE cols). It stays nil for pk columns (a pk is never NULL) — a nil Nulls means + // "all non-null", so the pk path is unchanged. When non-nil it is kept parallel to N + // (len(Nulls) == N): a NULL element still carries a well-formed placeholder in Data (a + // zero-filled fixed value / a [u32 0] varlena entry) so the Data cursor stays aligned. + Nulls []bool +} + +// Reset clears a ColumnBuffer for reuse without dropping its backing buffer. The +// streaming producer/consumer recycle these through fulltext2's pool (GetColumnBuffer / +// PutColumnBuffer); the pooling POLICY lives with its user, not in this shared struct. +func (k *ColumnBuffer) Reset() { + k.Data = k.Data[:0] + k.N = 0 + if k.Nulls != nil { + k.Nulls = k.Nulls[:0] + } +} + +// AppendColumnBuffer decodes a whole ColumnBuffer batch straight into vec (see +// AppendColumnBufferRange for the semantics); it is AppendColumnBufferRange over [0, k.N). +// It is the generic inverse of "fill a ColumnBuffer" — it reads only ColumnBuffer's own +// documented Data/Nulls layout (not any algo's on-disk format), so it lives here with the +// type. The box-free ENCODE side (segment bytes → ColumnBuffer) is algo-specific and stays +// in the algo package (e.g. fulltext2's appendPkTo/appendIncludeTo). +func AppendColumnBuffer(k *ColumnBuffer, vec *vector.Vector, mp *mpool.MPool) error { + return AppendColumnBufferRange(k, vec, 0, k.N, mp) +} + +// AppendColumnBufferRange decodes the rows [start, start+n) of a ColumnBuffer straight into +// vec, appending each element with the TYPED vector.AppendFixed/AppendBytes — no per-element +// boxing on the consumer side (the mirror of the producer's box-free encode). The Data layout +// is: fixed-width types are N contiguous width-byte little-endian values; varlena types are N +// [u32 len][content] entries. vec's type MUST match k.Type. +// +// The range form lets the LIMIT-path TVF page one whole-result buffer in output-sized chunks +// (the streaming path emits a fresh per-batch buffer instead, so it uses the [0,N) wrapper). +// A fixed-width column seeks to start in O(1); a varlena column walks the length prefixes up +// to start (O(start)), which across P forward pages totals O(N·P) — negligible for a LIMIT. +// +// NULLs: a pk column leaves k.Nulls nil (never NULL) — nul(i) is always false, unchanged. +// A nullable INCLUDE column carries k.Nulls parallel to N; a NULL element still occupies a +// well-formed placeholder in Data (zero-filled fixed value / [u32 0] varlena) so the cursor +// stays aligned, and nul(i) makes AppendFixed/AppendBytes write a SQL NULL instead. +func AppendColumnBufferRange(k *ColumnBuffer, vec *vector.Vector, start, n int, mp *mpool.MPool) error { + // Each arm loops with an `err == nil` guard and a single `return err` at the end, so + // AppendFixed/AppendBytes's error path (which does not fire for a well-typed vector + + // valid value) needs no per-arm return block. + if start < 0 { + start = 0 + } + end := start + n + if end > k.N { + end = k.N + } + if start >= end { + return nil + } + d := k.Data + nul := func(i int) bool { return k.Nulls != nil && i < len(k.Nulls) && k.Nulls[i] } + var err error + switch k.Type { + case types.T_int64: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, int64(binary.LittleEndian.Uint64(d[i*8:])), nul(i), mp) + } + case types.T_uint64, types.T_bit: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, binary.LittleEndian.Uint64(d[i*8:]), nul(i), mp) + } + case types.T_int32: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, int32(binary.LittleEndian.Uint32(d[i*4:])), nul(i), mp) + } + case types.T_uint32: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, binary.LittleEndian.Uint32(d[i*4:]), nul(i), mp) + } + case types.T_int16: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, int16(binary.LittleEndian.Uint16(d[i*2:])), nul(i), mp) + } + case types.T_uint16: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, binary.LittleEndian.Uint16(d[i*2:]), nul(i), mp) + } + case types.T_int8: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, int8(d[i]), nul(i), mp) + } + case types.T_uint8: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, d[i], nul(i), mp) + } + case types.T_date: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, types.Date(int32(binary.LittleEndian.Uint32(d[i*4:]))), nul(i), mp) + } + case types.T_datetime: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, types.Datetime(int64(binary.LittleEndian.Uint64(d[i*8:]))), nul(i), mp) + } + case types.T_time: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, types.Time(int64(binary.LittleEndian.Uint64(d[i*8:]))), nul(i), mp) + } + case types.T_timestamp: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, types.Timestamp(int64(binary.LittleEndian.Uint64(d[i*8:]))), nul(i), mp) + } + case types.T_decimal64: + for i := start; err == nil && i < end; i++ { + err = vector.AppendFixed(vec, types.Decimal64(binary.LittleEndian.Uint64(d[i*8:])), nul(i), mp) + } + case types.T_decimal128: + for i := start; err == nil && i < end; i++ { + b := d[i*16:] + err = vector.AppendFixed(vec, types.Decimal128{B0_63: binary.LittleEndian.Uint64(b[0:8]), B64_127: binary.LittleEndian.Uint64(b[8:16])}, nul(i), mp) + } + case types.T_varchar, types.T_char, types.T_text, types.T_datalink, + types.T_binary, types.T_varbinary, types.T_blob, types.T_json: + off := 0 + for i := 0; i < start; i++ { // walk length prefixes to the start row + off += 4 + int(binary.LittleEndian.Uint32(d[off:])) + } + for i := start; err == nil && i < end; i++ { + l := int(binary.LittleEndian.Uint32(d[off:])) + off += 4 + err = vector.AppendBytes(vec, d[off:off+l], nul(i), mp) + off += l + } + case types.T_uuid: + off := 0 + for i := 0; i < start; i++ { // walk length prefixes to the start row + off += 4 + int(binary.LittleEndian.Uint32(d[off:])) + } + for i := start; err == nil && i < end; i++ { + l := int(binary.LittleEndian.Uint32(d[off:])) + off += 4 + if nul(i) { + err = vector.AppendFixed(vec, types.Uuid{}, true, mp) + } else { + var u types.Uuid + if u, err = types.ParseUuid(string(d[off : off+l])); err == nil { + err = vector.AppendFixed(vec, u, false, mp) + } + } + off += l + } + default: + return moerr.NewInternalErrorNoCtxf("column buffer: unsupported type %d", k.Type) + } + return err +} diff --git a/pkg/vectorindex/cuvs/cdc.go b/pkg/vectorindex/cuvs/cdc.go index 7ec3a3be34642..23ba8e186d149 100644 --- a/pkg/vectorindex/cuvs/cdc.go +++ b/pkg/vectorindex/cuvs/cdc.go @@ -85,6 +85,12 @@ const ( cdcFrameOverhead = cdcHeaderSize + cdcFooterSize // 44 bytes, ex. header section ) +// CdcHeaderSize is the fixed frame header length — the minimum prefix CdcFrameLen +// needs. Exported so a streaming reader (e.g. the bm25 index's file-based tail +// loader) can read exactly the header to learn a frame's total length before +// reading it. +const CdcHeaderSize = cdcHeaderSize + // FrameCdcChunk wraps the given record bytes (plus an optional header, // typically colMetaJSON) into the on-wire chunk frame described above. // nInserts / nDeletes / nUpserts are the per-op record counts contained @@ -159,6 +165,26 @@ func UnframeCdcChunk(framed []byte) (records, header []byte, nInserts, nDeletes, return records, header, nInserts, nDeletes, nUpserts, nil } +// CdcFrameLen returns the total on-wire byte length of the CDC chunk frame whose +// leading bytes are `prefix` (at least cdcHeaderSize bytes — the frame header is +// self-describing). It lets a consumer that STORES one frame split across several +// fixed-size storage chunks reassemble it: read the first stored chunk's header, +// get the total length, then read that many bytes across the following chunks. +// cuVS's own tail packs small records so a frame is always <= one chunk; the bm25 +// retrieval index stores indivisible segment blobs that can exceed a chunk, hence +// this helper. Validates the start magic; does not require the full frame. +func CdcFrameLen(prefix []byte) (int, error) { + if len(prefix) < cdcHeaderSize { + return 0, moerr.NewInternalErrorNoCtxf("CdcFrameLen: prefix too short (%d < %d)", len(prefix), cdcHeaderSize) + } + if got := binary.LittleEndian.Uint32(prefix[0:4]); got != cdcChunkMagic { + return 0, moerr.NewInternalErrorNoCtxf("CdcFrameLen: bad start magic 0x%08x (want 0x%08x)", got, cdcChunkMagic) + } + plen := binary.LittleEndian.Uint32(prefix[20:24]) + hlen := binary.LittleEndian.Uint32(prefix[24:28]) + return cdcFrameOverhead + int(hlen) + int(plen), nil +} + // CDC event log helpers shared by CAGRA and IVF-PQ. // // CDC writes never touch the model tar (tag=0). They append op-tagged event diff --git a/pkg/vectorindex/hnsw/generation.go b/pkg/vectorindex/hnsw/generation.go new file mode 100644 index 0000000000000..f2663ebb542ba --- /dev/null +++ b/pkg/vectorindex/hnsw/generation.go @@ -0,0 +1,127 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hnsw + +import ( + "context" + "fmt" + "hash/fnv" + "sort" + + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/sqlquote" + "github.com/matrixorigin/matrixone/pkg/util/executor" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" +) + +// indirected for unit tests. +var runSqlAutoCommit = sqlexec.RunSqlAutoCommit + +// hnswGenerationSql returns the freshness-generation query for the VectorIndexCache cross-CN +// IsStale check: every model's file checksum (an MD5 of the model file). The generation is the +// MULTISET of those checksums — a pure CONTENT fingerprint, independent of any clock: +// +// - any content change (a CDC append/delete rewrites a model file, or a REBUILD/MERGE) yields a +// new checksum → the fingerprint changes; +// - emptying a model deletes its checksum row → the fingerprint changes; +// - a rebuild that reproduces byte-identical content yields the same checksums → the same +// generation, which is correct (serving identical bytes is not stale). +// +// It deliberately does NOT use metadata.timestamp: a timestamp cannot survive an intermediate empty +// state (a zero-row rebuild deletes every row), so an (X,N)→(0,0)→(X,N) rebuild sequence under a +// reused/backward wall-clock could re-mint a generation a warm remote cache already holds. A content +// checksum makes different content a different generation regardless of the clock — closing that ABA. +func hnswGenerationSql(tblcfg vectorindex.IndexTableConfig) string { + return fmt.Sprintf("SELECT %s FROM %s", + catalog.Hnsw_TblCol_Metadata_Checksum, + sqlquote.QualifiedIdent(tblcfg.DbName, tblcfg.MetadataTable)) +} + +// genChecksums folds a checksum result set into an order-independent 64-bit fingerprint plus the +// model-row count (a cheap second dimension that guards a fingerprint hash collision). The checksums +// are sorted first so the fingerprint is a pure multiset digest, insensitive to row order; each is +// NUL-delimited so ["a","bc"] and ["ab","c"] cannot collide. +func genChecksums(res executor.Result) (fp uint64, count int64) { + var sums []string + for _, bat := range res.Batches { + if bat == nil { + continue + } + v := bat.Vecs[0] + n := bat.RowCount() + for i := 0; i < n; i++ { + sums = append(sums, v.GetStringAt(i)) + } + } + sort.Strings(sums) + h := fnv.New64a() + for _, s := range sums { + _, _ = h.Write([]byte(s)) + _, _ = h.Write([]byte{0}) + } + return h.Sum64(), int64(len(sums)) +} + +// ClearIndexSqls returns the statements that empty the hnsw hidden tables (metadata + storage). +// The full-build TVF issues these before rebuilding, so it owns clear+rebuild. +// +// WHERE TRUE is deliberate: a bare "DELETE FROM t" is rewritten to a TRUNCATE, which swaps the +// table's physical id (a new index table under the same name). This in-place rebuild keeps the +// table stable and relies on the checksum generation for freshness, so we force a plain row delete. +func ClearIndexSqls(tblcfg vectorindex.IndexTableConfig) []string { + return []string{ + "DELETE FROM " + sqlquote.QualifiedIdent(tblcfg.DbName, tblcfg.MetadataTable) + " WHERE TRUE", + "DELETE FROM " + sqlquote.QualifiedIdent(tblcfg.DbName, tblcfg.IndexTable) + " WHERE TRUE", + } +} + +// loadHnswGeneration reads the current (checksumFingerprint, modelCount) generation using the +// caller's LIVE sqlproc/txn — captured at load so it reflects exactly the snapshot the cached index +// was built from. Panics from the internal SQL executor (e.g. no executor in a unit-test proc) are +// recovered into an error; the caller degrades to genValid=false rather than failing the load. +func loadHnswGeneration(sqlproc *sqlexec.SqlProcess, tblcfg vectorindex.IndexTableConfig) (fp uint64, count int64, err error) { + defer func() { + if r := recover(); r != nil { + fp, count, err = 0, 0, moerr.NewInternalErrorNoCtx(fmt.Sprintf("loadHnswGeneration recovered: %v", r)) + } + }() + res, err := runSql(sqlproc, hnswGenerationSql(tblcfg)) + if err != nil { + return 0, 0, err + } + fp, count = genChecksums(res) + res.Close() + return fp, count, nil +} + +// queryHnswGeneration reads the (checksumFingerprint, modelCount) generation in the BACKGROUND +// (housekeeping goroutine, no live sqlproc) via an executor-managed auto-commit txn, keyed by the +// CN UUID + tenant captured at load. Used by HnswSearch.IsStale. +func queryHnswGeneration(ctx context.Context, cnUUID string, accountID uint32, tblcfg vectorindex.IndexTableConfig) (fp uint64, count int64, err error) { + defer func() { + if r := recover(); r != nil { + fp, count, err = 0, 0, moerr.NewInternalErrorNoCtx(fmt.Sprintf("queryHnswGeneration recovered: %v", r)) + } + }() + res, err := runSqlAutoCommit(ctx, cnUUID, accountID, tblcfg.DbName, hnswGenerationSql(tblcfg)) + if err != nil { + return 0, 0, err + } + fp, count = genChecksums(res) + res.Close() + return fp, count, nil +} diff --git a/pkg/vectorindex/hnsw/plugin/compile/compile.go b/pkg/vectorindex/hnsw/plugin/compile/compile.go index 00a8a42109d63..c0f2ae568a4ef 100644 --- a/pkg/vectorindex/hnsw/plugin/compile/compile.go +++ b/pkg/vectorindex/hnsw/plugin/compile/compile.go @@ -117,18 +117,7 @@ func (Hooks) handleCreate(ctx compileplugin.CompileContext, indexDefs map[string cache.Cache.Remove(storageDef.IndexTableName) - // delete old data first - sqls, err := genDeleteSQL(indexDefs, ctx.QryDatabase()) - if err != nil { - return err - } - for _, sql := range sqls { - if err = ctx.RunSql(sql); err != nil { - return err - } - } - - async, err := catalog.IsIndexAsync(metaDef.IndexAlgoParams) + async, err := catalog.IndexParamAsync(metaDef.IndexAlgoParams) if err != nil { return err } @@ -143,6 +132,13 @@ func (Hooks) handleCreate(ctx compileplugin.CompileContext, indexDefs map[string // otherwise survive at its old watermark and replay historical // events on top of the freshly built state. forceSync (ALTER // REINDEX … FORCE_SYNC) takes this branch even for an async index. + // + // NOTE: the old-data DELETE is NOT issued here — the hnsw_create TVF + // owns clear+rebuild so it can read the pre-rebuild MAX(timestamp) + // BEFORE clearing and floor the new generation strictly above it + // (monotonic across a rebuild under a skewed/backward clock; see + // hnsw.BuildTimestamp / HnswSearch.IsStale). The TVF clears even when + // nothing is built, so a REBUILD to zero docs still empties the index. sqls, err := genBuildSQL(ctx, indexDefs) if err != nil { return err @@ -159,6 +155,18 @@ func (Hooks) handleCreate(ctx compileplugin.CompileContext, indexDefs map[string indexName, sinkerType, true, "", originalTableDef) } + // async: the CDC job rebuilds from the full log, so clear the old index + // data here first (the sync branch above defers this to the TVF). + delsqls, err := genDeleteSQL(indexDefs, ctx.QryDatabase()) + if err != nil { + return err + } + for _, sql := range delsqls { + if err = ctx.RunSql(sql); err != nil { + return err + } + } + // async: drop any existing CDC task, register a new one consuming the // full log from the table's creation timestamp. if err := ctx.DropIndexCdcTask(originalTableDef, ctx.QryDatabase(), originalTableDef.Name, indexName); err != nil { @@ -171,7 +179,7 @@ func (Hooks) handleCreate(ctx compileplugin.CompileContext, indexDefs map[string // HandleReindex: same code path as create, but honors forceSync so an // ALTER REINDEX … FORCE_SYNC (e.g. restore's RestoreTable) rebuilds an // always-async HNSW index synchronously instead of deferring to CDC. -func (h Hooks) HandleReindex(ctx compileplugin.CompileContext, indexDefs map[string]*plan.IndexDef, forceSync bool) error { +func (h Hooks) HandleReindex(ctx compileplugin.CompileContext, indexDefs map[string]*plan.IndexDef, forceSync bool, _ bool) error { return h.handleCreate(ctx, indexDefs, forceSync) } diff --git a/pkg/vectorindex/hnsw/plugin/runtime/runtime.go b/pkg/vectorindex/hnsw/plugin/runtime/runtime.go index 743a29141d3e4..a870fc3f9cef7 100644 --- a/pkg/vectorindex/hnsw/plugin/runtime/runtime.go +++ b/pkg/vectorindex/hnsw/plugin/runtime/runtime.go @@ -90,6 +90,8 @@ func (CatalogHooks) SupportedVectorTypes() []types.T { return []types.T{types.T_array_float32, types.T_array_float64} } +func (CatalogHooks) IsVectorIndex() bool { return true } + // SupportedPrimaryKeyTypes: requires an int64 primary key. func (CatalogHooks) SupportedPrimaryKeyTypes() []types.T { return []types.T{types.T_int64} } diff --git a/pkg/vectorindex/hnsw/search.go b/pkg/vectorindex/hnsw/search.go index 0d7933884bb8d..a22412ae8f308 100644 --- a/pkg/vectorindex/hnsw/search.go +++ b/pkg/vectorindex/hnsw/search.go @@ -20,6 +20,7 @@ import ( "math" "sync" "sync/atomic" + "time" "github.com/matrixorigin/matrixone/pkg/common/concurrent" "github.com/matrixorigin/matrixone/pkg/common/moerr" @@ -27,7 +28,6 @@ import ( "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/container/vector" "github.com/matrixorigin/matrixone/pkg/vectorindex" - "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" "github.com/matrixorigin/matrixone/pkg/vectorindex/metric" "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" ) @@ -44,6 +44,19 @@ type HnswSearch[T types.RealNumbers] struct { Mutex sync.Mutex Cond *sync.Cond ThreadsSearch int64 + + // Generation captured at Load for the cross-CN cache freshness check (IsStale): a CONTENT + // fingerprint over the per-model file checksums (MD5s) plus the model-row count — see + // hnswGenerationSql. loadedFp moves on ANY content change (CDC append/delete rewrites a model + // file, REBUILD/MERGE, or a model emptied) and is clock-independent, so it survives an + // intermediate empty state that a timestamp-based generation could not. cnUUID/accountID re-query + // in the background. genValid=false (capture failed) ⇒ IsStale reports stale so the entry is + // evicted and reloaded. + cnUUID string + accountID uint32 + loadedFp uint64 + loadedCount int64 + genValid bool } func NewHnswSearch[T types.RealNumbers](idxcfg vectorindex.IndexConfig, tblcfg vectorindex.IndexTableConfig) *HnswSearch[T] { @@ -289,9 +302,42 @@ func (s *HnswSearch[T]) Load(sqlproc *sqlexec.SqlProcess) error { s.Indexes = indexes + // Capture the generation + durable handles for IsStale. Same txn as the load, so the captured + // generation matches the loaded snapshot. If capture fails here, genValid stays false and + // IsStale reports the entry as uncheckable-hence-stale (evict + reload to retry capture) + // rather than pinning it forever — see IsStale. + s.cnUUID = sqlproc.GetService() + if acc, e := sqlproc.GetAccountID(); e == nil { + if fp, count, e2 := loadHnswGeneration(sqlproc, s.Tblcfg); e2 == nil { + s.accountID, s.loadedFp, s.loadedCount, s.genValid = acc, fp, count, true + } + } return nil } +// IsStale reports whether the loaded model has fallen behind the persisted index — any content +// change (CDC append/delete rewriting a model file, REBUILD/MERGE, or an emptied model) changes the +// checksum fingerprint / model count — for the VectorIndexCache cross-CN freshness check. +// Runs on the housekeeping goroutine via a background auto-commit txn (cnUUID/accountID captured +// at load). A query error ⇒ (true, err): the index was likely dropped/rebuilt, so reclaim the +// dead entry. No captured generation (capture failed at load, or no service to re-query) ⇒ +// (true, nil): the entry cannot self-check freshness, so evict it to force a reload that retries +// capture. Returning (false, nil) here would pin a hot entry — whose TTL keeps sliding on every +// search — in cache indefinitely, serving pre-CDC/rebuild data forever; the bounded reload (one +// per freshness sweep) self-heals the moment capture succeeds. +func (s *HnswSearch[T]) IsStale() (bool, error) { + if !s.genValid || s.cnUUID == "" { + return true, nil + } + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + fp, count, err := queryHnswGeneration(ctx, s.cnUUID, s.accountID, s.Tblcfg) + if err != nil { + return true, err + } + return fp != s.loadedFp || count != s.loadedCount, nil +} + // check config and update some parameters such as ef_search func (s *HnswSearch[T]) SearchFloat32(proc *sqlexec.SqlProcess, query any, rt vectorindex.RuntimeConfig, outKeys []int64, outDists []float32) error { keys, dists, err := s.Search(proc, query, rt) @@ -317,6 +363,8 @@ func (s *HnswSearch[T]) SearchFloat32(proc *sqlexec.SqlProcess, query any, rt ve return nil } -func (s *HnswSearch[T]) UpdateConfig(newalgo cache.VectorIndexSearchIf) error { - return nil +// SearchInto is not yet implemented for this algo (box-free LIMIT path); it will migrate +// from the []any Search per the SearchOutput plan. Mirrors fulltext2's SearchFloat32 stub. +func (s *HnswSearch[T]) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return moerr.NewInternalErrorNoCtx("SearchInto not supported") } diff --git a/pkg/vectorindex/hnsw/sync.go b/pkg/vectorindex/hnsw/sync.go index 37b34fea601a2..45a3a28ce02ad 100644 --- a/pkg/vectorindex/hnsw/sync.go +++ b/pkg/vectorindex/hnsw/sync.go @@ -185,8 +185,11 @@ func NewHnswSync[T types.RealNumbers](sqlproc *sqlexec.SqlProcess, // assume CDC run in single thread // model id for CDC is cdc:1:0:timestamp uid := fmt.Sprintf("%s:%d:%d", "cdc", 1, 0) - ts := time.Now().Unix() - sync := &HnswSync[T]{indexes: indexes, idxcfg: idxcfg, tblcfg: idxtblcfg, uid: uid, ts: ts, idxname: idxname} + sync := &HnswSync[T]{indexes: indexes, idxcfg: idxcfg, tblcfg: idxtblcfg, uid: uid, idxname: idxname} + // Monotonic metadata timestamp (the cross-CN cache-freshness generation) — strictly greater + // than every existing model row, not a bare wall-clock value. See nextTimestamp. (Save + // recomputes it just before writing; this initial value keeps the uid:ts model id unique.) + sync.ts = sync.nextTimestamp() // save all model to local by LoadIndex and Unload err = sync.DownloadAll(sqlproc) @@ -579,9 +582,32 @@ func (s *HnswSync[T]) Update(sqlproc *sqlexec.SqlProcess, cdc *vectorindex.Vecto return nil } +// nextTimestamp allocates a metadata timestamp strictly greater than every existing model row. +// The metadata timestamp is the cross-CN cache-freshness generation (MAX(timestamp), see +// cachegen), so it must be MONOTONIC — not merely microsecond-resolution wall-clock. time.Now() +// alone is not enough: CREATE and CDC may run on different CNs with clock skew, or a local clock +// may step backward, in which case a fresh wall-clock value could be <= an unchanged model row's +// timestamp; since CDC only replaces the dirty model, MAX(timestamp) would then stay == the +// value a warm remote CN loaded, and IsStale would miss the update. Allocating above the current +// max enforces the ordering regardless of the clock. Correct because CDC is single-writer per +// index (ISCP job ownership): s.indexes (loaded at NewHnswSync) reflects the current persisted +// max and no concurrent writer can insert between this read and the write. +func (s *HnswSync[T]) nextTimestamp() int64 { + var maxTs int64 + for _, m := range s.indexes { + if m != nil && m.Timestamp > maxTs { + maxTs = m.Timestamp + } + } + if now := time.Now().UnixMicro(); now > maxTs { + return now + } + return maxTs + 1 +} + func (s *HnswSync[T]) Save(sqlproc *sqlexec.SqlProcess) error { // save to files and then save to database - s.ts = time.Now().Unix() + s.ts = s.nextTimestamp() sqls, err := s.ToSql(s.ts) if err != nil { return err diff --git a/pkg/vectorindex/idxcron/executor.go b/pkg/vectorindex/idxcron/executor.go index 03ca5f3f94c44..ab95aeab7d88b 100644 --- a/pkg/vectorindex/idxcron/executor.go +++ b/pkg/vectorindex/idxcron/executor.go @@ -17,6 +17,8 @@ package idxcron import ( "context" "fmt" + "os" + "strconv" "strings" "sync/atomic" "time" @@ -339,7 +341,12 @@ func runReindex(ctx context.Context, resolveVariableFunc := (func(string, bool, bool) (any, error))(nil) if task.Metadata != nil { - resolveVariableFunc = task.Metadata.ResolveVariableFunc + // Use the session-defaulting resolver: the reindex compile/exec resolves + // session/system vars (e.g. sql_mode, wired into the write-policy check by + // #25438) that the captured Metadata never holds. Without this, the rebuild + // INSERT aborts every idxcron reindex with "key sql_mode not found". Algo + // vars stay strict — an uncaptured one still errors. See metadata.go. + resolveVariableFunc = task.Metadata.ResolveVariableWithSessionDefaults } err = runTxnWithSqlContext(ctx, txnEngine, txnClient, cnUUID, @@ -394,10 +401,56 @@ func runReindex(ctx context.Context, return err2 } } + + // SECOND is a sub-day cadence override: when set (> 0), the interval + // is that many seconds and the hour-of-day gate is bypassed (matched to + // currentHour) since it is meaningless at second granularity. Lets an + // index set a short interval in DDL (auto_update=true second=5) without + // the MO_IDXCRON_INTERVAL_SEC env override. + secondAst, err2 := sonic.Get([]byte(idx.IndexAlgoParams), catalog.Second) + if err2 == nil { + second, err2 := secondAst.Int64() + if err2 != nil { + return err2 + } + if second > 0 { + interval = time.Duration(second) * time.Second + hour = int64(currentHour) + } + } + + // Prefer the index's persisted algo_params.session_vars over the + // idxcron task's Capture blob (mo_index_update.metadata → task.Metadata, + // wired as the resolver above). session_vars is the mechanism the rest of + // the system (ISCP async build, clone, ALTER restore) already reads, and + // idxcron is migrating to it. This is read here, inside getTableDef's + // account context, so account scoping is correct (a global JOIN in the + // task-listing query would miss non-system accounts). No per-algo switch: + // any index that populates session_vars uses it; ivfflat (which does not) + // transparently keeps its Capture-based resolver, preserving legacy behavior. + if sv, sverr := catalog.IndexParamsSessionVars(idx.IndexAlgoParams); sverr == nil && len(sv) > 0 { + if sessionMeta, merr := sqlexec.NewMetadataFromJson(string(sv)); merr == nil { + sqlproc.SqlCtx.SetResolveVariableFunc(sessionMeta.ResolveVariableWithSessionDefaults) + } else { + logutil.Errorf("[idxcron] index=%s: algo_params.session_vars parse failed, falling back to Capture: %v", task.IndexName, merr) + } + } break } + // dev/test fast mode: a short interval + bypass the hour-of-day gate so a + // freshly-created index can be observed rebuilding within seconds. + if idxcronFastIntervalSec > 0 { + interval = time.Duration(idxcronFastIntervalSec) * time.Second + hour = int64(currentHour) + } + + logutil.Infof("[idxcron] eval action=%s db=%s table=%s index=%s: auto_update=%v interval=%v hour=%d currentHour=%d", + task.Action, task.DbName, task.TableName, task.IndexName, auto_update, interval, hour, currentHour) + if !auto_update || interval == 0 || currentHour != int(hour) { + logutil.Infof("[idxcron] skip index=%s: gate (auto_update=%v interval=%v hour=%d vs currentHour=%d)", + task.IndexName, auto_update, interval, hour, currentHour) reason = Reason_Skipped return } @@ -414,7 +467,7 @@ func runReindex(ctx context.Context, return } - ok, reason2, err2 := p.Idxcron().Updatable(idxcronplugin.UpdatableInput{ + upIn := idxcronplugin.UpdatableInput{ Sqlproc: sqlproc, TableDef: tableDef, IndexName: task.IndexName, @@ -422,23 +475,39 @@ func runReindex(ctx context.Context, CreatedAt: task.CreatedAt, LastUpdateAt: task.LastUpdateAt, Interval: interval, - }) + } + ok, reason2, err2 := p.Idxcron().Updatable(upIn) if err2 != nil { return } reason = reason2 if !ok { + logutil.Infof("[idxcron] skip index=%s: Updatable=false reason=%q", task.IndexName, reason2) return } - // run alter table alter reindex in force synchronous mode to make sure to build index in single transaction - sql := fmt.Sprintf("ALTER TABLE `%s`.`%s` ALTER REINDEX `%s` %s FORCE_SYNC", - task.DbName, task.TableName, task.IndexName, d.IdxcronAlgoToken) + // The reindex option is the descriptor's fixed value unless the plugin implements + // ReindexOptioner and picks one per fire (fulltext: MERGE vs REBUILD by dead fraction). + reindexOption := d.IdxcronReindexOption + if optioner, ok := p.Idxcron().(idxcronplugin.ReindexOptioner); ok { + opt, oerr := optioner.ReindexOption(upIn) + if oerr != nil { + err2 = oerr // closure's named return — NOT the outer err (clobbered by runTxn) + return + } + reindexOption = opt + } + + // run alter table alter reindex in force synchronous mode to make sure to build index in single transaction. + sql := buildReindexSql(task.DbName, task.TableName, task.IndexName, d.IdxcronAlgoToken, reindexOption) + logutil.Infof("[idxcron] reindex FIRING index=%s: %s", task.IndexName, sql) res, err2 := runReindexSql(sqlproc, sql) if err2 != nil { + logutil.Errorf("[idxcron] reindex FAILED index=%s: %v", task.IndexName, err2) return } res.Close() + logutil.Infof("[idxcron] reindex DONE index=%s", task.IndexName) // mark reindex is performed updated = true @@ -450,17 +519,23 @@ func runReindex(ctx context.Context, func (e *IndexUpdateTaskExecutor) run(ctx context.Context) (err error) { - logutil.Infof("IndexUpdateTaskExecutor START") currentHour := time.Now().Hour() + // tick-level trace: one line per cron fire, echoing the schedule + fast-mode overrides + // so a short-interval test deploy can confirm the executor is actually ticking (and how + // often) before the per-index eval/skip/FIRING lines below. + logutil.Infof("[idxcron] tick START currentHour=%d cronExpr=%q fastIntervalSec=%d", + currentHour, IndexUpdateTaskCronExpr, idxcronFastIntervalSec) + var fired, skipped int defer func() { - logutil.Infof("IndexUpdateTaskExecutor END") + logutil.Infof("[idxcron] tick END fired=%d skipped=%d err=%v", fired, skipped, err) }() tasks, err := getTasks(ctx, e.txnEngine, e.cnTxnClient, e.cnUUID) if err != nil { return err } + logutil.Infof("[idxcron] tick: %d index-update task(s) to evaluate", len(tasks)) // do the maintenance such as ivfflat re-index, fulltext batch_delete for _, t := range tasks { @@ -483,8 +558,12 @@ func (e *IndexUpdateTaskExecutor) run(ctx context.Context) (err error) { updated, reason, err2 = runReindex(ctx, e.txnEngine, e.cnTxnClient, e.cnUUID, t, currentHour, p) } + if updated { + fired++ + } if !updated && reason == Reason_Skipped { // skip save Status when update was skipped + skipped++ continue } @@ -499,11 +578,59 @@ func (e *IndexUpdateTaskExecutor) run(ctx context.Context) (err error) { return nil } -var IndexUpdateTaskCronExpr = "0 0 * * * *" // run once an hour, beginning of hour -// var IndexUpdateTaskCronExpr = "0 55 23 * * *" // 23:55:00 everyday +// IndexUpdateTaskCronExpr — how often the executor ticks. Default hourly; a dev/test +// deploy can lower it via env MO_IDXCRON_CRON (e.g. "*/20 * * * * *" every 20s) to +// observe scheduled rebuilds without waiting for the top of the hour. Read once at +// init; the cron task is registered from this value at bootstrap (predefine.go). +var IndexUpdateTaskCronExpr = func() string { + // MO_IDXCRON_TICK_SEC is an ergonomic seconds-interval knob for the bootstrap + // tick, translated to a cron descriptor (the parser enables cron.Descriptor). + // e.g. MO_IDXCRON_TICK_SEC=10 -> "@every 10s". Takes precedence over + // MO_IDXCRON_CRON so a deploy can change how often the executor checks index + // tasks without hand-writing a cron expression. Like MO_IDXCRON_CRON it is read + // once and baked into the cron task at first bootstrap (predefine.go), so it + // only takes effect on a fresh cluster — an existing cluster keeps its + // registered expr. The default stays conservative (hourly): every tick lists + // the registered index tasks (a small SQL), so a very short interval adds + // background load and should be an explicit opt-in. + if v := os.Getenv("MO_IDXCRON_TICK_SEC"); v != "" { + if n, err := strconv.ParseInt(v, 10, 64); err == nil && n > 0 { + return fmt.Sprintf("@every %ds", n) + } + } + if v := os.Getenv("MO_IDXCRON_CRON"); v != "" { + return v + } + return "0 0 * * * *" // run once an hour, beginning of hour +}() + +// buildReindexSql assembles the cron-triggered reindex statement. algoToken is the +// per-plugin keyword (SyncDescriptor.IdxcronAlgoToken, e.g. "IVFFLAT"/"FULLTEXT"); +// reindexOption is an optional extra keyword inserted before FORCE_SYNC +// (SyncDescriptor.IdxcronReindexOption, e.g. "MERGE" so fulltext retrieval runs +// incremental fold+tiered compaction), omitted when empty. FORCE_SYNC always runs the +// rebuild synchronously inside the txn. +func buildReindexSql(dbName, tableName, indexName, algoToken, reindexOption string) string { + reindexOpt := "" + if reindexOption != "" { + reindexOpt = " " + reindexOption + } + return fmt.Sprintf("ALTER TABLE `%s`.`%s` ALTER REINDEX `%s` %s%s FORCE_SYNC", + dbName, tableName, indexName, algoToken, reindexOpt) +} -// var IndexUpdateTaskCronExpr = "0 */5 * * * *" // every 5 minutes -// var IndexUpdateTaskCronExpr = "*/15 * * * * *" // every 15 seconds +// idxcronFastIntervalSec — dev/test override (env MO_IDXCRON_INTERVAL_SEC, in seconds). +// When > 0, runReindex uses it as the cadence interval AND bypasses the hour-of-day +// gate, so a freshly-created index can be observed rebuilding within seconds instead of +// waiting a day (the DAY param's minimum) at a specific HOUR. Unset (0) in production. +var idxcronFastIntervalSec = func() int64 { + if v := os.Getenv("MO_IDXCRON_INTERVAL_SEC"); v != "" { + if n, err := strconv.ParseInt(v, 10, 64); err == nil && n > 0 { + return n + } + } + return 0 +}() const ParamSeparator = " " diff --git a/pkg/vectorindex/index.go b/pkg/vectorindex/index.go index c3d615ec27c63..df879b248a759 100644 --- a/pkg/vectorindex/index.go +++ b/pkg/vectorindex/index.go @@ -271,6 +271,26 @@ func (h *FastMaxHeap[T, K]) Push(key K, dist T) { } } +// Len reports how many elements the heap currently holds (0..limit). +func (h *FastMaxHeap[T, K]) Len() int { return h.size } + +// Full reports whether the heap has reached its bounded capacity (limit). Once full, +// Push only admits an element whose distance is smaller than the current root. +func (h *FastMaxHeap[T, K]) Full() bool { return h.size >= h.limit } + +// Peek returns the root — the element with the LARGEST distance, i.e. the eviction +// candidate and the bounded-top-k threshold — without removing it. ok is false when the +// heap is empty. Callers use this for early-termination / pruning against the current +// worst-kept distance. +func (h *FastMaxHeap[T, K]) Peek() (K, T, bool) { + if h.size == 0 { + var zeroK K + var zeroT T + return zeroK, zeroT, false + } + return h.keys[0], h.distances[0], true +} + // Pop extracts the element with the largest distance from the max-heap. func (h *FastMaxHeap[T, K]) Pop() (K, T, bool) { if h.size == 0 { diff --git a/pkg/vectorindex/ivfflat/plugin/compile/compile.go b/pkg/vectorindex/ivfflat/plugin/compile/compile.go index 57f441605a46a..cc99f533c48b6 100644 --- a/pkg/vectorindex/ivfflat/plugin/compile/compile.go +++ b/pkg/vectorindex/ivfflat/plugin/compile/compile.go @@ -68,7 +68,7 @@ func (h Hooks) HandleCreateIndex(ctx compileplugin.CompileContext, indexDefs map // HandleReindex runs the same body as HandleCreateIndex with forceSync // threaded into centroid building. Matches ddl.go:980-987 dispatch. -func (h Hooks) HandleReindex(ctx compileplugin.CompileContext, indexDefs map[string]*plan.IndexDef, forceSync bool) error { +func (h Hooks) HandleReindex(ctx compileplugin.CompileContext, indexDefs map[string]*plan.IndexDef, forceSync bool, _ bool) error { return runCreateOrReindex(ctx, indexDefs, forceSync) } @@ -203,7 +203,7 @@ func runCreateOrReindex(ctx compileplugin.CompileContext, indexDefs map[string]* return nil } - async, err := catalog.IsIndexAsync(metaDef.IndexAlgoParams) + async, err := catalog.IndexParamAsync(metaDef.IndexAlgoParams) if err != nil { return err } @@ -400,7 +400,7 @@ func ivfIndexCentroidsTable( sqlquote.EscapeString(indexDef.IndexAlgoParams), sqlquote.EscapeString(string(cfgbytes))) } - async, err := catalog.IsIndexAsync(indexDef.IndexAlgoParams) + async, err := catalog.IndexParamAsync(indexDef.IndexAlgoParams) if err != nil { return err } diff --git a/pkg/vectorindex/ivfflat/plugin/runtime/runtime.go b/pkg/vectorindex/ivfflat/plugin/runtime/runtime.go index dfd97f1464e8f..bd3b3f951041c 100644 --- a/pkg/vectorindex/ivfflat/plugin/runtime/runtime.go +++ b/pkg/vectorindex/ivfflat/plugin/runtime/runtime.go @@ -135,6 +135,8 @@ func (CatalogHooks) SupportedVectorTypes() []types.T { } } +func (CatalogHooks) IsVectorIndex() bool { return true } + // SupportedPrimaryKeyTypes: IVF-FLAT imposes no PK-type constraint — the // primary key may be any type. nil = "no constraint". func (CatalogHooks) SupportedPrimaryKeyTypes() []types.T { return nil } @@ -281,6 +283,9 @@ func (CatalogHooks) ParamsFromTree(idx *tree.Index) (map[string]string, error) { if idx.IndexOption.Hour > 0 { res[catalog.Hour] = strconv.FormatInt(idx.IndexOption.Hour, 10) } + if idx.IndexOption.Second > 0 { + res[catalog.Second] = strconv.FormatInt(idx.IndexOption.Second, 10) + } if idx.IndexOption.KmeansTrainPercent > 0 { res[catalog.IndexAlgoParamKmeansTrainPercent] = strconv.FormatInt(idx.IndexOption.KmeansTrainPercent, 10) diff --git a/pkg/vectorindex/ivfflat/search.go b/pkg/vectorindex/ivfflat/search.go index ee3d7ac0700b8..ee7fa71ece8b8 100644 --- a/pkg/vectorindex/ivfflat/search.go +++ b/pkg/vectorindex/ivfflat/search.go @@ -1031,6 +1031,8 @@ func (s *IvfflatSearch[T]) SearchFloat32(proc *sqlexec.SqlProcess, query any, rt return nil } -func (s *IvfflatSearch[T]) UpdateConfig(newalgo cache.VectorIndexSearchIf) error { - return nil +// SearchInto is not yet implemented for this algo (box-free LIMIT path); it will migrate +// from the []any Search per the SearchOutput plan. Mirrors fulltext2's SearchFloat32 stub. +func (s *IvfflatSearch[T]) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return moerr.NewInternalErrorNoCtx("SearchInto not supported") } diff --git a/pkg/vectorindex/ivfpq/plugin/compile/compile.go b/pkg/vectorindex/ivfpq/plugin/compile/compile.go index 6d9f0b56383ad..d45056557b5dd 100644 --- a/pkg/vectorindex/ivfpq/plugin/compile/compile.go +++ b/pkg/vectorindex/ivfpq/plugin/compile/compile.go @@ -95,7 +95,7 @@ type Hooks struct{} // ivfpq_create table-function builder in pkg/sql/plan/ivfpq.go. // // The sync-vs-async branch is driven by the index's `async` -// IndexAlgoParam (catalog.IsIndexAsync). Default (key missing or +// IndexAlgoParam (catalog.IndexParamAsync). Default (key missing or // "false"): forceSync=true — ivfpq_create runs inline before the CDC // task is registered. Explicit async="true": forceSync=false — the // build SQL is stashed as ConsumerInfo.InitSQL and runs at the first @@ -108,7 +108,7 @@ func (h Hooks) HandleCreateIndex(ctx compileplugin.CompileContext, indexDefs map if !ok || metaDef == nil { return h.handleCreate(ctx, indexDefs, true) } - async, err := catalog.IsIndexAsync(metaDef.IndexAlgoParams) + async, err := catalog.IndexParamAsync(metaDef.IndexAlgoParams) if err != nil { return err } @@ -120,7 +120,7 @@ func (h Hooks) HandleCreateIndex(ctx compileplugin.CompileContext, indexDefs map // branch builds ivfpq_create synchronously inside the txn so the new // tag=0 model lands before subsequent steps observe the index — mirrors // IVF-FLAT and CAGRA. -func (h Hooks) HandleReindex(ctx compileplugin.CompileContext, indexDefs map[string]*plan.IndexDef, forceSync bool) error { +func (h Hooks) HandleReindex(ctx compileplugin.CompileContext, indexDefs map[string]*plan.IndexDef, forceSync bool, _ bool) error { return h.handleCreate(ctx, indexDefs, forceSync) } diff --git a/pkg/vectorindex/ivfpq/plugin/runtime/runtime.go b/pkg/vectorindex/ivfpq/plugin/runtime/runtime.go index 61b5b6633a643..cd47f059f67c9 100644 --- a/pkg/vectorindex/ivfpq/plugin/runtime/runtime.go +++ b/pkg/vectorindex/ivfpq/plugin/runtime/runtime.go @@ -133,6 +133,8 @@ func (CatalogHooks) SupportedVectorTypes() []types.T { return []types.T{types.T_array_float32, types.T_array_float16} } +func (CatalogHooks) IsVectorIndex() bool { return true } + // SupportedPrimaryKeyTypes: requires an int64 primary key. func (CatalogHooks) SupportedPrimaryKeyTypes() []types.T { return []types.T{types.T_int64} } @@ -265,6 +267,9 @@ func (CatalogHooks) ParamsFromTree(idx *tree.Index) (map[string]string, error) { if idx.IndexOption.Hour > 0 { res[catalog.Hour] = strconv.FormatInt(idx.IndexOption.Hour, 10) } + if idx.IndexOption.Second > 0 { + res[catalog.Second] = strconv.FormatInt(idx.IndexOption.Second, 10) + } if idx.IndexOption.KmeansTrainPercent > 0 { res[catalog.IndexAlgoParamKmeansTrainPercent] = strconv.FormatInt(idx.IndexOption.KmeansTrainPercent, 10) diff --git a/pkg/vectorindex/ivfpq/search_gpu.go b/pkg/vectorindex/ivfpq/search_gpu.go index 3eb2af627eded..5b828f70667c8 100644 --- a/pkg/vectorindex/ivfpq/search_gpu.go +++ b/pkg/vectorindex/ivfpq/search_gpu.go @@ -17,12 +17,14 @@ package ivfpq import ( + "context" "math" + "time" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/cuvs" "github.com/matrixorigin/matrixone/pkg/vectorindex" - "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" + "github.com/matrixorigin/matrixone/pkg/vectorindex/cachegen" cuvscdc "github.com/matrixorigin/matrixone/pkg/vectorindex/cuvs" "github.com/matrixorigin/matrixone/pkg/vectorindex/metric" "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" @@ -37,6 +39,15 @@ type IvfpqSearch[B, Q cuvs.VectorType] struct { Overflow cuvs.BruteForceOverflow[B] // CDC insert overflow; nil when no overflow records exist Devices []int ThreadsSearch int64 + + // Generation captured at Load for the cross-CN cache freshness check (IsStale): a + // REBUILD/MERGE bumps MAX(metadata.timestamp); a CDC append bumps the (CdcTailId,tag=1) + // tail chunk_id. cnUUID/accountID re-query in the background. genValid=false ⇒ no-op. + cnUUID string + accountID uint32 + loadedTs int64 + loadedTail int64 + genValid bool } func NewIvfpqSearch[B, Q cuvs.VectorType](idxcfg vectorindex.IndexConfig, tblcfg vectorindex.IndexTableConfig, devices []int) *IvfpqSearch[B, Q] { @@ -170,7 +181,39 @@ func (s *IvfpqSearch[B, Q]) Load(sqlproc *sqlexec.SqlProcess) (err error) { return err } s.MultiIndex, err = s.buildMultiIndex() - return err + if err != nil { + return err + } + // Capture the generation + durable handles for IsStale (same txn as the load). On capture + // failure genValid stays false and IsStale reports the entry as uncheckable-hence-stale + // (evict + reload to retry capture) rather than pinning it forever — see IsStale. + s.cnUUID = sqlproc.GetService() + if acc, e := sqlproc.GetAccountID(); e == nil { + if ts, tail, e2 := cachegen.LoadCdcGeneration(sqlproc, s.Tblcfg); e2 == nil { + s.accountID, s.loadedTs, s.loadedTail, s.genValid = acc, ts, tail, true + } + } + return nil +} + +// IsStale reports whether the loaded index has fallen behind the persisted one (REBUILD bumps +// the metadata timestamp; a CDC append bumps the tag=1 tail chunk_id), for the VectorIndexCache +// cross-CN freshness check. Runs on the housekeeping goroutine via a background auto-commit txn. +// A query error ⇒ (true, err): the index was likely dropped/rebuilt, reclaim the dead entry. +// No captured generation (capture failed at load, or no service to re-query) ⇒ (true, nil): the +// entry cannot self-check freshness, so evict it to force a reload that retries capture, rather +// than pinning a hot entry (whose TTL keeps sliding on every search) to serve stale data forever. +func (s *IvfpqSearch[B, Q]) IsStale() (bool, error) { + if !s.genValid || s.cnUUID == "" { + return true, nil + } + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + ts, tail, err := cachegen.QueryCdcGeneration(ctx, s.cnUUID, s.accountID, s.Tblcfg) + if err != nil { + return true, err + } + return ts != s.loadedTs || tail != s.loadedTail, nil } // loadCdcTail mirrors cagra.CagraSearch.loadCdcTail — see that for the @@ -433,7 +476,8 @@ func (s *IvfpqSearch[B, Q]) Destroy() { s.Indexes = nil } -// UpdateConfig implements cache.VectorIndexSearchIf. -func (s *IvfpqSearch[B, Q]) UpdateConfig(newalgo cache.VectorIndexSearchIf) error { - return nil +// SearchInto is not yet implemented for this algo (box-free LIMIT path); it will migrate +// from the []any Search per the SearchOutput plan. Mirrors fulltext2's SearchFloat32 stub. +func (s *IvfpqSearch[B, Q]) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return moerr.NewInternalErrorNoCtx("SearchInto not supported") } diff --git a/pkg/vectorindex/sqlexec/metadata.go b/pkg/vectorindex/sqlexec/metadata.go index 056c0d160c211..c61e688223588 100644 --- a/pkg/vectorindex/sqlexec/metadata.go +++ b/pkg/vectorindex/sqlexec/metadata.go @@ -16,6 +16,7 @@ package sqlexec import ( "fmt" + "strings" "github.com/bytedance/sonic" "github.com/matrixorigin/matrixone/pkg/common/moerr" @@ -116,6 +117,87 @@ func (m *Metadata) ResolveVariableFunc(varName string, isSystemVar, isGlobalVar return nil, moerr.NewInternalErrorNoCtx("invalid configuration type") } +// sessionSystemVarDefaults is the authoritative enumeration of the session/system +// variables a background reindex resolves that its captured Metadata does NOT +// hold, each mapped to the correct background default. It is deliberately an +// explicit whitelist, not a catch-all: a var that is neither captured nor listed +// here fails fast (see ResolveVariableWithSessionDefaults) so a newly-plumbed +// dependency surfaces loudly, by name, and gets a deliberate default rather than +// silently resolving to nil. +// +// - sql_mode: "" — #25438 wired it into the zero-temporal-date write-policy +// check (process/eval_expr_util.RejectZeroTemporalWritePolicy). "" (no strict / +// no zero-date modes) is the correct permissive value for a background reindex, +// matching backSession.GetSessionSysVar, and safer than capturing the user's +// real (possibly strict) sql_mode, which could make the rebuild reject rows +// already living in the base table. +// - lock_wait_timeout: nil — its callers (lockop, process_codec) fall back to +// their own context-aware default (txnTimeout / procSessionLockWaitTimeout) on +// nil, which a fixed value would wrongly override; this matches backSession, +// which returns nil for it too. +var sessionSystemVarDefaults = map[string]any{ + "sql_mode": "", + "lock_wait_timeout": nil, +} + +// captured reports whether varName was written into this blob's cfg — i.e. it is +// one of the algo build knobs snapshotted at CREATE. An uncaptured name is a +// session/system var the Metadata was never meant to hold. +func (m *Metadata) captured(varName string) bool { + if m.bj.IsNull() { + return false + } + path, err := bytejson.ParseJsonPath("$.cfg." + varName) + if err != nil { + return false + } + out := m.bj.QuerySimple([]*bytejson.Path{&path}) + return !out.IsNull() +} + +// ResolveVariableWithSessionDefaults is the ResolveVariableFunc to install for a +// background job that runs through a captured Metadata (the idxcron reindex +// hook). A background reindex resolves session/system vars its capture blob never +// held — sql_mode, lock_wait_timeout, and whatever the next release plumbs in — +// and the strict ResolveVariableFunc would error "key X not found" on every one, +// aborting the reindex the moment a non-graceful caller (like the sql_mode +// write-policy check) hits it. +// +// Resolution order: +// 1. CAPTURED var → resolve strictly; a malformed algo knob still errors, so a +// genuine build-config bug surfaces loudly. +// 2. Known session var (sessionSystemVarDefaults) → its background default. +// 3. Otherwise FAIL FAST, naming the var. This is deliberate: rather than +// silently defaulting an un-enumerated var to nil, we force a newly-plumbed +// session-var dependency to surface with a clear reason so it gets added to +// sessionSystemVarDefaults with a correct, deliberate default. +func (m *Metadata) ResolveVariableWithSessionDefaults(varName string, isSystemVar, isGlobalVar bool) (any, error) { + if m.captured(varName) { + return m.ResolveVariableFunc(varName, isSystemVar, isGlobalVar) + } + if def, ok := sessionSystemVarDefaults[strings.ToLower(varName)]; ok { + return def, nil + } + return nil, moerr.NewInternalErrorNoCtx(fmt.Sprintf( + "idxcron reindex resolved un-enumerated session variable %q: add it to "+ + "sqlexec.sessionSystemVarDefaults with the correct background default", + varName)) +} + +// ResolveVariableSoft resolves a captured var and returns (nil, nil) for any var +// the blob does not hold — no error, no log. It is for consumers that treat +// "absent" as "use the caller's own default" and must NOT emit spurious error +// logs, e.g. indexplugin.AlgoParamInt (flat key → session var → default), which +// already handles nil gracefully. This is deliberately distinct from +// ResolveVariableWithSessionDefaults (the idxcron reindex resolver), which +// fail-fasts on an un-enumerated var to surface new session-var dependencies. +func (m *Metadata) ResolveVariableSoft(varName string, isSystemVar, isGlobalVar bool) (any, error) { + if m.captured(varName) { + return m.ResolveVariableFunc(varName, isSystemVar, isGlobalVar) + } + return nil, nil +} + func (m *Metadata) Modify(varName string, v any) error { if m.bj.IsNull() { diff --git a/pkg/vectorindex/sqlexec/sqlexec.go b/pkg/vectorindex/sqlexec/sqlexec.go index 302c7facae347..7932c1b9927a5 100644 --- a/pkg/vectorindex/sqlexec/sqlexec.go +++ b/pkg/vectorindex/sqlexec/sqlexec.go @@ -19,6 +19,7 @@ import ( "errors" "time" + "github.com/matrixorigin/matrixone/pkg/common/moerr" moruntime "github.com/matrixorigin/matrixone/pkg/common/runtime" "github.com/matrixorigin/matrixone/pkg/defines" "github.com/matrixorigin/matrixone/pkg/pb/plan" @@ -125,6 +126,24 @@ func (s *SqlProcess) GetResolveVariableFunc() func(varName string, isSystemVar, return nil } +// GetService returns the CN UUID (service name) of the underlying proc or SqlContext. +// Captured at load so a later background query (e.g. the cache IsStale check) can reach +// the internal SQL executor by service name. +func (s *SqlProcess) GetService() string { + if s.Proc != nil { + return s.Proc.GetService() + } + return s.SqlCtx.GetService() +} + +// GetAccountID returns the tenant account id of the underlying proc or SqlContext. +func (s *SqlProcess) GetAccountID() (uint32, error) { + if s.Proc != nil { + return defines.GetAccountId(s.Proc.Ctx) + } + return s.SqlCtx.AccountId, nil +} + // run SQL in batch mode. Result batches will stored in memory and return once all result batches received. func RunSql(sqlproc *SqlProcess, sql string) (executor.Result, error) { if sqlproc.Proc != nil { @@ -192,6 +211,30 @@ func RunSql(sqlproc *SqlProcess, sql string) (executor.Result, error) { } } +// RunSqlAutoCommit runs a read-only SQL in a BACKGROUND context with an executor-managed +// (auto-commit) txn — no caller sqlproc/txn required, only the CN UUID + tenant. The +// internal SQL executor holds the engine/txnClient, so omitting WithTxn makes it +// create+commit its own txn (Options.ExistsTxn()==false). Used by the vector-index cache's +// IsStale freshness check, which runs on the housekeeping goroutine long after the loading +// query's txn is gone; it captures cnUUID+accountID at load and re-queries here. The caller +// owns ctx (deadline/cancel) and must Close the returned Result. +func RunSqlAutoCommit(ctx context.Context, cnUUID string, accountID uint32, db, sql string) (executor.Result, error) { + v, ok := moruntime.ServiceRuntime(cnUUID).GetGlobalVariables(moruntime.InternalSQLExecutor) + if !ok { + return executor.Result{}, moerr.NewInternalErrorNoCtx("RunSqlAutoCommit: missing internal sql executor") + } + exec := v.(executor.SQLExecutor) + ctx = context.WithValue(ctx, defines.TenantIDKey{}, accountID) + opts := executor.Options{}. + WithDisableIncrStatement(). + WithAccountID(accountID). + WithStatementOption(executor.StatementOption{}.WithDisableLog()) + if db != "" { + opts = opts.WithDatabase(db) + } + return exec.Exec(ctx, sql, opts) +} + // run SQL in WithStreaming() and pass the channel to SQL executor func RunStreamingSql( ctx context.Context, diff --git a/pkg/vectorindex/types.go b/pkg/vectorindex/types.go index 3eef81ffaf5a3..d86128c5f267a 100644 --- a/pkg/vectorindex/types.go +++ b/pkg/vectorindex/types.go @@ -269,6 +269,24 @@ type RuntimeConfig struct { // consumed by the C++ eval_filter_bitmap_cpu. FilterJSON string + // Emit, when non-nil, requests a STREAMING search: instead of returning all + // results at once, the index yields them in bounded batches by calling Emit once + // per batch with a *SearchOutput (Search then returns empty keys/distances). Only + // the fulltext2 index honors it, and only for the no-LIMIT case (return every + // matching doc, ranked by an upstream ORDER BY) — so it walks and streams without a + // top-K heap. Other algorithms ignore this field. + // + // Emit hands the consumer the SAME box-free container SearchInto fills (see + // SearchOutput), so the two result paths share one consumer. Ownership differs by + // path: SearchInto's out is caller-OWNED and reused across queries; an Emit batch is + // PRODUCER-owned per flush — its out.Keys is a pooled ColumnBuffer the consumer must + // recycle (PutColumnBuffer) once it has copied the batch out, and out.Include/out.Dists + // are per-batch and dropped. out.Dists is float32 (matching the T_float32 score column), + // out.Include is column-major (one nullable ColumnBuffer per FULL index INCLUDE column, + // segment order) or empty when the caller requested no include columns — so a + // million-row stream boxes nothing. + Emit func(out *SearchOutput) error + // Optional raw runtime-filter payload from the build side. IVF search turns // this into either an exact-pk filter or a membership filter for entries. RuntimeFilterData []byte @@ -298,6 +316,28 @@ type IvfIncludeResult struct { Nulls map[string][]bool } +// SearchOutput is the box-free result container shared by BOTH fulltext2 result paths: +// the pull path (LIMIT) fills a caller-owned one via VectorIndexSearchIf.SearchInto, and +// the push path (no-LIMIT streaming) hands one per batch through RuntimeConfig.Emit. One +// shape, one consumer. Fields (len(Dists) == Keys.N on both paths): +// - Keys: the pk column, box-free for EVERY pk type (unlike SearchFloat32's []int64). +// - Dists: scores aligned to Keys (float32 matches the T_float32 score column). +// - Include: one nullable ColumnBuffer per FULL index INCLUDE column (segment order), or +// empty when rt.RequestedIncludeColumns is empty; the TVF maps its projected columns to +// segment positions the same way on both paths. +// +// Two ownership modes: +// - SearchInto (pull): the caller pools the SearchOutput and its buffers and Resets them +// per query, so a warm LIMIT query allocates nothing for its results. SearchInto Resets +// before filling. +// - Emit (push): each batch's SearchOutput is producer-owned; its Keys is a pooled +// ColumnBuffer the consumer must recycle after copying, and Dists/Include are per-batch. +type SearchOutput struct { + Keys *ColumnBuffer + Dists []float32 + Include []*ColumnBuffer +} + type IvfSearchCursor struct { RankedCentroidIDs []int64 NextBucketOffset uint @@ -306,6 +346,9 @@ type IvfSearchCursor struct { Exhausted bool } +// ColumnBuffer (the typed, box-free streaming key batch used by Emit) lives in +// columnbuffer.go. + type VectorIndexCdc[T types.RealNumbers] struct { Data []VectorIndexCdcEntry[T] `json:"cdc"` } From 403a99fb65304e75b82e75793448f0690a871d0e Mon Sep 17 00:00:00 2001 From: Eric Lam Date: Wed, 26 Aug 2026 19:49:19 +0800 Subject: [PATCH 03/17] feat(fulltext2): route DDL DML and planner paths Backport-of: 1d7b6311ce91df0968da435bb405f3d68137c595 --- pkg/sql/plan/apply_indices.go | 4 +- pkg/sql/plan/apply_indices_cagra.go | 2 +- pkg/sql/plan/apply_indices_fulltext.go | 409 +++++++++++++++++++++++- pkg/sql/plan/apply_indices_fulltext2.go | 248 ++++++++++++++ pkg/sql/plan/apply_indices_ivfflat.go | 2 +- pkg/sql/plan/apply_indices_ivfpq.go | 2 +- pkg/sql/plan/bind_insert.go | 5 +- pkg/sql/plan/bind_update.go | 2 +- pkg/sql/plan/build_ddl.go | 69 +++- pkg/sql/plan/build_dml_util.go | 10 +- pkg/sql/plan/build_show_util.go | 48 ++- pkg/sql/plan/build_update.go | 2 +- pkg/sql/plan/filter_predicate.go | 54 +++- pkg/sql/plan/utils.go | 3 + 14 files changed, 804 insertions(+), 56 deletions(-) create mode 100644 pkg/sql/plan/apply_indices_fulltext2.go diff --git a/pkg/sql/plan/apply_indices.go b/pkg/sql/plan/apply_indices.go index 024798fd863cf..c387d5ebf0b3b 100644 --- a/pkg/sql/plan/apply_indices.go +++ b/pkg/sql/plan/apply_indices.go @@ -615,7 +615,7 @@ func (builder *QueryBuilder) applyIndicesForProject(nodeID int32, projNode *plan // get the list of filter that is fulltext_match func filterids, filter_ftidxs := builder.getFullTextMatchFiltersFromScanNode(scanNode) - // apply fulltext indices when fulltext_match exists + // apply the match indices (one unified pass handles a mix of MATCH + BM25) if len(filterids) > 0 { return builder.applyIndicesForAggUsingFullTextIndex(nodeID, projNode, aggNode, scanNode, filterids, filter_ftidxs, colRefCnt, idxColMap) @@ -627,7 +627,7 @@ func (builder *QueryBuilder) applyIndicesForProject(nodeID int32, projNode *plan // get the list of filter that is fulltext_match func filterids, filter_ftidxs := builder.getFullTextMatchFiltersFromScanNode(scanNode) - // apply fulltext indices when fulltext_match exists + // apply the match indices (one unified pass handles a mix of MATCH + BM25) if len(filterids) > 0 || len(projids) > 0 { return builder.applyIndicesForProjectionUsingFullTextIndex(nodeID, projNode, sortNode, scanNode, filterids, filter_ftidxs, projids, proj_ftidxs, colRefCnt, idxColMap) diff --git a/pkg/sql/plan/apply_indices_cagra.go b/pkg/sql/plan/apply_indices_cagra.go index f227027b49a57..cffd2b4c3b7ff 100644 --- a/pkg/sql/plan/apply_indices_cagra.go +++ b/pkg/sql/plan/apply_indices_cagra.go @@ -173,7 +173,7 @@ func (builder *QueryBuilder) applyIndicesForSortUsingCagra(nodeID int32, vecCtx includeCols, len(scanNode.FilterList)) } predsJSON, peeled, residualFilters, err := buildFilterPredicateJSON( - scanNode.FilterList, scanNode, includeCols, pkColName) + scanNode.FilterList, scanNode, includeCols, pkColName, false) if err != nil { return nodeID, err } diff --git a/pkg/sql/plan/apply_indices_fulltext.go b/pkg/sql/plan/apply_indices_fulltext.go index 9830953196374..d5442e4eb171b 100644 --- a/pkg/sql/plan/apply_indices_fulltext.go +++ b/pkg/sql/plan/apply_indices_fulltext.go @@ -76,6 +76,18 @@ func (builder *QueryBuilder) applyIndicesForProjectionUsingFullTextIndex(nodeID internalLimit, internalOffset = nil, nil } + // Covered fast path (Phase 6): a fully-covered SELECT over a single fulltext2 index + // WITH include columns can DROP the base-table JOIN and serve pk/score/include-cols + // straight from the fulltext2_search TVF. Purely additive: when not covered, falls + // through to the existing JOIN path below byte-for-byte. + if handled, herr := builder.tryApplyCoveredFulltext2(nodeID, projNode, sortNode, scanNode, + filterids, filterIndexDefs, projids, projIndexDef, eqmap, + paginationLimit, paginationOffset); herr != nil { + return -1, herr + } else if handled { + return nodeID, nil + } + idxID, filter_node_ids, proj_node_ids, err := builder.applyJoinFullTextIndices(nodeID, projNode, scanNode, internalLimit, internalOffset, filterids, filterIndexDefs, projids, projIndexDef, eqmap, colRefCnt, idxColMap) if err != nil { @@ -252,6 +264,27 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl } } + // fulltext2 INCLUDE/pk prefilter pushdown: when the driving index is a fulltext2 index + // WITH INCLUDE columns, peel the WHERE predicates on those INCLUDE columns (and the pk) + // out of scanNode.FilterList into the ivfpq-aligned predicate JSON, which fulltext2_search + // evaluates against the stored per-doc values inside the WAND walk (bounding the pushed + // LIMIT to the filtered set). Reusing buildFilterPredicateJSON keeps this correctness-safe: + // it peels only NUMERIC + pk predicates exactly and leaves varchar/others residual (so a + // collation-sensitive string filter stays on the existing scan path — no wrong results). + // Removing the peeled predicates lets the pre-filter second-scan below be skipped when no + // residual filter remains. + var ft2PredsJSON string + if len(indexDefs) > 0 && catalog.IsFullText2IndexAlgo(indexDefs[0].IndexAlgo) { + if incCols := indexDefIncludedColumnsBestEffort(indexDefs[0]); len(incCols) > 0 { + pkColName := fulltext2PeelablePkColName(scanNode) // "" for a non-evaluable pk type + preds, serialized, residual, perr := buildFilterPredicateJSON(scanNode.FilterList, scanNode, incCols, pkColName, true) + if perr == nil && len(serialized) > 0 { + ft2PredsJSON = preds + scanNode.FilterList = residual + } + } + } + // A single fulltext stream can safely keep LIMIT+OFFSET candidates. With // multiple streams, limiting each input before their intersection can drop // documents that belong to the final top page, so leave those inputs @@ -280,24 +313,57 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl } mode := modeLit.GetI64Val() - sql := "" - if patternLit := fn.Args[0].GetLit(); patternLit != nil { - fullTextSQL, err := builder.getFullTextIndexScanSql(params, idxtblname, patternLit.GetSval(), mode) + // Dispatch the per-match TVF by the resolved index's algo: fulltext2 -> + // fulltext2_search, classic fulltext -> fulltext_index_scan. Both emit the same + // (doc_id, score) shape and take the search pattern as an EXPRESSION (fn.Args[0]), + // so a prepared-statement '?' pattern flows through either path unchanged. This + // 2-member dispatch stays inline (not an index-plugin hook) because building the + // node needs QueryBuilder internals a plugin sub-package must not import. + var curr_ftnode_id int32 + var err error + if catalog.IsFullText2IndexAlgo(idxdef.IndexAlgo) { + cfg, cfgErr := builder.buildFulltext2SearchCfg(scanNode, idxdef, mode) + if cfgErr != nil { + return -1, nil, nil, cfgErr + } + exprs := []*plan.Expr{ + makePlan2StringConstExprWithType(cfg), + DeepCopyExpr(fn.Args[0]), // pattern (may be a bound '?' parameter) + DeepCopyExpr(fn.Args[1]), // mode (a constant) + } + // Attach the peeled INCLUDE/pk predicate JSON to the DRIVING TVF (i==0). With + // multiple MATCHes the JOIN #1 doc_id intersection propagates the filter, so one + // filtered stream constrains the whole result — the predicate need only ride the + // driving stream. + if i == 0 && ft2PredsJSON != "" { + exprs = append(exprs, makePlan2StringConstExprWithType(ft2PredsJSON)) + } + curr_ftnode_id, err = builder.buildFulltext2SearchNode(ctx, exprs, nil) if err != nil { return -1, nil, nil, err } - sql = fullTextSQL - } + } else { + // A literal pattern is pre-compiled to the index-scan SQL now; a runtime + // pattern expression (prepared '?') leaves sql empty and compiles at exec. + sql := "" + if patternLit := fn.Args[0].GetLit(); patternLit != nil { + fullTextSQL, sqlErr := builder.getFullTextIndexScanSql(params, idxtblname, patternLit.GetSval(), mode) + if sqlErr != nil { + return -1, nil, nil, sqlErr + } + sql = fullTextSQL + } - exprs := []*plan.Expr{ - makePlan2StringConstExprWithType(srctblname), - makePlan2StringConstExprWithType(idxtblname), - DeepCopyExpr(fn.Args[0]), - DeepCopyExpr(fn.Args[1]), - } - curr_ftnode_id, err := builder.buildFullTextIndexScanNode(ctx, exprs, nil, params, sql) - if err != nil { - return -1, nil, nil, err + exprs := []*plan.Expr{ + makePlan2StringConstExprWithType(srctblname), + makePlan2StringConstExprWithType(idxtblname), + DeepCopyExpr(fn.Args[0]), + DeepCopyExpr(fn.Args[1]), + } + curr_ftnode_id, err = builder.buildFullTextIndexScanNode(ctx, exprs, nil, params, sql) + if err != nil { + return -1, nil, nil, err + } } // save the created fulltext node to either filter or projection // check equal fulltext_match() and return node id to correct project position @@ -632,6 +698,309 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl return joinnodeID, ret_filter_node_ids, ret_proj_node_ids, nil } +// tryApplyCoveredFulltext2 implements the covered fast path (Phase 6): a fully-covered +// projection over a SINGLE fulltext2 index WITH include columns drops the base-table JOIN +// and reads pk/score/include-cols straight from the fulltext2_search TVF. It returns +// (handled=true) only when EVERY guard holds: +// +// (a) a single MATCH driving a single fulltext2 index (len(filterids)==1, and any +// projection MATCH is the SAME func — i.e. eqmap-equal, no independent extra MATCH); +// (b) the index is fulltext2 (not classic) with >=1 include column; +// (c) AFTER peeling numeric/pk predicates into the TVF prefilter JSON, scanNode.FilterList +// is EMPTY (no residual varchar/complex predicate left on the base scan); +// (d) every base-scan column the outer query projects is the pk or an include column +// (no non-covered base column; the only MATCH is the one replaced by score). +// +// When handled it builds the include-coldef TVF, keeps the score-DESC SORT above it, and +// remaps the projection (pk -> TVF col 0, MATCH -> score col 1, include col -> col 2+j). +// On any guard miss it returns (false, nil) and mutates NOTHING, so the caller runs the +// existing JOIN path unchanged. +func (builder *QueryBuilder) tryApplyCoveredFulltext2(nodeID int32, projNode, sortNode, scanNode *plan.Node, + filterids []int32, filterIndexDefs []*plan.IndexDef, projids []int32, projIndexDef []*plan.IndexDef, + eqmap map[int32]int32, paginationLimit, paginationOffset *plan.Expr) (bool, error) { + + // (a) exactly one MATCH filter, driving one index. + if len(filterids) != 1 || len(filterIndexDefs) != 1 { + return false, nil + } + // Every projection MATCH must be eqmap-equal to the single filter MATCH — otherwise + // there is an independent extra MATCH (multi-stream), which the covered path can't serve. + for i := range projids { + if _, ok := eqmap[int32(i)]; !ok { + return false, nil + } + } + + idxdef := filterIndexDefs[0] + // (b) fulltext2 index with >=1 include column. + if !catalog.IsFullText2IndexAlgo(idxdef.IndexAlgo) { + return false, nil + } + incCols := indexDefIncludedColumnsBestEffort(idxdef) + if len(incCols) == 0 { + return false, nil + } + if scanNode.TableDef == nil || scanNode.TableDef.Pkey == nil || scanNode.TableDef.Pkey.PkeyColName == "" { + return false, nil + } + if len(scanNode.BindingTags) == 0 { + return false, nil + } + scanTag := scanNode.BindingTags[0] + pkColName := scanNode.TableDef.Pkey.PkeyColName + var pkColPos int32 = -1 + for i, c := range scanNode.TableDef.Cols { + if strings.EqualFold(c.Name, pkColName) { + pkColPos = int32(i) + break + } + } + if pkColPos < 0 { + return false, nil + } + + // The single MATCH func + its mode. + ftFilterExpr := scanNode.FilterList[filterids[0]] + fn := ftFilterExpr.GetF() + if fn == nil || len(fn.Args) < 2 { + return false, nil + } + modeLit := fn.Args[1].GetLit() + if modeLit == nil { + return false, nil + } + mode := modeLit.GetI64Val() + + // (c) peel numeric/pk predicates into the TVF prefilter; residual MUST be empty. Do the + // peel on a COPY of FilterList so a residual miss leaves scanNode untouched (still covered + // only when peel consumes ALL non-MATCH filters). Exclude the MATCH filter itself. + nonMatchFilters := make([]*plan.Expr, 0, len(scanNode.FilterList)) + for i, f := range scanNode.FilterList { + if int32(i) == filterids[0] { + continue + } + nonMatchFilters = append(nonMatchFilters, f) + } + // Gate the pk peel on an evaluable pk type: a pk predicate on a non-evaluable type + // (uuid/decimal/date/...) is left RESIDUAL (pkColName ""), which makes residual non-empty + // below → not covered → safe 2-JOIN fallback, instead of peeling a predicate the TVF + // would reject at runtime. (pkColName above stays the real name for the coverage check.) + ft2PredsJSON, _, residual, perr := buildFilterPredicateJSON(nonMatchFilters, scanNode, incCols, fulltext2PeelablePkColName(scanNode), true) + if perr != nil { + return false, nil // a peel error → fall back to the safe JOIN path + } + if len(residual) != 0 { + return false, nil // residual varchar/complex predicate stays on the base scan → not covered + } + + // (d) coverage: every projected base-scan ColRef must be the pk or an include column + // (the MATCH funcs are replaced by score, so skip them). Build the include-name set. + incSet := make(map[string]int, len(incCols)) + for j, n := range incCols { + incSet[strings.ToLower(n)] = j + } + covered := true + for i, expr := range projNode.ProjectList { + if isProjIndexPosition(projids, int32(i)) { + continue // this projection is the MATCH → becomes score, handled below + } + coveredBaseColRefs(expr, scanTag, func(colPos int32, name string) bool { + if colPos == pkColPos { + return true + } + cn := name + if cn == "" && colPos >= 0 && int(colPos) < len(scanNode.TableDef.Cols) { + cn = scanNode.TableDef.Cols[colPos].Name + } + if _, ok := incSet[strings.ToLower(cn)]; ok { + return true + } + covered = false + return false + }) + if !covered { + return false, nil + } + } + + // ---- All guards passed: build the covered plan. From here on we mutate. ---- + ctx := builder.ctxByNode[nodeID] + + // Remove the MATCH filter from the base scan (it is fully served by the TVF now). This is + // the last filter standing (residual is empty), so FilterList becomes empty. + scanNode.FilterList = append(scanNode.FilterList[:filterids[0]], scanNode.FilterList[filterids[0]+1:]...) + + cfg, cfgErr := builder.buildFulltext2SearchCfg(scanNode, idxdef, mode) + if cfgErr != nil { + return false, cfgErr + } + exprs := []*plan.Expr{ + makePlan2StringConstExprWithType(cfg), + DeepCopyExpr(fn.Args[0]), // pattern (may be a bound '?' parameter) + DeepCopyExpr(fn.Args[1]), // mode (a constant) + } + if ft2PredsJSON != "" { + exprs = append(exprs, makePlan2StringConstExprWithType(ft2PredsJSON)) + } + ftnodeID, err := builder.buildFulltext2SearchNodeCovered(ctx, exprs, nil, scanNode, incCols) + if err != nil { + return false, err + } + ftnode := builder.qry.Nodes[ftnodeID] + ftTag := ftnode.BindingTags[0] + + // The TVF's doc_id (col 0) carries the source pk; retype it to the pk's real type, as the + // JOIN path does (change doc_id type to the primary type). + pkType := scanNode.TableDef.Cols[pkColPos].Typ + ftnode.TableDef.Cols[0].Typ.Id = pkType.Id + ftnode.TableDef.Cols[0].Typ.Width = pkType.Width + ftnode.TableDef.Cols[0].Typ.Scale = pkType.Scale + + // Register the TVF's output column names for EXPLAIN/name resolution. + for colPos, colDef := range ftnode.TableDef.Cols { + builder.nameByColRef[[2]int32{ftTag, int32(colPos)}] = "mo_fulltext_alias_0." + colDef.Name + } + + // Keep the score-DESC SORT above the TVF (heap tie order is unspecified, so the Sort + // stays). No JOIN: the SORT/PROJECT reads directly from the TVF. + // + // On an EXPLICIT ORDER BY (sortNode != nil) we simply repoint its child at the TVF and do + // NOT remap sortNode.OrderBy here — that is correct, not a bug. A review flagged that the + // sort's OrderBy would dangle on the removed base scan; it does not, because: + // - EVERY ORDER BY sort-key rides in projNode.ProjectList — the binder projects each as a + // hidden INTERNAL column (that is how the SORT gets its key) EVEN when it is not in the + // user's SELECT — so guard (d) above already vets sort-keys identically to selected cols: + // an uncovered ORDER BY column/expr (incl. one not in SELECT, e.g. `ORDER BY b` / `b+1`) + // is a non-pk/non-include base ref that makes the whole rewrite decline to the 2-JOIN + // path (verified live: such an ORDER BY produces the JOIN plan, not this one); + // - a COVERED ORDER BY column (score / pk / include) is remapped to its TVF output by the + // projection remap below, and the final remapAllColRefs pass binds the SORT's OrderBy to + // those TVF outputs. Verified live: ORDER BY — even unprojected, even with + // base/TVF ordinals differing — returns correct rows on this 0-JOIN plan. + scoreOrderBy := []*OrderBySpec{{ + Expr: &Expr{ + Typ: ftnode.TableDef.Cols[1].Typ, + Expr: &plan.Expr_Col{Col: &plan.ColRef{RelPos: ftTag, ColPos: 1}}, + }, + Flag: plan.OrderBySpec_DESC, + }} + if sortNode != nil { + sortNode.Children[0] = ftnodeID + sortNode.Limit = DeepCopyExpr(paginationLimit) + sortNode.Offset = DeepCopyExpr(paginationOffset) + } else { + sortByID := builder.appendNode(&plan.Node{ + NodeType: plan.Node_SORT, + Children: []int32{ftnodeID}, + OrderBy: scoreOrderBy, + Limit: DeepCopyExpr(paginationLimit), + Offset: DeepCopyExpr(paginationOffset), + SpillMem: builder.sortSpillMem, + }, ctx) + projNode.Children[0] = sortByID + } + + // Pagination now belongs to the SORT above the TVF, not the base scan. + scanNode.Limit = nil + scanNode.Offset = nil + + // Remap the projection off the TVF: MATCH -> score (col 1); pk base ColRef -> col 0; + // include base ColRef -> col 2+j. + scoreType := ftnode.TableDef.Cols[1].Typ + for i := range projNode.ProjectList { + if isProjIndexPosition(projids, int32(i)) { + projNode.ProjectList[i] = &Expr{ + Typ: scoreType, + Expr: &plan.Expr_Col{Col: &plan.ColRef{RelPos: ftTag, ColPos: 1}}, + } + continue + } + projNode.ProjectList[i] = remapCoveredBaseColRefs(projNode.ProjectList[i], scanTag, ftTag, + pkColPos, incSet, scanNode) + } + + if err := builder.recordPreparedPluginDependencies(scanNode); err != nil { + return false, err + } + return true, nil +} + +// isProjIndexPosition reports whether projList index i is one of the MATCH projection +// positions (projids holds ProjectList indices of the fulltext MATCH funcs). +func isProjIndexPosition(projids []int32, i int32) bool { + for _, p := range projids { + if p == i { + return true + } + } + return false +} + +// coveredBaseColRefs walks expr and invokes visit(colPos, name) for every base-scan ColRef +// (RelPos == scanTag). visit returns false to stop the walk early (coverage failed). +func coveredBaseColRefs(expr *plan.Expr, scanTag int32, visit func(colPos int32, name string) bool) bool { + if expr == nil { + return true + } + switch impl := expr.Expr.(type) { + case *plan.Expr_Col: + if impl.Col.RelPos == scanTag { + return visit(impl.Col.ColPos, impl.Col.Name) + } + case *plan.Expr_F: + for _, arg := range impl.F.Args { + if !coveredBaseColRefs(arg, scanTag, visit) { + return false + } + } + case *plan.Expr_List: + for _, sub := range impl.List.List { + if !coveredBaseColRefs(sub, scanTag, visit) { + return false + } + } + } + return true +} + +// remapCoveredBaseColRefs rewrites, in place, every base-scan ColRef (RelPos == scanTag) in +// expr to the equivalent TVF output column: the pk -> TVF col 0, an include column -> TVF +// col 2+j (j = its include index). All such ColRefs are guaranteed pk/include by the (d) +// coverage guard, so no default branch is needed. Returns expr for convenience. +func remapCoveredBaseColRefs(expr *plan.Expr, scanTag, ftTag, pkColPos int32, + incSet map[string]int, scanNode *plan.Node) *plan.Expr { + if expr == nil { + return nil + } + switch impl := expr.Expr.(type) { + case *plan.Expr_Col: + if impl.Col.RelPos == scanTag { + if impl.Col.ColPos == pkColPos { + impl.Col.RelPos = ftTag + impl.Col.ColPos = 0 + } else { + name := impl.Col.Name + if name == "" && impl.Col.ColPos >= 0 && int(impl.Col.ColPos) < len(scanNode.TableDef.Cols) { + name = scanNode.TableDef.Cols[impl.Col.ColPos].Name + } + if j, ok := incSet[strings.ToLower(name)]; ok { + impl.Col.RelPos = ftTag + impl.Col.ColPos = int32(2 + j) + } + } + } + case *plan.Expr_F: + for _, arg := range impl.F.Args { + remapCoveredBaseColRefs(arg, scanTag, ftTag, pkColPos, incSet, scanNode) + } + case *plan.Expr_List: + for _, sub := range impl.List.List { + remapCoveredBaseColRefs(sub, scanTag, ftTag, pkColPos, incSet, scanNode) + } + } + return expr +} + func (builder *QueryBuilder) scanHasMatchedFullTextFilter(node *plan.Node) bool { filterids, _ := builder.getFullTextMatchFiltersFromScanNode(node) return len(filterids) > 0 @@ -782,7 +1151,17 @@ func (builder *QueryBuilder) findMatchFullTextIndex(fn *plan.Function, scanNode nargs := len(fn.Args) - 2 for _, idx := range scanNode.TableDef.Indexes { - if idx == nil || !idx.TableExist || !catalog.IsFullTextIndexAlgo(idx.IndexAlgo) { + if idx == nil || !idx.TableExist { + continue + } + // A fulltext2 index has two hidden-table defs (storage + metadata) sharing the + // IndexName; resolve against the STORAGE def so IndexTableName/Parts are the ones + // the fulltext2_search TVF expects. Classic fulltext has a single def. + if catalog.IsFullText2IndexAlgo(idx.IndexAlgo) { + if idx.IndexAlgoTableType != catalog.FullText2Index_TblType_Storage { + continue + } + } else if !catalog.IsFullTextIndexAlgo(idx.IndexAlgo) { continue } if len(idx.Parts) != nargs { diff --git a/pkg/sql/plan/apply_indices_fulltext2.go b/pkg/sql/plan/apply_indices_fulltext2.go new file mode 100644 index 0000000000000..3066ec9de36b9 --- /dev/null +++ b/pkg/sql/plan/apply_indices_fulltext2.go @@ -0,0 +1,248 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// fulltext2-specific pieces of the MATCH query rewrite. fulltext2 reuses the +// classic MATCH(col) AGAINST(...) surface (fulltext_match), so it is resolved by +// findMatchFullTextIndex and chained by applyJoinFullTextIndices exactly like a +// classic fulltext index — the only difference is the per-match TVF: this file's +// buildFulltext2SearchTableFunc emits a fulltext2_search TVF (WAND positional +// engine) instead of fulltext_index_scan. Both emit (doc_id, score), so the +// downstream join/sort/limit is unchanged. +package plan + +import ( + "encoding/json" + "strings" + + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" +) + +// fulltext2PeelablePkColName returns the pk column name ONLY when the pk's type is one the +// fulltext2 in-index predicate evaluator can actually compare — the integer family (incl. +// BIT) and varchar/char, i.e. exactly isComparableIncludeType's set. For any other pk type +// (uuid, decimal, date, datetime, ...) it returns "", so buildFilterPredicateJSON leaves a +// pk predicate RESIDUAL on the base scan instead of peeling one the TVF would reject at +// runtime ("col -1 type not comparable"). This keeps the planner peel and the executor eval +// in agreement (the peel resolves the pk sentinel by name with no type gate of its own). +func fulltext2PeelablePkColName(scanNode *plan.Node) string { + if scanNode.TableDef == nil || scanNode.TableDef.Pkey == nil { + return "" + } + pkColName := scanNode.TableDef.Pkey.PkeyColName + if pkColName == "" { + return "" + } + for _, c := range scanNode.TableDef.Cols { + if c.Name != pkColName { + continue + } + switch types.T(c.Typ.Id) { + case types.T_int8, types.T_int16, types.T_int32, types.T_int64, + types.T_uint8, types.T_uint16, types.T_uint32, types.T_uint64, + types.T_bit, types.T_varchar, types.T_char: + return pkColName + } + return "" + } + return "" +} + +const fulltext2_search_func_name = "fulltext2_search" + +// buildFulltext2SearchCfg validates a MATCH against a fulltext2 index and returns +// the fulltext2_search config JSON ({db,index,metadata[,parser]}). It resolves the +// storage/metadata hidden tables and rejects NL/BOOLEAN modes on a POSITION_FREE +// index (which has no positions — only IN BM25 MODE bag-of-words is valid). +func (builder *QueryBuilder) buildFulltext2SearchCfg(scanNode *plan.Node, idxdef *plan.IndexDef, mode int64) (string, error) { + storeTbl, metaTbl, ok := builder.findFulltext2IndexTables(scanNode, idxdef) + if !ok { + return "", moerr.NewInternalErrorf(builder.GetContext(), + "fulltext2 index %q: storage/metadata tables not found (index may be partially materialized); reindex required", + idxdef.IndexName) + } + + // A POSITION_FREE index stores no positions, so NL / BOOLEAN (and the bare default, + // which is NL semantics) phrase matching cannot work — only IN BM25 MODE (bag-of-words) + // is valid. Reject the others up front with a clear message instead of silently + // returning wrong/empty results. + if mode != int64(tree.FULLTEXT_BM25) && fulltext2PositionFreeFromParams(idxdef.IndexAlgoParams) { + return "", moerr.NewInvalidInputf(builder.GetContext(), + "fulltext2 index %q is POSITION_FREE (bag-of-words only): query it with MATCH(...) AGAINST(... IN BM25 MODE); it has no positions for natural-language / boolean phrase matching", + idxdef.IndexName) + } + + cfgMap := map[string]any{ + "db": scanNode.ObjRef.SchemaName, + "index": storeTbl, + "metadata": metaTbl, + } + if parser := fulltext2ParserFromParams(idxdef.IndexAlgoParams); parser != "" { + cfgMap["parser"] = parser + } + // Carry the INCLUDE column NAMES so the engine can map a covering query's requested + // columns (by name) to each result's positional Include values (search_cache + // fillIncludeResult). Harmless for a non-covering query (the TVF requests nothing). + if incCols := indexDefIncludedColumnsBestEffort(idxdef); len(incCols) > 0 { + cfgMap["include_columns"] = incCols + } + cfgBytes, err := json.Marshal(cfgMap) + if err != nil { + return "", err + } + return string(cfgBytes), nil +} + +// ft2SearchBaseColDefs returns the fulltext2_search TVF's built-in output coldefs: the same +// (pk, relevance) shape and types as classic fulltext's ftIndexColdefs, but RENAMED to the +// reserved __mo_ft_* aliases. The covered path emits INCLUDE columns as sibling outputs and +// the runtime (fulltext2_search.go start()) classifies the output batch BY NAME, so the pk/ +// score outputs must use names no user INCLUDE column can equal — otherwise an INCLUDE column +// named "doc_id"/"score" collides and is misrouted (silent wrong output / shuffle panic). +// This is also what decouples fulltext2 from classic fulltext's coldef list (it only borrowed +// the shape). Kept as a copy-then-rename so the classic types (T_any pk / T_float32 score) +// stay the single source of truth. +func ft2SearchBaseColDefs() []*plan.ColDef { + cds := DeepCopyColDefList(ftIndexColdefs) + cds[0].Name = catalog.FullText2Search_OutCol_DocId + cds[1].Name = catalog.FullText2Search_OutCol_Score + return cds +} + +// buildFulltext2SearchNode builds the fulltext2_search FUNCTION_SCAN node for a MATCH +// resolved to a fulltext2 index. exprs are the TVF args [cfg(const), pattern, mode]; +// pattern is passed as an expression, so a prepared-statement '?' parameter is bound +// and evaluated at execution time (the TVF reads it per row). The node emits the reserved +// (__mo_ft_doc_id, __mo_ft_score) shape, so the downstream join/sort binds by position. +func (builder *QueryBuilder) buildFulltext2SearchNode(ctx *BindContext, exprs []*plan.Expr, children []int32) (int32, error) { + colDefs := ft2SearchBaseColDefs() + node := &plan.Node{ + NodeType: plan.Node_FUNCTION_SCAN, + Stats: &plan.Stats{}, + TableDef: &plan.TableDef{ + TableType: "func_table", + TblFunc: &plan.TableFunction{ + Name: fulltext2_search_func_name, + }, + Cols: colDefs, + }, + BindingTags: []int32{builder.genNewBindTag()}, + TblFuncExprList: exprs, + Children: children, + } + return builder.appendNode(node, ctx), nil +} + +// buildFulltext2SearchNodeCovered builds the fulltext2_search FUNCTION_SCAN node for the +// COVERED fast path: its output coldefs are [doc_id, score] PLUS one column per INCLUDE +// column (at positions 2+), so a fully-covered projection reads pk/score/include straight +// from the TVF with NO base-table JOIN. includeCols are the INCLUDE column NAMES in the +// SAME order as the cfg's include_columns (= the order decodeInclude returns), so output +// col 2+j maps to the j-th include value. Each include coldef takes the base column's real +// type (looked up from scanNode.TableDef.Cols by name). Everything else matches the plain +// buildFulltext2SearchNode (used by the JOIN path). +func (builder *QueryBuilder) buildFulltext2SearchNodeCovered(ctx *BindContext, exprs []*plan.Expr, children []int32, scanNode *plan.Node, includeCols []string) (int32, error) { + colDefs := ft2SearchBaseColDefs() + for _, name := range includeCols { + colTyp, ok := baseColTypeByName(scanNode, name) + if !ok { + return -1, moerr.NewInternalErrorf(builder.GetContext(), + "fulltext2 covered path: INCLUDE column %q not found on base table %q", name, scanNode.TableDef.Name) + } + colDefs = append(colDefs, &plan.ColDef{Name: name, Typ: colTyp}) + } + node := &plan.Node{ + NodeType: plan.Node_FUNCTION_SCAN, + Stats: &plan.Stats{}, + TableDef: &plan.TableDef{ + TableType: "func_table", + TblFunc: &plan.TableFunction{ + Name: fulltext2_search_func_name, + }, + Cols: colDefs, + }, + BindingTags: []int32{builder.genNewBindTag()}, + TblFuncExprList: exprs, + Children: children, + } + return builder.appendNode(node, ctx), nil +} + +// baseColTypeByName returns the plan type of the named column on scanNode's base table +// (case-insensitive), and whether it was found. +func baseColTypeByName(scanNode *plan.Node, name string) (plan.Type, bool) { + if scanNode == nil || scanNode.TableDef == nil { + return plan.Type{}, false + } + for _, c := range scanNode.TableDef.Cols { + if strings.EqualFold(c.Name, name) { + return c.Typ, true + } + } + return plan.Type{}, false +} + +// fulltext2ParserFromParams extracts the "parser" field from an index's +// IndexAlgoParams JSON (empty → default parser at the TVF). +func fulltext2ParserFromParams(params string) string { + if len(params) == 0 { + return "" + } + var p struct { + Parser string `json:"parser"` + } + if err := json.Unmarshal([]byte(params), &p); err != nil { + return "" + } + return p.Parser +} + +// fulltext2PositionFreeFromParams reports whether the index was built POSITION_FREE +// (position_free=="true" in IndexAlgoParams). Such an index has no positional payload, +// so only IN BM25 MODE (bag-of-words) can query it. +func fulltext2PositionFreeFromParams(params string) bool { + if len(params) == 0 { + return false + } + var p struct { + PositionFree string `json:"position_free"` + } + if err := json.Unmarshal([]byte(params), &p); err != nil { + return false + } + return p.PositionFree == "true" +} + +// findFulltext2IndexTables resolves the storage + metadata hidden tables of a +// fulltext2 index (the two sibling defs sharing the IndexName). +func (builder *QueryBuilder) findFulltext2IndexTables(scanNode *plan.Node, idxdef *plan.IndexDef) (storeTbl, metaTbl string, ok bool) { + if scanNode == nil || scanNode.TableDef == nil || idxdef == nil { + return "", "", false + } + for _, idx := range scanNode.TableDef.Indexes { + if idx == nil || idx.IndexName != idxdef.IndexName { + continue + } + switch idx.IndexAlgoTableType { + case catalog.FullText2Index_TblType_Storage: + storeTbl = idx.IndexTableName + case catalog.FullText2Index_TblType_Metadata: + metaTbl = idx.IndexTableName + } + } + return storeTbl, metaTbl, storeTbl != "" && metaTbl != "" +} diff --git a/pkg/sql/plan/apply_indices_ivfflat.go b/pkg/sql/plan/apply_indices_ivfflat.go index 2683dd64d21e4..a0a8dcb711bdc 100644 --- a/pkg/sql/plan/apply_indices_ivfflat.go +++ b/pkg/sql/plan/apply_indices_ivfflat.go @@ -1377,7 +1377,7 @@ func (builder *QueryBuilder) applyIndicesForSortUsingIvfflat(nodeID int32, vecCt bucketExpandStep = 1 } } - asyncIndex, err := catalog.IsIndexAsync(ivfCtx.metaDef.IndexAlgoParams) + asyncIndex, err := catalog.IndexParamAsync(ivfCtx.metaDef.IndexAlgoParams) if err != nil { return nodeID, err } diff --git a/pkg/sql/plan/apply_indices_ivfpq.go b/pkg/sql/plan/apply_indices_ivfpq.go index 0739bf4142e24..b37c28fea1359 100644 --- a/pkg/sql/plan/apply_indices_ivfpq.go +++ b/pkg/sql/plan/apply_indices_ivfpq.go @@ -180,7 +180,7 @@ func (builder *QueryBuilder) applyIndicesForSortUsingIvfpq(nodeID int32, vecCtx includeCols, len(scanNode.FilterList)) } predsJSON, peeled, residualFilters, err := buildFilterPredicateJSON( - scanNode.FilterList, scanNode, includeCols, pkColName) + scanNode.FilterList, scanNode, includeCols, pkColName, false) if err != nil { return nodeID, err } diff --git a/pkg/sql/plan/bind_insert.go b/pkg/sql/plan/bind_insert.go index 00da6ecec2804..793e8c591ba99 100644 --- a/pkg/sql/plan/bind_insert.go +++ b/pkg/sql/plan/bind_insert.go @@ -24,6 +24,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/catalog" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/defines" + indexplugin "github.com/matrixorigin/matrixone/pkg/indexplugin" "github.com/matrixorigin/matrixone/pkg/logutil" lockpb "github.com/matrixorigin/matrixone/pkg/pb/lock" "github.com/matrixorigin/matrixone/pkg/pb/plan" @@ -604,7 +605,7 @@ func (builder *QueryBuilder) deletePkColExpr(relPos int32) *plan.Expr { // (the join key is the integer PK, never the vector), so it is robust to the // materialized layout and never copies the base vector through the hash join. func (builder *QueryBuilder) buildIrregularIvfDeleteByPk(bindCtx *BindContext, multiTableIndex *MultiTableIndex) error { - async, err := catalog.IsIndexAsync(multiTableIndex.IndexAlgoParams) + async, err := catalog.IndexParamAsync(multiTableIndex.IndexAlgoParams) if err != nil { return err } @@ -692,7 +693,7 @@ func (builder *QueryBuilder) buildIrregularIvfDeleteByPk(bindCtx *BindContext, m // the IVF delete: join the index table on doc_id == old/final PK and project only // the index row_id + fake pk into the output. func (builder *QueryBuilder) buildIrregularFulltextDeleteByPk(bindCtx *BindContext, indexdef *plan.IndexDef) error { - async, err := catalog.IsIndexAsync(indexdef.IndexAlgoParams) + async, err := indexplugin.IsAsync(indexdef.IndexAlgo, indexdef.IndexAlgoParams) if err != nil { return err } diff --git a/pkg/sql/plan/bind_update.go b/pkg/sql/plan/bind_update.go index c91c31fb82b87..899e38639e696 100644 --- a/pkg/sql/plan/bind_update.go +++ b/pkg/sql/plan/bind_update.go @@ -1351,7 +1351,7 @@ func classifyIrregularIndexesForUpdate( if desc.AlwaysAsync { continue } - async, err := catalog.IsIndexAsync(idxDef.IndexAlgoParams) + async, err := catalog.IndexParamAsync(idxDef.IndexAlgoParams) if err != nil { return nil, false, err } diff --git a/pkg/sql/plan/build_ddl.go b/pkg/sql/plan/build_ddl.go index 07313383c9495..a354e24ed94e8 100644 --- a/pkg/sql/plan/build_ddl.go +++ b/pkg/sql/plan/build_ddl.go @@ -2310,11 +2310,18 @@ func getRefAction(typ tree.ReferenceOptionType) plan.ForeignKeyDef_RefAction { // lives at pkg/fulltext/plugin/plan/schema.go). It keeps the // batched, in-place-append signature the legacy callers used. func buildFullTextIndexTable(createTable *plan.CreateTable, indexInfos []*tree.FullTextIndex, colMap map[string]*ColDef, existedIndexes []*plan.IndexDef, pkeyName string, ctx CompilerContext) error { - p, ok := indexplugin.Get(catalog.MOIndexFullTextAlgo.ToString()) - if !ok { - return moerr.NewInternalErrorNoCtx("fulltext plugin not registered") + if err := checkFulltextEngineConflict(indexInfos, existedIndexes, ctx); err != nil { + return err } for _, indexInfo := range indexInfos { + algo := catalog.MOIndexFullTextAlgo.ToString() + if indexInfo.IsV2 { + algo = catalog.MoIndexFullText2Algo.ToString() + } + p, ok := indexplugin.Get(algo) + if !ok { + return moerr.NewInternalErrorNoCtx(algo + " plugin not registered") + } idxDefs, tblDefs, err := p.Plan().BuildFullTextIndexDefs( ctx, indexInfo, colMap, existedIndexes, pkeyName, ) @@ -2327,6 +2334,39 @@ func buildFullTextIndexTable(createTable *plan.CreateTable, indexInfos []*tree.F return nil } +// ftColSetKey makes the fulltext column set order-independent. A classic and a +// FULLTEXT2 index over the same columns would make MATCH routing depend on +// catalog enumeration order, so reject that ambiguous state at DDL time. +func ftColSetKey(cols []string) string { + c := append([]string(nil), cols...) + slices.Sort(c) + return strings.Join(c, "\x00") +} + +func checkFulltextEngineConflict(indexInfos []*tree.FullTextIndex, existedIndexes []*plan.IndexDef, ctx CompilerContext) error { + engine := make(map[string]bool) + for _, idx := range existedIndexes { + v2 := catalog.IsFullText2IndexAlgo(idx.IndexAlgo) + if !v2 && !catalog.IsFullTextIndexAlgo(idx.IndexAlgo) { + continue + } + engine[ftColSetKey(idx.Parts)] = v2 + } + for _, info := range indexInfos { + cols := make([]string, 0, len(info.KeyParts)) + for _, kp := range info.KeyParts { + cols = append(cols, kp.ColName.ColName()) + } + key := ftColSetKey(cols) + if prev, ok := engine[key]; ok && prev != info.IsV2 { + return moerr.NewInvalidInput(ctx.GetContext(), + "cannot create both a FULLTEXT and a FULLTEXT2 index on the same column(s); drop the existing one first") + } + engine[key] = info.IsV2 + } + return nil +} + func buildUniqueIndexTable(createTable *plan.CreateTable, indexInfos []*tree.UniqueIndex, colMap map[string]*ColDef, pkeyName string, ctx CompilerContext) error { for _, indexInfo := range indexInfos { indexDef := &plan.IndexDef{} @@ -3512,6 +3552,13 @@ func buildCreateIndex(stmt *tree.CreateIndex, ctx CompilerContext) (*Plan, error KeyParts: stmt.KeyParts, IndexOption: stmt.IndexOption, } + case tree.INDEX_CATEGORY_FULLTEXT2: + ftIdx = &tree.FullTextIndex{ + Name: indexName, + KeyParts: stmt.KeyParts, + IndexOption: stmt.IndexOption, + IsV2: true, + } case tree.INDEX_CATEGORY_SPATIAL: keyType := tree.INDEX_TYPE_RTREE if stmt.IndexOption != nil && stmt.IndexOption.IType != tree.INDEX_TYPE_INVALID { @@ -3916,6 +3963,10 @@ func buildAlterTableInplace(stmt *tree.AlterTable, ctx CompilerContext) (*Plan, var updateSqls []string uniqueIndexInfos := make([]*tree.UniqueIndex, 0) secondaryIndexInfos := make([]*tree.Index, 0) + // Index definitions added earlier in this ALTER are visible to duplicate-name + // and FULLTEXT/FULLTEXT2 conflict checks, but stay out of tableDef.Indexes + // until their hidden tables are actually created by the executor. + var addedIdxDefs []*plan.IndexDef for i, option := range stmt.Options { switch opt := option.(type) { case *tree.AlterOptionDrop: @@ -4068,7 +4119,6 @@ func buildAlterTableInplace(stmt *tree.AlterTable, ctx CompilerContext) (*Plan, nameLower := strings.ToLower(idx.IndexName) constrNames[nameLower] = true } - if err := checkDuplicateConstraint( constrNames, indexName, @@ -4123,6 +4173,9 @@ func buildAlterTableInplace(stmt *tree.AlterTable, ctx CompilerContext) (*Plan, nameLower := strings.ToLower(idx.IndexName) constrNames[nameLower] = true } + for _, idx := range addedIdxDefs { + constrNames[strings.ToLower(idx.IndexName)] = true + } if err := checkDuplicateConstraint( constrNames, @@ -4140,16 +4193,21 @@ func buildAlterTableInplace(stmt *tree.AlterTable, ctx CompilerContext) (*Plan, oriPriKeyName := getTablePriKeyName(tableDef.Pkey) indexInfo := &plan.CreateTable{TableDef: &TableDef{}} + existed := tableDef.Indexes + if len(addedIdxDefs) > 0 { + existed = append(append([]*plan.IndexDef{}, tableDef.Indexes...), addedIdxDefs...) + } if err := buildFullTextIndexTable( indexInfo, []*tree.FullTextIndex{def}, colMap, - tableDef.Indexes, + existed, oriPriKeyName, ctx, ); err != nil { return nil, err } + addedIdxDefs = append(addedIdxDefs, indexInfo.TableDef.Indexes...) alterTable.Actions[i] = &plan.AlterTable_Action{ Action: &plan.AlterTable_Action_AddIndex{ @@ -4267,6 +4325,7 @@ func buildAlterTableInplace(stmt *tree.AlterTable, ctx CompilerContext) (*Plan, // merge + reject happens at compile in Compile.ValidateReindexParams, // reading the options straight off the parse tree. alterTableReIndex.ForceSync = opt.ForceSync + alterTableReIndex.Merge = opt.Merge name_not_found := true // check index diff --git a/pkg/sql/plan/build_dml_util.go b/pkg/sql/plan/build_dml_util.go index e6f6f07c88475..018eed503654e 100644 --- a/pkg/sql/plan/build_dml_util.go +++ b/pkg/sql/plan/build_dml_util.go @@ -4945,7 +4945,7 @@ func buildPreInsertMultiTableIndexes(ctx CompilerContext, builder *QueryBuilder, case catalog.MoIndexIvfFlatAlgo.ToString(): // skip async var async bool - async, err = catalog.IsIndexAsync(multiTableIndex.IndexAlgoParams) + async, err = catalog.IndexParamAsync(multiTableIndex.IndexAlgoParams) if err != nil { return err } @@ -5059,7 +5059,7 @@ func buildDeleteMultiTableIndexes(ctx CompilerContext, builder *QueryBuilder, bi case catalog.MoIndexIvfFlatAlgo.ToString(): // skip async var async bool - async, err = catalog.IsIndexAsync(multiTableIndex.IndexAlgoParams) + async, err = catalog.IndexParamAsync(multiTableIndex.IndexAlgoParams) if err != nil { return err } @@ -5795,7 +5795,7 @@ func buildPreInsertFullTextIndex(stmt *tree.Insert, ctx CompilerContext, builder } // skip async - async, err := catalog.IsIndexAsync(indexdef.IndexAlgoParams) + async, err := indexplugin.IsAsync(indexdef.IndexAlgo, indexdef.IndexAlgoParams) if err != nil { return err } @@ -6242,7 +6242,7 @@ func buildPreDeleteFullTextIndex(ctx CompilerContext, builder *QueryBuilder, bin indexdef *plan.IndexDef, idx int, typMap map[string]plan.Type, posMap map[string]int) error { // skip async - async, err := catalog.IsIndexAsync(indexdef.IndexAlgoParams) + async, err := indexplugin.IsAsync(indexdef.IndexAlgo, indexdef.IndexAlgoParams) if err != nil { return err } @@ -6282,7 +6282,7 @@ func buildPostDmlFullTextIndex(ctx CompilerContext, builder *QueryBuilder, bindC sourceStep int32, indexdef *plan.IndexDef, idx int, isDelete, isInsert, isDeleteWithoutFilters bool) error { // skip async - async, err := catalog.IsIndexAsync(indexdef.IndexAlgoParams) + async, err := indexplugin.IsAsync(indexdef.IndexAlgo, indexdef.IndexAlgoParams) if err != nil { return err } diff --git a/pkg/sql/plan/build_show_util.go b/pkg/sql/plan/build_show_util.go index 7597f56404ad8..9873226c7788c 100644 --- a/pkg/sql/plan/build_show_util.go +++ b/pkg/sql/plan/build_show_util.go @@ -240,8 +240,12 @@ func constructCreateTableSQL( } var indexStr string - if !indexdef.Unique && catalog.IsFullTextIndexAlgo(indexdef.IndexAlgo) { - indexStr += " FULLTEXT " + if !indexdef.Unique && (catalog.IsFullTextIndexAlgo(indexdef.IndexAlgo) || catalog.IsFullText2IndexAlgo(indexdef.IndexAlgo)) { + if catalog.IsFullText2IndexAlgo(indexdef.IndexAlgo) { + indexStr += " FULLTEXT2 " + } else { + indexStr += " FULLTEXT " + } if len(indexdef.IndexName) > 0 { indexStr += sqlquote.Ident(indexdef.IndexName) @@ -263,6 +267,16 @@ func constructCreateTableSQL( indexStr += ")" + // INCLUDE columns: render so SHOW CREATE round-trips — a rebuild from + // the clause-less DDL would silently drop the covering/prefilter columns. + // Uses the same helper as the vector-index branch below (INCLUDE is an + // order-flexible index_option, so it may precede WITH PARSER). + includedColumns, incErr := indexDefIncludedColumns(indexdef) + if incErr != nil { + return "", nil, incErr + } + indexStr += indexIncludeColumnsToString(includedColumns, colNameToOriginName) + if indexdef.IndexAlgoParams != "" { val, err := sonic.Get([]byte(indexdef.IndexAlgoParams), "parser") // ignore err != nil --> value not found @@ -278,20 +292,32 @@ func constructCreateTableSQL( } } - val, err = sonic.Get([]byte(indexdef.IndexAlgoParams), catalog.Async) - // ignore err != nil --> value not found - if err == nil { - async, err := val.StrictString() + if catalog.IsFullText2IndexAlgo(indexdef.IndexAlgo) { + // fulltext2 carries persisted build options (position_free, + // max_index_capacity, max_postings_capacity) plus async / cron + // scheduling. Render the FULL set via the shared list so SHOW CREATE + // round-trips — a rebuild from parser-only DDL would silently drop + // POSITION_FREE and the capacities and build a different index. + paramStr, err := catalog.IndexParamsToStringList(indexdef.IndexAlgoParams) if err != nil { - // value exists but not string type return "", nil, err } - - if async == "true" { - indexStr += " ASYNC" + indexStr += paramStr + } else { + val, err = sonic.Get([]byte(indexdef.IndexAlgoParams), catalog.Async) + // ignore err != nil --> value not found + if err == nil { + async, err := val.StrictString() + if err != nil { + // value exists but not string type + return "", nil, err + } + + if async == "true" { + indexStr += " ASYNC" + } } } - } } else { diff --git a/pkg/sql/plan/build_update.go b/pkg/sql/plan/build_update.go index 0da6612c2b738..ef213654c5fb8 100644 --- a/pkg/sql/plan/build_update.go +++ b/pkg/sql/plan/build_update.go @@ -44,7 +44,7 @@ func isSyncMaintainedIrregularIndex(idxDef *plan.IndexDef) bool { if p.Catalog().SyncDescriptor().AlwaysAsync { return false } - async, _ := catalog.IsIndexAsync(idxDef.IndexAlgoParams) + async, _ := catalog.IndexParamAsync(idxDef.IndexAlgoParams) return !async } diff --git a/pkg/sql/plan/filter_predicate.go b/pkg/sql/plan/filter_predicate.go index 68ec8f231d02c..28021d3657ab9 100644 --- a/pkg/sql/plan/filter_predicate.go +++ b/pkg/sql/plan/filter_predicate.go @@ -126,6 +126,7 @@ func buildFilterPredicateJSON( scanNode *plan.Node, includeColumns []string, pkColName string, + allowStrings bool, // fulltext2 (actual-value storage, byte-compare) peels string preds; vector plugins (hashed) do not ) (predsJSON string, serialized []*plan.Expr, residual []*plan.Expr, err error) { if scanNode == nil || scanNode.TableDef == nil || len(scanNode.BindingTags) == 0 { return "", nil, filters, nil @@ -145,7 +146,7 @@ func buildFilterPredicateJSON( preds := make([]filterJSONPred, 0, len(filters)) for _, expr := range filters { - entries, ok, ferr := filterExprToPreds(expr, scanTag, td, colOrd, pkColName) + entries, ok, ferr := filterExprToPreds(expr, scanTag, td, colOrd, pkColName, allowStrings) if ferr != nil { return "", nil, nil, ferr } @@ -180,7 +181,7 @@ func buildFilterPredicateJSON( // C++ side's implicit-AND of the predicate array). ok=false means the // expression isn't serializable and must remain a residual filter. func filterExprToPreds( - expr *plan.Expr, scanTag int32, td *plan.TableDef, colOrd map[string]int, pkColName string, + expr *plan.Expr, scanTag int32, td *plan.TableDef, colOrd map[string]int, pkColName string, allowStrings bool, ) ([]filterJSONPred, bool, error) { fn := expr.GetF() if fn == nil || fn.Func == nil { @@ -189,11 +190,11 @@ func filterExprToPreds( name := strings.ToLower(fn.Func.ObjName) if name == "and" && len(fn.Args) == 2 { - left, okL, err := filterExprToPreds(fn.Args[0], scanTag, td, colOrd, pkColName) + left, okL, err := filterExprToPreds(fn.Args[0], scanTag, td, colOrd, pkColName, allowStrings) if err != nil { return nil, false, err } - right, okR, err := filterExprToPreds(fn.Args[1], scanTag, td, colOrd, pkColName) + right, okR, err := filterExprToPreds(fn.Args[1], scanTag, td, colOrd, pkColName, allowStrings) if err != nil { return nil, false, err } @@ -211,20 +212,41 @@ func filterExprToPreds( if flipped { op = filterFlipCmpOp(op) } - v, ok := filterLiteralToJSONValue(lit) + v, ok := filterLiteralToJSONValue(lit, allowStrings) if !ok { return nil, false, nil } return []filterJSONPred{{Col: ord, Op: op, Val: v}}, true, nil } + // LIKE 'prefix%' on a fulltext2 INCLUDE/pk varchar column -> a byte-prefix predicate. + // MO rewrites a pure-prefix LIKE into prefix_eq(col, 'prefix') (the '%' already stripped); + // the evaluator's "prefix" op is bytes.HasPrefix, exactly equivalent for MO's byte-exact + // varchar compare. Only when allowStrings (the vector plugins hash strings, never emit + // "prefix"). A non-pure-prefix LIKE is NOT rewritten to prefix_eq, so it stays residual. + if allowStrings && name == "prefix_eq" && len(fn.Args) == 2 { + ord, ok := filterColOrdinal(fn.Args[0], scanTag, td, colOrd, pkColName) + if !ok { + return nil, false, nil + } + lit := fn.Args[1].GetLit() + if lit == nil || lit.Isnull { + return nil, false, nil + } + sv, ok := lit.Value.(*plan.Literal_Sval) + if !ok { + return nil, false, nil + } + return []filterJSONPred{{Col: ord, Op: "prefix", Val: sv.Sval}}, true, nil + } + if name == "between" && len(fn.Args) == 3 { ord, ok := filterColOrdinal(fn.Args[0], scanTag, td, colOrd, pkColName) if !ok { return nil, false, nil } - lo, okL := filterLiteralToJSONValue(fn.Args[1].GetLit()) - hi, okH := filterLiteralToJSONValue(fn.Args[2].GetLit()) + lo, okL := filterLiteralToJSONValue(fn.Args[1].GetLit(), allowStrings) + hi, okH := filterLiteralToJSONValue(fn.Args[2].GetLit(), allowStrings) if !okL || !okH { return nil, false, nil } @@ -242,7 +264,7 @@ func filterExprToPreds( } vals := make([]any, 0, len(items)) for _, it := range items { - v, ok := filterLiteralToJSONValue(it.GetLit()) + v, ok := filterLiteralToJSONValue(it.GetLit(), allowStrings) if !ok { return nil, false, nil } @@ -362,7 +384,12 @@ func filterInListItems(args []*plan.Expr) []*plan.Expr { // json.Marshal emits as a bare JSON number (for numerics) or string. // Unsupported shapes (NULL, date/time types, decimals, binary, vectors) // return ok=false so the caller treats the predicate as residual. -func filterLiteralToJSONValue(lit *plan.Literal) (any, bool) { +// filterLiteralToJSONValue converts a scalar literal to its JSON wire value. allowStrings +// enables VARCHAR/CHAR string literals — safe ONLY for fulltext2, which stores the actual +// value and byte-compares it (MO compares varchar byte-exact regardless of declared +// collation, so byte-compare == SQL compare). The vector plugins hash strings to uint64, +// so for them a string literal cannot match the stored column and stays residual. +func filterLiteralToJSONValue(lit *plan.Literal, allowStrings bool) (any, bool) { if lit == nil || lit.Isnull { return nil, false } @@ -392,8 +419,13 @@ func filterLiteralToJSONValue(lit *plan.Literal) (any, bool) { return int64(1), true } return int64(0), true + case *plan.Literal_Sval: + // fulltext2 stores the actual varchar/char value and byte-compares it; the vector + // plugins hash strings to uint64, so a raw string can't match their storage. + if allowStrings { + return v.Sval, true + } + return nil, false } - // VARCHAR/string literals require FNV-1a hashing to match the - // UINT64-hashed column storage — deferred; treat as residual for now. return nil, false } diff --git a/pkg/sql/plan/utils.go b/pkg/sql/plan/utils.go index f6dcf2f0fb075..034d1df8d3665 100644 --- a/pkg/sql/plan/utils.go +++ b/pkg/sql/plan/utils.go @@ -383,6 +383,9 @@ func splitAndBindCondition(astExpr tree.Expr, expandAlias ExpandAliasMode, ctx * needCast := true fn := expr.GetF() if fn != nil { + // fulltext_match / bm25_match are rewritten to an index-scan join by the + // optimizer; leave them un-cast so the rewrite can find them by name in the + // filter list (a wrapping cast(... AS BOOL) would hide the function). needCast = fn.Func.ObjName != "fulltext_match" } // expr must be bool type, if not, try to do type convert From bfa83f42737ca6304c0af3f6913e70ccc961464f Mon Sep 17 00:00:00 2001 From: Eric Lam Date: Wed, 26 Aug 2026 19:49:19 +0800 Subject: [PATCH 04/17] test(fulltext2): add unit and distributed BVT coverage Backport-of: 1d7b6311ce91df0968da435bb405f3d68137c595 --- pkg/catalog/fulltext2_params_test.go | 69 ++ pkg/catalog/secondary_index_utils_test.go | 10 +- pkg/fulltext2/boolean_parity_test.go | 79 ++ pkg/fulltext2/boolean_stream_test.go | 172 +++ pkg/fulltext2/boolean_test.go | 166 +++ pkg/fulltext2/boolean_tree_test.go | 112 ++ pkg/fulltext2/builder_test.go | 184 +++ pkg/fulltext2/cdc_lww_test.go | 408 +++++++ pkg/fulltext2/cdc_test.go | 200 ++++ pkg/fulltext2/chinese_test.go | 105 ++ pkg/fulltext2/compact_test.go | 103 ++ pkg/fulltext2/coverage_extra_test.go | 129 +++ pkg/fulltext2/coverage_index_test.go | 80 ++ .../coverage_query_membership_test.go | 205 ++++ pkg/fulltext2/docmap_include_test.go | 155 +++ pkg/fulltext2/frames_test.go | 123 ++ pkg/fulltext2/fulltext_boolean_port_test.go | 215 ++++ pkg/fulltext2/include_boxfree_test.go | 153 +++ pkg/fulltext2/include_build_test.go | 98 ++ pkg/fulltext2/include_coverage_test.go | 73 ++ pkg/fulltext2/include_predicate_test.go | 211 ++++ pkg/fulltext2/index_test.go | 116 ++ pkg/fulltext2/jsonvalue_test.go | 79 ++ pkg/fulltext2/membership_test.go | 135 +++ pkg/fulltext2/mmap_test.go | 151 +++ pkg/fulltext2/multiseg_test.go | 217 ++++ pkg/fulltext2/parser_test.go | 141 +++ pkg/fulltext2/phrase_cursor_test.go | 90 ++ pkg/fulltext2/phrase_exact_test.go | 96 ++ pkg/fulltext2/phrase_perf_bench_test.go | 124 ++ pkg/fulltext2/pkcodec_review_test.go | 84 ++ pkg/fulltext2/pkcodec_test.go | 74 ++ pkg/fulltext2/plugin/compile/compile_test.go | 115 ++ .../plugin/idxcron/idxcron_cover_test.go | 134 +++ pkg/fulltext2/plugin/iscp/iscp_cover_test.go | 80 ++ pkg/fulltext2/plugin/plan/plan_cover_test.go | 548 +++++++++ pkg/fulltext2/plugin/plugin_cover_test.go | 55 + pkg/fulltext2/plugin/runtime/runtime_test.go | 105 ++ pkg/fulltext2/pool_bench_test.go | 115 ++ .../positionfree_reconstruct_test.go | 61 + pkg/fulltext2/positionfree_test.go | 110 ++ pkg/fulltext2/search_cache_test.go | 110 ++ pkg/fulltext2/search_cache_unit_test.go | 310 +++++ pkg/fulltext2/search_test.go | 114 ++ pkg/fulltext2/segment_test.go | 87 ++ pkg/fulltext2/serialize_test.go | 183 +++ pkg/fulltext2/sink_test.go | 82 ++ pkg/fulltext2/storage_mock_test.go | 412 +++++++ pkg/fulltext2/storage_test.go | 189 ++++ pkg/fulltext2/stream_phrase_test.go | 174 +++ pkg/fulltext2/stream_test.go | 82 ++ pkg/fulltext2/stream_vec_test.go | 169 +++ pkg/fulltext2/termdict_jieba_test.go | 131 +++ pkg/fulltext2/termdict_test.go | 133 +++ pkg/fulltext2/topk_bench_test.go | 48 + pkg/fulltext2/topk_test.go | 82 ++ pkg/fulltext2/wand_blockmax_test.go | 169 +++ pkg/fulltext2/wand_test.go | 142 +++ pkg/indexplugin/async_test.go | 59 + pkg/indexplugin/catalog/hooks_test.go | 66 ++ pkg/iscp/executor_factory_test.go | 1 + pkg/iscp/fulltext2_sqlwriter_test.go | 189 ++++ pkg/iscp/initsql_test.go | 57 + pkg/iscp/util_test.go | 65 +- pkg/monlp/tokenizer/word_id_test.go | 42 + .../table_function/fulltext2_compact_test.go | 188 ++++ .../table_function/fulltext2_create_test.go | 251 +++++ .../table_function/fulltext2_search_test.go | 409 +++++++ .../table_function/hnsw_create_test.go | 65 ++ .../table_function/hnsw_search_test.go | 9 +- .../colexec/table_function/ivf_search_test.go | 8 +- pkg/sql/compile/ddl_drop_plugin_index_test.go | 8 +- .../dialect/mysql/keyword_policy_test.go | 1 + .../parsers/dialect/mysql/mysql_sql_test.go | 6 + pkg/sql/parsers/tree/fulltext_match_test.go | 56 + pkg/sql/plan/apply_indices_fulltext2_test.go | 107 ++ pkg/sql/plan/filter_predicate_test.go | 106 +- pkg/sql/plan/function/function_id_test.go | 3 +- .../checkpointtool/fulltext2_dump_test.go | 88 ++ pkg/tools/checkpointtool/table_dump_test.go | 30 + .../brute_force/brute_force_test.go | 8 +- pkg/vectorindex/brute_force/gpu_test.go | 6 + pkg/vectorindex/cache/cache_test.go | 32 +- pkg/vectorindex/cachegen/cachegen_test.go | 144 +++ .../cagra/plugin/compile/compile_test.go | 6 +- pkg/vectorindex/cagra/search_test.go | 10 + pkg/vectorindex/hnsw/generation_test.go | 36 + .../hnsw/plugin/compile/compile_smoke_test.go | 2 +- .../hnsw/plugin/runtime/runtime_test.go | 6 + pkg/vectorindex/hnsw/search_test.go | 167 ++- pkg/vectorindex/idxcron/executor_test.go | 1 + .../plugin/compile/compile_smoke_test.go | 2 +- .../narrow_quantization_compile_test.go | 9 +- pkg/vectorindex/ivfflat/search_test.go | 11 +- .../ivfpq/plugin/compile/compile_test.go | 6 +- pkg/vectorindex/ivfpq/search_test.go | 5 + .../sqlexec/idxcron_sessionvar_guard_test.go | 110 ++ pkg/vectorindex/sqlexec/metadata_test.go | 57 + pkg/vectorindex/sqlexec/sqlexec_test.go | 18 + .../cases/fulltext/fulltext2.result | 547 +++++---- test/distributed/cases/fulltext/fulltext2.sql | 2 +- .../cases/fulltext2/fulltext2.result | 90 ++ .../distributed/cases/fulltext2/fulltext2.sql | 71 ++ .../cases/fulltext2/fulltext2_bitpk.result | 46 + .../cases/fulltext2/fulltext2_bitpk.sql | 36 + .../cases/fulltext2/fulltext2_conflict.result | 19 + .../cases/fulltext2/fulltext2_conflict.sql | 32 + .../cases/fulltext2/fulltext2_covered.result | 151 +++ .../cases/fulltext2/fulltext2_covered.sql | 108 ++ .../cases/fulltext2/fulltext2_include.result | 129 +++ .../cases/fulltext2/fulltext2_include.sql | 85 ++ .../fulltext2/fulltext2_jsonvalue.result | 62 + .../cases/fulltext2/fulltext2_jsonvalue.sql | 59 + .../fulltext2/fulltext2_membership.result | 119 ++ .../cases/fulltext2/fulltext2_membership.sql | 98 ++ .../fulltext2_merge_forcesync.result | 57 + .../fulltext2/fulltext2_merge_forcesync.sql | 46 + .../cases/fulltext2/fulltext2_parser.result | 79 ++ .../cases/fulltext2/fulltext2_parser.sql | 60 + .../fulltext2/fulltext2_position_free.result | 53 + .../fulltext2/fulltext2_position_free.sql | 48 + .../fulltext2_posting_capacity.result | 65 ++ .../fulltext2/fulltext2_posting_capacity.sql | 61 + .../cases/fulltext2/fulltext2_prepare.result | 117 ++ .../cases/fulltext2/fulltext2_prepare.sql | 96 ++ .../cases/fulltext2/fulltext2_pushdown.result | 131 +++ .../cases/fulltext2/fulltext2_pushdown.sql | 101 ++ .../fulltext2/fulltext2_review_regress.result | 57 + .../fulltext2/fulltext2_review_regress.sql | 48 + .../fulltext2/fulltext2_showcreate.result | 49 + .../cases/fulltext2/fulltext2_showcreate.sql | 38 + .../cases/fulltext2/fulltext2_wiki.result | 63 ++ .../cases/fulltext2/fulltext2_wiki.sql | 54 + .../fulltext2/fulltext2_async.result | 41 + .../fulltext2/fulltext2_async.sql | 58 + .../fulltext2/fulltext2_bugfix.result | 60 + .../fulltext2/fulltext2_bugfix.sql | 67 ++ .../fulltext2/fulltext2_datalink.result | 34 + .../fulltext2/fulltext2_datalink.sql | 45 + .../fulltext2_datalink_tenant.result | 21 + .../fulltext2/fulltext2_datalink_tenant.sql | 33 + .../fulltext2/fulltext2_include_async.result | 72 ++ .../fulltext2/fulltext2_include_async.sql | 74 ++ .../fulltext2_position_free_merge.result | 55 + .../fulltext2_position_free_merge.sql | 42 + .../fulltext2/fulltext2_restore_clone.result | 54 + .../fulltext2/fulltext2_restore_clone.sql | 76 ++ .../resources/fulltext2/wiki_en_1k.csv | 1000 +++++++++++++++++ .../resources/fulltext2/wiki_zh_1k.csv | 1000 +++++++++++++++++ 149 files changed, 16044 insertions(+), 312 deletions(-) create mode 100644 pkg/catalog/fulltext2_params_test.go create mode 100644 pkg/fulltext2/boolean_parity_test.go create mode 100644 pkg/fulltext2/boolean_stream_test.go create mode 100644 pkg/fulltext2/boolean_test.go create mode 100644 pkg/fulltext2/boolean_tree_test.go create mode 100644 pkg/fulltext2/builder_test.go create mode 100644 pkg/fulltext2/cdc_lww_test.go create mode 100644 pkg/fulltext2/cdc_test.go create mode 100644 pkg/fulltext2/chinese_test.go create mode 100644 pkg/fulltext2/compact_test.go create mode 100644 pkg/fulltext2/coverage_extra_test.go create mode 100644 pkg/fulltext2/coverage_index_test.go create mode 100644 pkg/fulltext2/coverage_query_membership_test.go create mode 100644 pkg/fulltext2/docmap_include_test.go create mode 100644 pkg/fulltext2/frames_test.go create mode 100644 pkg/fulltext2/fulltext_boolean_port_test.go create mode 100644 pkg/fulltext2/include_boxfree_test.go create mode 100644 pkg/fulltext2/include_build_test.go create mode 100644 pkg/fulltext2/include_coverage_test.go create mode 100644 pkg/fulltext2/include_predicate_test.go create mode 100644 pkg/fulltext2/index_test.go create mode 100644 pkg/fulltext2/jsonvalue_test.go create mode 100644 pkg/fulltext2/membership_test.go create mode 100644 pkg/fulltext2/mmap_test.go create mode 100644 pkg/fulltext2/multiseg_test.go create mode 100644 pkg/fulltext2/parser_test.go create mode 100644 pkg/fulltext2/phrase_cursor_test.go create mode 100644 pkg/fulltext2/phrase_exact_test.go create mode 100644 pkg/fulltext2/phrase_perf_bench_test.go create mode 100644 pkg/fulltext2/pkcodec_review_test.go create mode 100644 pkg/fulltext2/pkcodec_test.go create mode 100644 pkg/fulltext2/plugin/compile/compile_test.go create mode 100644 pkg/fulltext2/plugin/idxcron/idxcron_cover_test.go create mode 100644 pkg/fulltext2/plugin/iscp/iscp_cover_test.go create mode 100644 pkg/fulltext2/plugin/plan/plan_cover_test.go create mode 100644 pkg/fulltext2/plugin/plugin_cover_test.go create mode 100644 pkg/fulltext2/plugin/runtime/runtime_test.go create mode 100644 pkg/fulltext2/pool_bench_test.go create mode 100644 pkg/fulltext2/positionfree_reconstruct_test.go create mode 100644 pkg/fulltext2/positionfree_test.go create mode 100644 pkg/fulltext2/search_cache_test.go create mode 100644 pkg/fulltext2/search_cache_unit_test.go create mode 100644 pkg/fulltext2/search_test.go create mode 100644 pkg/fulltext2/segment_test.go create mode 100644 pkg/fulltext2/serialize_test.go create mode 100644 pkg/fulltext2/sink_test.go create mode 100644 pkg/fulltext2/storage_mock_test.go create mode 100644 pkg/fulltext2/storage_test.go create mode 100644 pkg/fulltext2/stream_phrase_test.go create mode 100644 pkg/fulltext2/stream_test.go create mode 100644 pkg/fulltext2/stream_vec_test.go create mode 100644 pkg/fulltext2/termdict_jieba_test.go create mode 100644 pkg/fulltext2/termdict_test.go create mode 100644 pkg/fulltext2/topk_bench_test.go create mode 100644 pkg/fulltext2/topk_test.go create mode 100644 pkg/fulltext2/wand_blockmax_test.go create mode 100644 pkg/fulltext2/wand_test.go create mode 100644 pkg/indexplugin/async_test.go create mode 100644 pkg/indexplugin/catalog/hooks_test.go create mode 100644 pkg/iscp/fulltext2_sqlwriter_test.go create mode 100644 pkg/iscp/initsql_test.go create mode 100644 pkg/monlp/tokenizer/word_id_test.go create mode 100644 pkg/sql/colexec/table_function/fulltext2_compact_test.go create mode 100644 pkg/sql/colexec/table_function/fulltext2_create_test.go create mode 100644 pkg/sql/colexec/table_function/fulltext2_search_test.go create mode 100644 pkg/sql/parsers/tree/fulltext_match_test.go create mode 100644 pkg/sql/plan/apply_indices_fulltext2_test.go create mode 100644 pkg/tools/checkpointtool/fulltext2_dump_test.go create mode 100644 pkg/vectorindex/cachegen/cachegen_test.go create mode 100644 pkg/vectorindex/hnsw/generation_test.go create mode 100644 pkg/vectorindex/sqlexec/idxcron_sessionvar_guard_test.go create mode 100644 test/distributed/cases/fulltext2/fulltext2.result create mode 100644 test/distributed/cases/fulltext2/fulltext2.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_bitpk.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_bitpk.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_conflict.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_conflict.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_covered.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_covered.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_include.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_include.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_jsonvalue.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_jsonvalue.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_membership.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_membership.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_merge_forcesync.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_merge_forcesync.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_parser.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_parser.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_position_free.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_position_free.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_posting_capacity.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_posting_capacity.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_prepare.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_prepare.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_pushdown.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_pushdown.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_review_regress.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_review_regress.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_showcreate.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_showcreate.sql create mode 100644 test/distributed/cases/fulltext2/fulltext2_wiki.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_wiki.sql create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.result create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.sql create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.result create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.sql create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.result create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.sql create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.result create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.sql create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.result create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.sql create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.result create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.sql create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.result create mode 100644 test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.sql create mode 100644 test/distributed/resources/fulltext2/wiki_en_1k.csv create mode 100644 test/distributed/resources/fulltext2/wiki_zh_1k.csv diff --git a/pkg/catalog/fulltext2_params_test.go b/pkg/catalog/fulltext2_params_test.go new file mode 100644 index 0000000000000..501fdf81bc1f0 --- /dev/null +++ b/pkg/catalog/fulltext2_params_test.go @@ -0,0 +1,69 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package catalog + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +// TestIndexParamsToStringListFulltext2Options pins that the shared SHOW CREATE / +// checkpoint-restore option renderer emits the fulltext2 build options — +// position_free plus the capacities — so a rebuilt index keeps them instead of +// silently reverting to a positional / default-capacity one. +func TestIndexParamsToStringListFulltext2Options(t *testing.T) { + params, err := IndexParamsMapToJsonString(map[string]string{ + "parser": "ngram", + IndexAlgoParamPositionFree: "true", + IndexAlgoParamMaxIndexCapacity: "1000", + IndexAlgoParamMaxPostingsCapacity: "8000", + }) + require.NoError(t, err) + + s, err := IndexParamsToStringList(params) + require.NoError(t, err) + require.Contains(t, s, IndexAlgoParamPositionFree+" = true") + require.Contains(t, s, IndexAlgoParamMaxIndexCapacity+" = 1000") + require.Contains(t, s, IndexAlgoParamMaxPostingsCapacity+" = 8000") + + // position_free=false ⇒ positional ⇒ NOT rendered (only recorded/rendered when true). + params2, err := IndexParamsMapToJsonString(map[string]string{IndexAlgoParamPositionFree: "false"}) + require.NoError(t, err) + s2, err := IndexParamsToStringList(params2) + require.NoError(t, err) + require.NotContains(t, s2, IndexAlgoParamPositionFree) +} + +// TestIndexParamsToStringListAsyncCadence pins that the AUTO_UPDATE compaction cadence — +// DAY / HOUR / SECOND — round-trips through the shared SHOW CREATE / checkpoint-restore +// renderer. SECOND in particular was dropped, so an async index (fulltext2 / ivfflat / +// cagra / ivfpq — all persist it) created with SECOND=5 silently lost its cadence on +// SHOW CREATE / checkpoint restore and reverted to the idxcron default interval. +func TestIndexParamsToStringListAsyncCadence(t *testing.T) { + params, err := IndexParamsMapToJsonString(map[string]string{ + AutoUpdate: "true", + Day: "1", + Hour: "2", + Second: "5", + }) + require.NoError(t, err) + + s, err := IndexParamsToStringList(params) + require.NoError(t, err) + require.Contains(t, s, Day+" = 1") + require.Contains(t, s, Hour+" = 2") + require.Contains(t, s, Second+" = 5") +} diff --git a/pkg/catalog/secondary_index_utils_test.go b/pkg/catalog/secondary_index_utils_test.go index 00339aaa4ce11..08705686de187 100644 --- a/pkg/catalog/secondary_index_utils_test.go +++ b/pkg/catalog/secondary_index_utils_test.go @@ -58,7 +58,7 @@ func newPrefixKeyPart(colName string, length int) *tree.KeyPart { } } -func TestIsIndexAsync(t *testing.T) { +func TestIndexParamAsync(t *testing.T) { var ( json string err error @@ -66,22 +66,22 @@ func TestIsIndexAsync(t *testing.T) { ) json = "" - ok, err = IsIndexAsync(json) + ok, err = IndexParamAsync(json) require.Nil(t, err) require.Equal(t, ok, false) json = "{}" - ok, err = IsIndexAsync(json) + ok, err = IndexParamAsync(json) require.Nil(t, err) require.Equal(t, ok, false) json = `{"async": "true"}` - ok, err = IsIndexAsync(json) + ok, err = IndexParamAsync(json) require.Nil(t, err) require.Equal(t, ok, true) json = `{"async": 1}` - _, err = IsIndexAsync(json) + _, err = IndexParamAsync(json) require.NotNil(t, err) } diff --git a/pkg/fulltext2/boolean_parity_test.go b/pkg/fulltext2/boolean_parity_test.go new file mode 100644 index 0000000000000..5c7fac03e72b8 --- /dev/null +++ b/pkg/fulltext2/boolean_parity_test.go @@ -0,0 +1,79 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Boolean-mode PARITY with classic fulltext (pkg/fulltext/fulltext_test.go): +// fulltext2 uses its own parser + WAND evaluator, but must produce the same +// MySQL boolean-mode membership as the classic engine for the same queries +// (+apple banana, +apple -banana, +apple ~banana, nested +(...), CJK +/-). +package fulltext2 + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" + "github.com/stretchr/testify/require" +) + +// classicCorpus mirrors the apple/banana doc shape used by classic fulltext's +// TestFullTextPlusOr / TestFullTextMinus / TestFullTextTilda (doc 0 has both, +// doc 1 apple-only, docs 11/12 banana-only), plus a matrix/origin group set. +func classicCorpus(t *testing.T) *Segment { + docs := []Doc{ + {int64(0), []byte("apple banana")}, + {int64(1), []byte("apple apple apple")}, + {int64(11), []byte("banana banana banana")}, + {int64(12), []byte("banana banana banana banana")}, + {int64(20), []byte("matrix origin")}, + {int64(21), []byte("matrix one")}, + {int64(22), []byte("matrix two")}, + {int64(23), []byte("matrix alone")}, + {int64(24), []byte("origin standalone")}, + } + s, err := BuildSegmentFromDocs("classic", int32(types.T_int64), docs, tokenizer.NewSimpleTokenizer()) + require.NoError(t, err) + return s +} + +// TestParityPlusOr: "+apple banana" — apple MUST, banana SHOULD (OR). Docs with +// apple match (0,1); banana-only docs (11,12) do NOT. (classic TestFullTextPlusOr) +func TestParityPlusOr(t *testing.T) { + require.ElementsMatch(t, []any{int64(0), int64(1)}, bquery(t, classicCorpus(t), "+apple banana")) +} + +// TestParityMinus: "+apple -banana" — apple MUST, banana MUST-NOT. Only doc 1 +// (apple without banana). (classic TestFullTextMinus) +func TestParityMinus(t *testing.T) { + require.Equal(t, []any{int64(1)}, bquery(t, classicCorpus(t), "+apple -banana")) +} + +// TestParityTilde: "+apple ~banana" — apple MUST, banana a penalty (not an +// exclusion): docs 0 AND 1 match, 0 ranked below 1. (classic TestFullTextTilda) +func TestParityTilde(t *testing.T) { + got := bqueryOrdered(t, classicCorpus(t), "+apple ~banana") + require.ElementsMatch(t, []any{int64(0), int64(1)}, got) + require.Equal(t, int64(1), got[0], "the banana-free doc ranks first under ~") +} + +// TestParityNestedGroup: "+matrix +(origin (one two))" — matrix AND (origin OR +// one OR two). Nested groups flatten to an OR. (classic TestPatternBoolean shape) +func TestParityNestedGroup(t *testing.T) { + require.ElementsMatch(t, []any{int64(20), int64(21), int64(22)}, + bquery(t, classicCorpus(t), "+matrix +(origin (one two))")) +} + +// TestParityPlusPlus: "+apple +banana" — both MUST → only doc 0. +func TestParityPlusPlus(t *testing.T) { + require.Equal(t, []any{int64(0)}, bquery(t, classicCorpus(t), "+apple +banana")) +} diff --git a/pkg/fulltext2/boolean_stream_test.go b/pkg/fulltext2/boolean_stream_test.go new file mode 100644 index 0000000000000..8a293ba8e8a49 --- /dev/null +++ b/pkg/fulltext2/boolean_stream_test.go @@ -0,0 +1,172 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "errors" + "reflect" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/stretchr/testify/require" +) + +// TestStreamBoolean pins the no-LIMIT boolean streaming path (Index.streamBoolean): a +// NON-disjunctive boolean query (MUST / MUST-NOT / mixed phrase) must stream EXACTLY the +// (pk, score) set the materialized SearchQuery(k=all) produces — one segment at a time, +// with no global top-K sized to globalN — must dedup a pk across the base + CDC tail +// (per-segment liveness), and must propagate an emit error. This is precisely the shape +// that used to fall back to SearchQuery(globalN) and materialize the whole live corpus +// before emitting; +alpha (matches every doc) exercises the low-selectivity case. +func TestStreamBoolean(t *testing.T) { + split := NewIndex([]*Segment{ + buildFtSeg(t, "a", 0, 0, 12), + buildFtSeg(t, "b", 1, 12, 24), + }, nil) + + cases := []struct { + pat string + nonEmpty bool + }{ + {"+alpha", true}, // MUST, matches EVERY doc (low selectivity) + {"+alpha beta", true}, // MUST + SHOULD + {"+alpha -delta", true}, // MUST + MUST-NOT + {"alpha -gamma", true}, // SHOULD + MUST-NOT (non-disjunctive via -) + {"+beta gamma", true}, // MUST + SHOULD + {`+alpha "beta gamma"`, false}, // MUST + phrase (mixed) — parity even if empty + } + + // Guard: every case is genuinely non-disjunctive, so StreamQuery routes it to + // streamBoolean (NOT streamWAND / streamPhrase). If a future refactor made one + // disjunctive, this test would silently exercise the wrong path — so pin it here. + for _, c := range cases { + q, err := buildBooleanQuery(c.pat, normalizeParser(ParserDefault)) + require.NoErrorf(t, err, "build %q", c.pat) + _, disj := disjunctiveTerms(q) + require.Falsef(t, disj, "%q must be non-disjunctive (exercise streamBoolean)", c.pat) + } + + collect := func(pattern string) map[int64]float64 { + m := map[int64]float64{} + err := split.StreamQuery([]byte(pattern), true, ParserDefault, BM25, nil, false, + func(o *vectorindex.SearchOutput) error { + require.LessOrEqual(t, o.Keys.N, streamBatch) + for i, k := range int64ColumnBuffer(o.Keys) { + _, dup := m[k] + require.Falsef(t, dup, "pk %d emitted twice", k) // liveness dedup + m[k] = float64(o.Dists[i]) + } + return nil + }) + require.NoError(t, err) + return m + } + + for _, c := range cases { + want, err := split.SearchQuery([]byte(c.pat), true, ParserDefault, BM25, 1000, nil) + require.NoError(t, err) + got := collect(c.pat) + require.Equalf(t, len(want), len(got), "%q: same match count", c.pat) + for _, r := range want { + g, ok := got[r.Pk.(int64)] + require.Truef(t, ok, "%q: pk %v streamed", c.pat, r.Pk) + require.InDeltaf(t, r.Score, g, 1e-5, "%q: pk %v score", c.pat, r.Pk) + } + if c.nonEmpty { + require.NotEmptyf(t, got, "%q: expected a non-empty streamed result", c.pat) + } + } + + // emit-error path: the callback error propagates and stops the boolean walk. + sentinel := errors.New("consumer aborted") + err := split.StreamQuery([]byte("+alpha beta"), true, ParserDefault, BM25, nil, false, + func(o *vectorindex.SearchOutput) error { return sentinel }) + require.ErrorIs(t, err, sentinel) +} + +// buildPhraseSeg builds a segment [lo,hi) where EVERY doc is "common foo bar" — so the +// term "common" and the contiguous phrase "foo bar" each match every doc (low selectivity). +func buildPhraseSeg(t *testing.T, id string, rec int64, lo, hi int) *Segment { + b := NewBuilder(id, int32(types.T_int64)) + for i := lo; i < hi; i++ { + feed(t, b, int64(i), "common", "foo", "bar") + } + s, err := b.Finish() + require.NoError(t, err) + s.Recency = rec + return s +} + +// TestStreamBooleanMixedPhraseLowSelectivity is the bounded-memory regression for the +// no-LIMIT streaming path: a mixed boolean query with a LOW-SELECTIVITY phrase over MANY +// segments must stream correctly WITHOUT retaining every segment's phrase hits. The old +// phraseHitsCache held one []docTf per (phrase, segment) for the whole query, so the first +// segment's scoring — which triggers the cross-segment phraseDf scan — materialized O(the +// global matching corpus) before a single row could be emitted (CN OOM). The phrase here +// matches ALL 120 docs across 3 segments; the streamed result must equal the materialized +// SearchQuery and be non-empty. (Correctness of count-and-discard phraseDf: the global +// phrase idf must still be identical to the retaining version — verified via score parity.) +func TestStreamBooleanMixedPhraseLowSelectivity(t *testing.T) { + idx := NewIndex([]*Segment{ + buildPhraseSeg(t, "a", 0, 0, 40), + buildPhraseSeg(t, "b", 1, 40, 80), + buildPhraseSeg(t, "c", 2, 80, 120), + }, nil) + + const pat = `+common "foo bar"` // MUST term + phrase (mixed, non-disjunctive → streamBoolean) + q, err := buildBooleanQuery(pat, normalizeParser(ParserDefault)) + require.NoError(t, err) + _, disj := disjunctiveTerms(q) + require.False(t, disj, "must route to streamBoolean") + + got := map[int64]float64{} + err = idx.StreamQuery([]byte(pat), true, ParserDefault, BM25, nil, false, + func(o *vectorindex.SearchOutput) error { + require.LessOrEqual(t, o.Keys.N, streamBatch) + for i, k := range int64ColumnBuffer(o.Keys) { + _, dup := got[k] + require.Falsef(t, dup, "pk %d emitted twice", k) + got[k] = float64(o.Dists[i]) + } + return nil + }) + require.NoError(t, err) + + want, err := idx.SearchQuery([]byte(pat), true, ParserDefault, BM25, 1000, nil) + require.NoError(t, err) + require.NotEmpty(t, got) // non-empty, low selectivity + require.Equal(t, 120, len(got)) // the phrase matches every doc across all 3 segments + require.Equal(t, len(want), len(got)) // parity with the materialized path + for _, r := range want { + g, ok := got[r.Pk.(int64)] + require.Truef(t, ok, "pk %v streamed", r.Pk) + require.InDeltaf(t, r.Score, g, 1e-5, "pk %v score parity (global phrase idf unchanged)", r.Pk) + } +} + +// TestGlobalStatsNoPhraseHitRetention structurally guards the fix above: global phrase DF +// must be count-and-discard, so globalStats must NOT hold a field that retains per-segment +// matchPhrase slices ([]docTf). Reintroducing a cross-segment []docTf cache (the removed +// phraseHitsCache) would defeat the no-LIMIT streaming O(one segment) bound. +func TestGlobalStatsNoPhraseHitRetention(t *testing.T) { + typ := reflect.TypeOf(globalStats{}) + for i := 0; i < typ.NumField(); i++ { + f := typ.Field(i) + require.NotContainsf(t, f.Type.String(), "docTf", + "globalStats.%s (%s) retains phrase hit slices; global phrase DF must count-and-discard, not cache []docTf", + f.Name, f.Type) + } +} diff --git a/pkg/fulltext2/boolean_test.go b/pkg/fulltext2/boolean_test.go new file mode 100644 index 0000000000000..a5b3f38b20e0e --- /dev/null +++ b/pkg/fulltext2/boolean_test.go @@ -0,0 +1,166 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "testing" + + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" + "github.com/stretchr/testify/require" +) + +func bquery(t *testing.T, s *Segment, q string) []any { + t.Helper() + rs, err := s.SearchBooleanText([]byte(q), tokenizer.NewSimpleTokenizer(), TfIdf, 10) + require.NoError(t, err) + return pkslice(rs) +} + +// TestBooleanMode covers the + - " " * and bare-OR surface over the shared +// fulltext corpus. +func TestBooleanMode(t *testing.T) { + s := fulltextCorpus(t) + + // MUST (AND) + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4)}, bquery(t, s, "+quick +fox")) + // MUST + MUST-NOT + require.Equal(t, []any{int64(2)}, bquery(t, s, "+quick -jumps")) + // SHOULD (OR) minus MUST-NOT + require.ElementsMatch(t, []any{int64(2), int64(4)}, bquery(t, s, "fox -dog")) + // a required absent term → no matches + require.Empty(t, bquery(t, s, "+fox +cat")) + + // quoted phrase (bare) and required phrase + require.ElementsMatch(t, []any{int64(1), int64(3)}, bquery(t, s, `"lazy dog"`)) + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4)}, bquery(t, s, `+"quick brown fox"`)) + + // bare OR of two separate words + require.ElementsMatch(t, []any{int64(3), int64(4)}, bquery(t, s, "sleeps high")) + + // prefix: qui* → quick + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4)}, bquery(t, s, "qui*")) + // prefix in a MUST, combined with another MUST: (day|dog) AND fox → docs 1,5 + require.ElementsMatch(t, []any{int64(1), int64(5)}, bquery(t, s, "+d* +fox")) +} + +// TestBooleanNonContiguousPhrase: a phrase clause still requires adjacency. +func TestBooleanNonContiguousPhrase(t *testing.T) { + s := fulltextCorpus(t) + // "quick fox" as a phrase is never adjacent (brown between) → no matches, + // even though both terms exist. + require.Empty(t, bquery(t, s, `+"quick fox"`)) +} + +// TestBooleanOnLoadedSegment exercises the loaded (FST) path, including prefix +// expansion over the FST iterator. +func TestBooleanOnLoadedSegment(t *testing.T) { + data, err := fulltextCorpus(t).Serialize() + require.NoError(t, err) + loaded, err := Deserialize("seg", bytes.NewReader(data)) + require.NoError(t, err) + t.Cleanup(func() { _ = loaded.dict.Close() }) + + require.Equal(t, []any{int64(2)}, bquery(t, loaded, "+quick -jumps")) + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4)}, bquery(t, loaded, "qui*")) + require.ElementsMatch(t, []any{int64(1), int64(5)}, bquery(t, loaded, "+d* +fox")) +} + +// bqueryOrdered runs a boolean query and returns pks in ranked order. +func bqueryOrdered(t *testing.T, s *Segment, q string) []any { + t.Helper() + rs, err := s.SearchBooleanText([]byte(q), tokenizer.NewSimpleTokenizer(), TfIdf, 10) + require.NoError(t, err) + return pkslice(rs) +} + +// TestBooleanGroups: ( ) groups — OR of children (max sub-scorer), combinable +// with + and nestable. +func TestBooleanGroups(t *testing.T) { + s := fulltextCorpus(t) + // (quick OR sleeps) AND fox + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4)}, bquery(t, s, "+(quick sleeps) +fox")) + // (lazy OR high) AND fox + require.ElementsMatch(t, []any{int64(1), int64(4)}, bquery(t, s, "+(lazy high) +fox")) + // bare group = OR + require.ElementsMatch(t, []any{int64(3), int64(4)}, bquery(t, s, "(sleeps high)")) + // nested group + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4), int64(5)}, bquery(t, s, "+((quick) fox)")) +} + +// TestBooleanWeights: > boosts and < demotes a SHOULD clause's rank without +// changing membership. +func TestBooleanWeights(t *testing.T) { + s := fulltextCorpus(t) + // membership is unchanged by a weight operator + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4)}, bquery(t, s, ">quick")) + + // sleeps(doc3) and high(doc4) score equally → tied, order unspecified. + require.ElementsMatch(t, []any{int64(3), int64(4)}, bqueryOrdered(t, s, "sleeps high")) + // boosting high gives it a strictly higher score, so it ranks first: [4,3]. + require.Equal(t, []any{int64(4), int64(3)}, bqueryOrdered(t, s, "sleeps >high")) +} + +// TestBooleanTilde: ~ penalizes but does not exclude — jumps-bearing docs (1,4) +// sink below the others (2,5), all still present. +func TestBooleanTilde(t *testing.T) { + s := fulltextCorpus(t) + // Two equal-score groups: {2,5} (fox, unpenalized) rank above {1,4} (fox + ~jumps + // penalty); order WITHIN each tied group is unspecified. + got := bqueryOrdered(t, s, "fox ~jumps") + require.Len(t, got, 4) + require.ElementsMatch(t, []any{int64(2), int64(5)}, got[:2]) + require.ElementsMatch(t, []any{int64(1), int64(4)}, got[2:]) +} + +// TestBooleanGroupsLoaded exercises groups/weights on the loaded (FST) path. +func TestBooleanGroupsLoaded(t *testing.T) { + data, err := fulltextCorpus(t).Serialize() + require.NoError(t, err) + loaded, err := Deserialize("seg", bytes.NewReader(data)) + require.NoError(t, err) + t.Cleanup(func() { _ = loaded.dict.Close() }) + + require.ElementsMatch(t, []any{int64(1), int64(4)}, bquery(t, loaded, "+(lazy high) +fox")) + // {2,5} rank above {1,4}; order within each tied group is unspecified (see TestBooleanTilde). + got := bqueryOrdered(t, loaded, "fox ~jumps") + require.Len(t, got, 4) + require.ElementsMatch(t, []any{int64(2), int64(5)}, got[:2]) + require.ElementsMatch(t, []any{int64(1), int64(4)}, got[2:]) +} + +// TestScanBoolean checks the top-level clause scanner directly. +func TestScanBoolean(t *testing.T) { + got := scanBoolean(`+quick -"lazy dog" fox*`) + require.Len(t, got, 3) + require.Equal(t, rawClause{prefix: '+', text: "quick"}, got[0]) + require.Equal(t, rawClause{prefix: '-', quoted: true, text: "lazy dog"}, got[1]) + require.Equal(t, rawClause{star: true, text: "fox"}, got[2]) +} + +// TestScanBooleanGroups: nested groups and weight prefixes scan correctly. +func TestScanBooleanGroups(t *testing.T) { + got := scanBoolean(`>quick +(lazy dog) ~jumps`) + require.Len(t, got, 3) + require.Equal(t, byte('>'), got[0].prefix) + require.Equal(t, "quick", got[0].text) + require.Equal(t, byte('+'), got[1].prefix) + require.True(t, got[1].group) + require.Len(t, got[1].children, 2) + require.Equal(t, "lazy", got[1].children[0].text) + require.Equal(t, "dog", got[1].children[1].text) + require.Equal(t, byte('~'), got[2].prefix) + require.Equal(t, "jumps", got[2].text) +} diff --git a/pkg/fulltext2/boolean_tree_test.go b/pkg/fulltext2/boolean_tree_test.go new file mode 100644 index 0000000000000..38d92d4d27c0c --- /dev/null +++ b/pkg/fulltext2/boolean_tree_test.go @@ -0,0 +1,112 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Boolean query-tree printer — ported verbatim from classic fulltext +// (pkg/fulltext/fulltext_test.go TestPatternBoolean / TestPatternPhrase). +// BooleanTreeString reuses fulltext.PatternToString, so the printed S-expression +// is identical to classic. +package fulltext2 + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestBooleanTreeString(t *testing.T) { + cases := []struct { + pattern string + expect string + }{ + {"Ma'trix Origin", "(text 0 ma'trix) (text 1 origin)"}, + {"Matrix Origin", "(text 0 matrix) (text 1 origin)"}, + {"+Matrix Origin", "(+ (text 0 matrix)) (text 1 origin)"}, + {"+Matrix -Origin", "(+ (text 0 matrix)) (- (text 1 origin))"}, + {"Matrix ~Origin", "(text 0 matrix) (~ (text 1 origin))"}, + {"Matrix +(One)", "(+ (group (< (text 0 origin)) (> (text 1 one)))) (text 2 matrix)"}, + {"+Matrix +Origin", "(join 0 (+ (text 0 matrix)) (+ (text 0 origin)))"}, + {`"Matrix origin"`, "(phrase (text 0 matrix) (text 1 origin))"}, + {"Matrix Origin*", "(text 0 matrix) (* 1 origin*)"}, + {"+Matrix +(Origin (One Two))", "(+ (text 0 matrix)) (+ (group (text 1 origin) (group (text 2 one) (text 3 two))))"}, + {"+读写汉字 -学中文", "(+ (text 0 读写汉字)) (- (text 1 学中文))"}, + {"+读书会 +提效 +社群 +案例 +运营", "(join 0 (+ (text 0 读书会)) (+ (text 0 提效)) (+ (text 0 社群)) (+ (text 0 案例)) (+ (text 0 运营)))"}, + } + for _, c := range cases { + got, err := BooleanTreeString(c.pattern) + require.NoError(t, err, c.pattern) + require.Equal(t, c.expect, got, "pattern %q", c.pattern) + } +} + +func TestBooleanTreeStringWithPosition(t *testing.T) { + cases := []struct { + pattern string + expect string + }{ + {`"Ma'trix Origin"`, "(phrase (text 0 0 ma'trix) (text 1 12 origin))"}, + {`"Matrix Origin"`, "(phrase (text 0 0 matrix) (text 1 7 origin))"}, + {`"Matrix"`, "(phrase (text 0 0 matrix))"}, + {`" Matrix "`, "(phrase (text 0 0 matrix))"}, + {`"Matrix Origin"`, "(phrase (text 0 0 matrix) (text 1 11 origin))"}, + {`" 你好嗎? Hello World 在一起 Happy 再见 "`, + "(phrase (text 0 0 你好嗎?) (text 1 11 hello) (text 2 17 world) (text 3 24 在一起) (text 4 35 happy) (text 5 42 再见))"}, + } + for _, c := range cases { + got, err := BooleanTreeStringWithPosition(c.pattern) + require.NoError(t, err, c.pattern) + require.Equal(t, c.expect, got, "pattern %q", c.pattern) + } +} + +// Ported from classic TestPatternNL: NL-mode ngram/word expansion with positions +// (short tokens → * prefix, CJK → 3-grams, mixed CJK/latin). +func TestNLTreeStringWithPosition(t *testing.T) { + cases := []struct { + pattern string + expect string + }{ + {"Ma'trix Origin", "(* 0 0 ma*) (text 1 3 trix) (text 2 8 origin)"}, + {"Matrix Origin", "(text 0 0 matrix) (text 1 7 origin)"}, + {"读写汉字 学中文", "(text 0 0 读写汉) (text 1 3 写汉字) (text 2 13 学中文)"}, + {"读写", "(* 0 0 读写*)"}, + {"肥胖的原因都是因为摄入脂肪多导致的吗", + "(text 0 0 肥胖的) (text 1 9 原因都) (text 2 18 是因为) (text 3 27 摄入脂) (text 4 36 肪多导) (text 5 45 致的吗)"}, + {"肥胖的原因都是因为摄入fat多导致的吗", + "(text 0 0 肥胖的) (text 1 9 原因都) (text 2 18 是因为) (text 3 24 为摄入) (text 4 33 fat) (text 5 36 多导致) (text 6 42 致的吗)"}, + } + for _, c := range cases { + got, err := NLTreeStringWithPosition(c.pattern) + require.NoError(t, err, c.pattern) + require.Equal(t, c.expect, got, "pattern %q", c.pattern) + } +} + +// Ported from classic TestPatternFail: malformed boolean queries error. +func TestBooleanTreeStringFail(t *testing.T) { + for _, p := range []string{ + "Matrix Origin( ", "(+Matrix Origin", "++Matrix -Origin", + "Matrix ~~Origin", "Matrix +(<(+Origin -apple) >One)", "+Matrix --Origin", + } { + _, err := BooleanTreeString(p) + require.Error(t, err, "pattern %q should fail", p) + } +} + +// Ported from classic TestPatternNLFail: malformed NL queries error. +func TestNLTreeStringFail(t *testing.T) { + for _, p := range []string{"+[[[", "+''"} { + _, err := NLTreeString(p) + require.Error(t, err, "pattern %q should fail", p) + } +} diff --git a/pkg/fulltext2/builder_test.go b/pkg/fulltext2/builder_test.go new file mode 100644 index 0000000000000..aac52ec7ac59d --- /dev/null +++ b/pkg/fulltext2/builder_test.go @@ -0,0 +1,184 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Builder API parity with bm25.wand.Builder: NewBuilder(id, pkType) / Add(word, pk) +// / Finish / FinishSegments(capacity), so the two engines can share a core later. +// fulltext2's builder additionally records token POSITIONS (Add order within a doc). +package fulltext2 + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// feed adds a doc's tokens in order (mirrors the fulltext2_create per-token Add loop). +// These unit tests use the token index as a synthetic byte position — self-consistent +// with phr() below, which builds consecutive-offset phrase slots. (Real byte positions +// are exercised by the text-based / CJK tests via BuildSegmentFromDocsParser + SearchQuery.) +func feed(t *testing.T, b *Builder, pk any, words ...string) { + pos := int32(0) + for _, w := range words { + require.NoError(t, b.Add(w, pos, pk)) + pos += int32(len(w)) + 1 // byte position of single-space-joined words + } +} + +// phr builds phrase slots for a contiguous term list at the byte offsets a +// single-space-joined query text would tokenize to — matching feed's positions and the +// production query path (tokenizing "a b" → a@0, b@len(a)+1). +func phr(terms ...string) []phraseSlot { + s := make([]phraseSlot, len(terms)) + pos := int32(0) + for i, t := range terms { + s[i] = phraseSlot{term: t, off: pos} + pos += int32(len(t)) + 1 + } + return s +} + +// TestBuilderSingleSegment: Finish() builds one positional segment; phrase and +// term queries resolve against it. +func TestBuilderSingleSegment(t *testing.T) { + b := NewBuilder("idx", int32(types.T_int64)) + feed(t, b, int64(0), "quick", "brown", "fox") + feed(t, b, int64(1), "brown", "fox", "jumps") + feed(t, b, int64(2), "quick", "red", "fox") + require.Equal(t, 3, b.NumDocs()) + + seg, err := b.Finish() + require.NoError(t, err) + idx := NewIndex([]*Segment{seg}, nil) + + // exact phrase "brown fox" is contiguous in docs 0 and 1, not 2. + require.ElementsMatch(t, []any{int64(0), int64(1)}, resultIDs(idx.SearchPhrase(phr("brown", "fox"), BM25, 10, nil))) + // single term. + require.ElementsMatch(t, []any{int64(0), int64(2)}, resultIDs(idx.SearchPhrase(phr("quick"), BM25, 10, nil))) +} + +// TestBuilderCapacitySplit: FinishSegments(capacity) splits by contiguous doc-ord +// range into ceil(n/capacity) segments; the merged Index still finds every doc. +func TestBuilderCapacitySplit(t *testing.T) { + b := NewBuilder("idx", int32(types.T_int64)) + for i := 0; i < 10; i++ { + feed(t, b, int64(i), "common", "brown") + } + segs, err := b.FinishSegments(3) // 10 docs / 3 => 4 segments + require.NoError(t, err) + require.Len(t, segs, 4) + require.Equal(t, int64(3), segs[0].N) + require.Equal(t, int64(1), segs[3].N) // remainder + + idx := NewIndex(segs, nil) + got := resultIDs(idx.SearchPhrase(phr("brown"), BM25, 100, nil)) + require.Len(t, got, 10) // all docs across the 4 bases +} + +// TestBuilderEmpty: no Add → no segments. +func TestBuilderEmpty(t *testing.T) { + b := NewBuilder("idx", int32(types.T_int64)) + segs, err := b.FinishSegments(0) + require.NoError(t, err) + require.Nil(t, segs) +} + +// TestBuilderNumPostings: NumPostings counts term occurrences (one per Add), not docs +// — the memory-correlated seal dimension. Three 2-token docs => 6 postings, 3 docs. +func TestBuilderNumPostings(t *testing.T) { + b := NewBuilder("idx", int32(types.T_int64)) + feed(t, b, int64(0), "a", "b") + feed(t, b, int64(1), "c", "d") + feed(t, b, int64(2), "e", "f") + require.Equal(t, 3, b.NumDocs()) + require.Equal(t, 6, b.NumPostings()) +} + +// TestReachedSegmentCap: a segment seals on whichever cap (docs OR postings) is hit +// first, and non-positive caps fall back to the package defaults. +func TestReachedSegmentCap(t *testing.T) { + // Doc cap fires first: 2 single-token docs reach docCap=2 before postingCap=100. + b := NewBuilder("d", int32(types.T_int64)) + feed(t, b, int64(0), "x") + require.False(t, ReachedSegmentCap(b, 2, 100)) // 1 doc / 1 posting + feed(t, b, int64(1), "y") + require.True(t, ReachedSegmentCap(b, 2, 100)) // 2 docs => doc cap + + // Posting cap fires first: one long doc crosses postingCap=3 while docCap=100 idle. + // This is the long-document case a doc-only seal would miss. + b2 := NewBuilder("p", int32(types.T_int64)) + feed(t, b2, int64(0), "a", "b", "c") // 1 doc, 3 postings + require.True(t, ReachedSegmentCap(b2, 100, 3)) + require.False(t, ReachedSegmentCap(b2, 100, 4)) // 3 postings < 4 + + // Non-positive caps fall back to defaults (both large) — a tiny builder never seals. + require.False(t, ReachedSegmentCap(b2, 0, 0)) + require.Less(t, int64(b2.NumDocs()), DefaultBuildCapacity) + require.Less(t, int64(b2.NumPostings()), DefaultPostingCapacity) +} + +// TestReachedSegmentCapDataBytes: one-token rows with wide varlena pk/INCLUDE reach neither +// the doc nor posting cap, so the payload-bytes cap must bound them. Also checks the byte +// accounting (varlena pk + INCLUDE, NULL = 0, replace-on-upsert delta). +func TestReachedSegmentCapDataBytes(t *testing.T) { + // varchar pk (10 B) + one INCLUDE value (5 B) on a single-token doc. + b := NewBuilder("d", int32(types.T_varchar)) + require.NoError(t, b.Add("tok", 0, "PPPPPPPPPP")) // pk = 10 bytes + b.SetInclude("PPPPPPPPPP", []any{"IIIII"}) // +5 include bytes + require.EqualValues(t, 15, b.NumDataBytes()) + + // upsert replace supersedes the INCLUDE bytes in lock-step (5 -> 2), pk unchanged. + b.SetDoc("PPPPPPPPPP", []WordPos{{Word: "tok2", Pos: 0}}, []any{"XX"}) + require.EqualValues(t, 12, b.NumDataBytes()) // 10 pk + 2 include + + // 1 doc / 1 posting: doc & posting caps idle, but the byte cap seals when dataBytes hits it. + require.False(t, ReachedSegmentCap(b, 0, 0)) + b.dataBytes = DefaultDataBytesCapacity + require.True(t, ReachedSegmentCap(b, 0, 0)) + + // fixed pk (8 B est.) + INCLUDE with a NULL (0) and a 2-byte value. + b2 := NewBuilder("n", int32(types.T_int64)) + require.NoError(t, b2.Add("t", 0, int64(1))) + b2.SetInclude(int64(1), []any{nil, "ab"}) + require.EqualValues(t, 10, b2.NumDataBytes()) // 8 pk + 0 + 2 +} + +// TestBuilderPostingSplitStreaming: the streaming seal pattern the build paths use — +// ReachedSegmentCap on a posting cap seals a long-document corpus into multiple +// segments even though the doc cap is never reached; the merged Index finds every doc. +func TestBuilderPostingSplitStreaming(t *testing.T) { + const postingCap = int64(4) + var segs []*Segment + cur := NewBuilder("s", int32(types.T_int64)) + seal := func() { + if cur.NumDocs() == 0 { + return + } + seg, err := cur.Finish() + require.NoError(t, err) + segs = append(segs, seg) + cur = NewBuilder("s", int32(types.T_int64)) + } + // 6 docs × 2 tokens = 12 postings; docCap huge, postingCap=4 => seal every 2 docs. + for i := 0; i < 6; i++ { + feed(t, cur, int64(i), "common", "brown") + if ReachedSegmentCap(cur, 1_000_000, postingCap) { + seal() + } + } + seal() + require.Len(t, segs, 3) // 12 postings / 4 => 3 segments + idx := NewIndex(segs, nil) + require.Len(t, resultIDs(idx.SearchPhrase(phr("brown"), BM25, 100, nil)), 6) +} diff --git a/pkg/fulltext2/cdc_lww_test.go b/pkg/fulltext2/cdc_lww_test.go new file mode 100644 index 0000000000000..bd267e12ef66b --- /dev/null +++ b/pkg/fulltext2/cdc_lww_test.go @@ -0,0 +1,408 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// CDC last-write-wins semantics (engine layer, no live DB): a later upsert of a pk +// must REPLACE — not merge with — its earlier terms, and an upsert to empty/NULL text +// must shadow the old version. Drives the whole write path (TailBuilder -> frames -> +// Index-with-liveness) and asserts the resolved search results, without reordering the +// delete-before-insert frame order (which CDC relies on for same-batch updates). +package fulltext2 + +import ( + "fmt" + "os" + "strings" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// mkBase builds a tag=0 base segment (recency 0) from pk->text. +func mkBase(t *testing.T, pkTexts map[int64]string) *Segment { + t.Helper() + bb := NewBuilder("base", int32(types.T_int64)) + for pk, txt := range pkTexts { + feed(t, bb, pk, strings.Fields(txt)...) + } + seg, err := bb.Finish() + require.NoError(t, err) + seg.Recency = 0 + return seg +} + +// buildTailIndexLWW runs c through a TailBuilder at capacity, decodes the spilled frames in +// Finish order (recency = position, above the base) exactly like LoadTailSegments, and assembles +// the queryable Index. Single-blob convenience over buildTailIndexLWWMulti. +func buildTailIndexLWW(t *testing.T, base *Segment, c *Cdc, capacity int64) *Index { + t.Helper() + return buildTailIndexLWWMulti(t, base, capacity, c) +} + +// buildTailIndexLWWMulti runs SEVERAL CDC blobs through ONE TailBuilder — mimicking the real +// producer, which flushes a new blob on every op change, so an INSERT and a later DELETE of the +// same pk arrive as SEPARATE AddBatch calls. It then assembles the Index from the Finish-order +// frames (recency = position), exercising cross-blob last-writer-wins. +func buildTailIndexLWWMulti(t *testing.T, base *Segment, capacity int64, cdcs ...*Cdc) *Index { + t.Helper() + tb, err := NewTailBuilder(int32(types.T_int64), capacity, 0, "", wsTokenize) + require.NoError(t, err) + defer tb.Cleanup() + for _, c := range cdcs { + require.NoError(t, tb.AddBatch(c)) + } + segs, err := tb.Finish() + require.NoError(t, err) + return framesToIndex(t, base, segs) +} + +// framesToIndex decodes packed tail frames in Finish order (recency = position, above the base) +// exactly like LoadTailSegments, and assembles the queryable Index. Shared by the LWW helpers and +// the packed-spool test. +func framesToIndex(t *testing.T, base *Segment, segs []TailSegment) *Index { + t.Helper() + var tails []*Segment + if base != nil { + tails = append(tails, base) + } + deletes := map[any]int64{} + recency := int64(100) + for i, ts := range segs { + framed := readTailFrame(t, ts) + seg, dels, uerr := UnframeTail("tail", framed) + require.NoError(t, uerr) + switch { + case seg != nil: + seg.Recency = recency + int64(i) + tails = append(tails, seg) + case dels != nil: + deletes = foldDeleteFrame(deletes, dels, recency+int64(i)) + } + } + return NewIndex(tails, deletes) +} + +// distinctPaths counts the distinct spool files a frame set occupies. +func distinctPaths(segs []TailSegment) int { + seen := map[string]struct{}{} + for _, s := range segs { + seen[s.Path] = struct{}{} + } + return len(seen) +} + +// TestCdcInsertThenDeleteAcrossCdcBlobs is the cross-blob regression: the producer flushes a new +// CDC blob on each op change, so INSERT(pk) then a later DELETE(pk) reach ONE TailBuilder as TWO +// AddBatch calls. The later DELETE must win — the covered 0-JOIN path has no source join to mask a +// resurrected row. (Before the session-wide fix, delete-first framing gave the older insert the +// higher recency and returned the deleted doc.) +func TestCdcInsertThenDeleteAcrossCdcBlobs(t *testing.T) { + ins := NewCdc(int32(types.T_int64)) + ins.Insert(int64(1), "ghost", nil) + del := NewCdc(int32(types.T_int64)) + del.Delete(int64(1)) + idx := buildTailIndexLWWMulti(t, nil, 1000, ins, del) // two blobs, one builder + require.Empty(t, queryIDs(t, idx, "ghost"), "the later DELETE must win across CDC blobs") +} + +// TestCdcDeleteThenReinsertAcrossCdcBlobs: DELETE(pk) then a later INSERT(pk) across blobs — the +// re-insert wins (it removes the pending tombstone), no phantom delete. +func TestCdcDeleteThenReinsertAcrossCdcBlobs(t *testing.T) { + del := NewCdc(int32(types.T_int64)) + del.Delete(int64(1)) + ins := NewCdc(int32(types.T_int64)) + ins.Insert(int64(1), "reborn", nil) + idx := buildTailIndexLWWMulti(t, nil, 1000, del, ins) + require.Equal(t, []any{int64(1)}, queryIDs(t, idx, "reborn"), "re-insert after delete must be live") +} + +// TestCdcUpdateAcrossCdcBlobs: an UPDATE split across blobs (DELETE(base pk) then INSERT(pk,new)) +// keeps the new version live and the old gone — no lingering tombstone resurrects the delete. +func TestCdcUpdateAcrossCdcBlobs(t *testing.T) { + base := mkBase(t, map[int64]string{1: "oldterm"}) + del := NewCdc(int32(types.T_int64)) + del.Delete(int64(1)) + ins := NewCdc(int32(types.T_int64)) + ins.Insert(int64(1), "newterm", nil) + idx := buildTailIndexLWWMulti(t, base, 1000, del, ins) + require.Empty(t, queryIDs(t, idx, "oldterm"), "old version superseded by the update") + require.Equal(t, []any{int64(1)}, queryIDs(t, idx, "newterm"), "updated version is live") +} + +// readTailFrame reads one frame's bytes from its (now packed) spool file, honoring Offset/FrameLen +// exactly as persist's load_file range does — so tests decode the same bytes the DB would store. +func readTailFrame(t *testing.T, ts TailSegment) []byte { + t.Helper() + all, err := os.ReadFile(ts.Path) + require.NoError(t, err) + require.LessOrEqual(t, int(ts.Offset)+ts.FrameLen, len(all), "frame range must lie within the spool file") + return all[ts.Offset : ts.Offset+int64(ts.FrameLen)] +} + +// TestCdcPackedSpoolBoundedFiles is the regression for the reviewer's unbounded-file finding: an +// alternating DELETE,INSERT,… update stream still produces one FRAME per op run (chronological seal +// preserved — LWW unchanged), but the frames are PACKED into a bounded number of spool files instead +// of one file per frame. Proves file count is bounded while frame/chunk count and LWW are unchanged. +func TestCdcPackedSpoolBoundedFiles(t *testing.T) { + const n = 200 + baseTexts := map[int64]string{} + for i := int64(1); i <= n; i++ { + baseTexts[i] = fmt.Sprintf("old%d", i) + } + base := mkBase(t, baseTexts) + + var blobs []*Cdc + for i := int64(1); i <= n; i++ { + del := NewCdc(int32(types.T_int64)) + del.Delete(i) + ins := NewCdc(int32(types.T_int64)) + ins.Insert(i, fmt.Sprintf("new%d", i), nil) + blobs = append(blobs, del, ins) + } + + tb, err := NewTailBuilder(int32(types.T_int64), 1000, 0, "", wsTokenize) + require.NoError(t, err) + defer tb.Cleanup() + for _, c := range blobs { + require.NoError(t, tb.AddBatch(c)) + } + segs, err := tb.Finish() + require.NoError(t, err) + + // Frames are NOT collapsed — one per op run (the faithful op log is preserved). + require.Greater(t, len(segs), n, "chronological seal keeps one frame per op run") + // ...but they pack into a SINGLE default spool file — not len(segs) files. + require.Equal(t, 1, distinctPaths(segs), "tiny frames pack into one spool file, not one file per frame") + + // LWW still correct after packing: each pk updated to its new text, old gone. + idx := framesToIndex(t, base, segs) + for i := int64(1); i <= n; i++ { + require.Emptyf(t, queryIDs(t, idx, fmt.Sprintf("old%d", i)), "old version of pk %d gone", i) + require.Equalf(t, []any{i}, queryIDs(t, idx, fmt.Sprintf("new%d", i)), "updated pk %d live", i) + } + + // With a roll cap of ~1/4 the total frame bytes the spool ROLLS to a handful of files, but the + // count stays bounded (bytes/rollCap ≈ 4–5) — far below one file per frame — and LWW is + // unaffected by which file a frame lands in. + var total int64 + for _, s := range segs { + total += int64(s.FrameLen) + } + tb2, err := NewTailBuilder(int32(types.T_int64), 1000, 0, "", wsTokenize) + require.NoError(t, err) + defer tb2.Cleanup() + tb2.rollCap = total / 4 // force a few rolls, many frames per file + for _, c := range blobs { + require.NoError(t, tb2.AddBatch(c)) + } + segs2, err := tb2.Finish() + require.NoError(t, err) + files := distinctPaths(segs2) + require.GreaterOrEqual(t, files, 2, "roll cap forces multiple spool files") + require.Less(t, files, len(segs2)/10, "each spool file still packs many frames (not one-per-file)") + idx2 := framesToIndex(t, base, segs2) + require.Equal(t, []any{int64(1)}, queryIDs(t, idx2, "new1"), "LWW holds across rolled spool files") + require.Empty(t, queryIDs(t, idx2, "old1")) +} + +func queryIDs(t *testing.T, idx *Index, word string) []any { + t.Helper() + res, err := idx.SearchQuery([]byte(word), false, ParserDefault, BM25, 100, nil) + require.NoError(t, err) + return resultIDs(res) +} + +// TestDirtyVsCompactedScoreParity pins the live-DF fix: a base+tail carrying DEAD copies +// (rows updated in the tail, their stale base copies not yet reclaimed) must score and rank a +// query IDENTICALLY to a clean index holding only the same LIVE rows. Before live-DF, df was +// summed over physical postings (dead copies included), inflating df past the live N and giving +// the dirty corpus different idf — hence a different ranking — until a MERGE reclaimed the dead +// copies. Covers BM25 and TF-IDF across EVERY path that consumes global DF: WAND bag-of-words +// disjunction and boolean OR (term df), and NL / boolean phrase clauses (phrase df). +func TestDirtyVsCompactedScoreParity(t *testing.T) { + // The final LIVE state a compacted index holds. + live := map[int64]string{ + 1: "x y y", + 2: "x x y", + 3: "y y y", + } + compacted := NewIndex([]*Segment{mkBase(t, live)}, nil) + + // Dirty: a base of STALE versions (extra "x" copies inflate df(x)) + a tail that upserts + // every pk to its live text, leaving the stale base copies dead (df-inflating until MERGE). + dirtyBase := mkBase(t, map[int64]string{1: "x x x x", 2: "x x x x", 3: "x x x x"}) + c := NewCdc(int32(types.T_int64)) + c.Upsert(int64(1), "x y y", nil) + c.Upsert(int64(2), "x x y", nil) + c.Upsert(int64(3), "y y y", nil) + dirty := buildTailIndexLWW(t, dirtyBase, c, 1000) + + require.Equal(t, compacted.NumDocs(), dirty.NumDocs(), "same live doc count") + + scoresOf := func(res []Result, err error) map[int64]float64 { + require.NoError(t, err) + m := make(map[int64]float64, len(res)) + for _, r := range res { + m[r.Pk.(int64)] = float64(r.Score) + } + return m + } + // Every scoring path that reads global DF, so both the term df() (WAND) and the phraseDf() + // live-fixes are exercised — SearchQuery(boolean=false) alone would only hit the phrase path. + modes := []struct { + name string + run func(idx *Index, algo ScoreAlgo, q string) map[int64]float64 + }{ + {"wand-bag", func(idx *Index, algo ScoreAlgo, q string) map[int64]float64 { // WAND disjunction -> df() + return scoresOf(idx.SearchBagOfWords([]byte(q), ParserDefault, algo, 100, nil)) + }}, + {"bool-or", func(idx *Index, algo ScoreAlgo, q string) map[int64]float64 { // boolean OR -> WAND -> df() + return scoresOf(idx.SearchQuery([]byte(q), true, ParserDefault, algo, 100, nil)) + }}, + {"nl-phrase", func(idx *Index, algo ScoreAlgo, q string) map[int64]float64 { // NL / phrase -> phraseDf() + return scoresOf(idx.SearchQuery([]byte(q), false, ParserDefault, algo, 100, nil)) + }}, + } + for _, algo := range []ScoreAlgo{BM25, TfIdf} { + for _, m := range modes { + for _, q := range []string{"x", "y", "x y"} { + cs := m.run(compacted, algo, q) + ds := m.run(dirty, algo, q) + require.Equalf(t, len(cs), len(ds), "algo=%v mode=%s q=%q same match count", algo, m.name, q) + for pk, cscore := range cs { + dscore, ok := ds[pk] + require.Truef(t, ok, "algo=%v mode=%s q=%q pk %d present in dirty result", algo, m.name, q, pk) + require.InDeltaf(t, cscore, dscore, 1e-6, "algo=%v mode=%s q=%q pk %d dirty-vs-compacted score parity", algo, m.name, q, pk) + } + } + } + } +} + +// TestBuilderSetDocReplace pins the LWW primitive: SetDoc REPLACES a pk's terms (not +// append), and a zero-word SetDoc keeps a live 0-term doc (the empty-upsert shadow). +func TestBuilderSetDocReplace(t *testing.T) { + b := NewBuilder("b", int32(types.T_int64)) + b.SetDoc(int64(1), []WordPos{{Word: "alpha", Pos: 0}}, nil) + b.SetDoc(int64(1), []WordPos{{Word: "beta", Pos: 0}, {Word: "gamma", Pos: 6}}, nil) + require.Equal(t, 1, b.NumDocs()) // same pk => one doc + require.Equal(t, 2, b.NumPostings()) // replaced: 1 -> 2, not appended to 3 + require.Equal(t, []string{"beta", "gamma"}, b.docs[0].Terms) + + // zero-word upsert => a retained 0-term doc (carries pk + recency to shadow). + b.SetDoc(int64(2), nil, nil) + require.Equal(t, 2, b.NumDocs()) + require.Empty(t, b.docs[1].Terms) + require.Equal(t, int64(2), b.docs[1].Pk) +} + +// TestCdcUpsertReplaceNoMerge: two upserts of one pk in a flush must not leave BOTH +// terms searchable (the merge bug). Only the final version survives. +func TestCdcUpsertReplaceNoMerge(t *testing.T) { + base := mkBase(t, map[int64]string{1: "alpha"}) + c := NewCdc(int32(types.T_int64)) + c.Upsert(int64(1), "beta", nil) + c.Upsert(int64(1), "gamma", nil) + idx := buildTailIndexLWW(t, base, c, 1000) + + require.Empty(t, queryIDs(t, idx, "alpha"), "base term superseded by the upsert") + require.Empty(t, queryIDs(t, idx, "beta"), "intermediate upsert term must NOT linger (no merge)") + require.Equal(t, []any{int64(1)}, queryIDs(t, idx, "gamma"), "only the final upsert term is live") +} + +// TestCdcUpsertEmptyShadows: an upsert to empty text (also how a NULL indexed column +// arrives — rowText returns "") must shadow the old indexed version. A row with no +// searchable content must stop matching its old terms. +func TestCdcUpsertEmptyShadows(t *testing.T) { + base := mkBase(t, map[int64]string{1: "alpha", 2: "delta"}) + c := NewCdc(int32(types.T_int64)) + c.Upsert(int64(1), "", nil) // empty / NULL text + idx := buildTailIndexLWW(t, base, c, 1000) + + require.Empty(t, queryIDs(t, idx, "alpha"), "emptied row must no longer match its old term") + require.Equal(t, []any{int64(2)}, queryIDs(t, idx, "delta"), "untouched row still matches") +} + +// TestCdcNonemptyThenEmptySameFlush: nonempty upsert then empty upsert of the SAME pk +// in one flush — the final (empty) state wins via in-segment replace, so neither the +// base term nor the intermediate term matches. This case is NOT masked by the +// source-join (the pk still exists), so the engine must get it right itself. +func TestCdcNonemptyThenEmptySameFlush(t *testing.T) { + base := mkBase(t, map[int64]string{1: "alpha"}) + c := NewCdc(int32(types.T_int64)) + c.Upsert(int64(1), "beta", nil) + c.Upsert(int64(1), "", nil) // then emptied + idx := buildTailIndexLWW(t, base, c, 1000) + + require.Empty(t, queryIDs(t, idx, "alpha")) + require.Empty(t, queryIDs(t, idx, "beta"), "final empty state shadows the intermediate term") +} + +// TestCdcUpsertReplaceAcrossCapacity: a repeated-pk upsert that straddles a sealed +// segment boundary — the newer copy (higher-recency segment) wins via liveness, the +// stale copy in the earlier sealed segment is dead. +func TestCdcUpsertReplaceAcrossCapacity(t *testing.T) { + c := NewCdc(int32(types.T_int64)) + c.Upsert(int64(1), "aaa", nil) + c.Upsert(int64(2), "bbb", nil) // seals seg0 (capacity 2) + c.Upsert(int64(1), "ccc", nil) // pk 1 again, now in seg1 + idx := buildTailIndexLWW(t, nil, c, 2) + + require.Empty(t, queryIDs(t, idx, "aaa"), "stale copy of pk 1 in the sealed segment is superseded") + require.Equal(t, []any{int64(2)}, queryIDs(t, idx, "bbb")) + require.Equal(t, []any{int64(1)}, queryIDs(t, idx, "ccc"), "newest copy of pk 1 wins") +} + +// TestCdcUpsertThenDeleteLWW: an UPSERT followed by a DELETE of the same pk in one flush +// resolves to the DELETE (last-writer-wins collapse) — the row is gone from search, no phantom. +// Previously the delete framed below the insert (delete-first) so the engine still returned the +// upserted term; that phantom was masked only by the source-table join, which the covered +// 0-JOIN path removes. The collapse fixes it at the source (no pk is both an insert and a +// delete), so BOTH the base term and the upserted term are gone. +func TestCdcUpsertThenDeleteLWW(t *testing.T) { + base := mkBase(t, map[int64]string{1: "alpha"}) + c := NewCdc(int32(types.T_int64)) + c.Upsert(int64(1), "beta", nil) + c.Delete(int64(1)) + idx := buildTailIndexLWW(t, base, c, 1000) + + require.Empty(t, queryIDs(t, idx, "alpha"), "base term superseded by the delete") + require.Empty(t, queryIDs(t, idx, "beta"), "upsert-then-delete: the row is gone, no phantom") +} + +// TestCdcDeleteThenUpsertLWW: an UPDATE (DELETE then INSERT of the same pk in one flush) +// resolves to the INSERT — the new version is live and supersedes the base. Confirms the LWW +// collapse does not break UPDATE (the case delete-first framing existed to protect). +func TestCdcDeleteThenUpsertLWW(t *testing.T) { + base := mkBase(t, map[int64]string{1: "alpha"}) + c := NewCdc(int32(types.T_int64)) + c.Delete(int64(1)) + c.Upsert(int64(1), "beta", nil) + idx := buildTailIndexLWW(t, base, c, 1000) + + require.Empty(t, queryIDs(t, idx, "alpha"), "old version superseded") + require.Equal(t, []any{int64(1)}, queryIDs(t, idx, "beta"), "update: new version is live") +} + +// TestCdcPlainDelete: a straight delete (no preceding same-flush upsert) removes the +// base doc — the delete tombstone (higher recency) shadows the base copy. +func TestCdcPlainDelete(t *testing.T) { + base := mkBase(t, map[int64]string{1: "alpha", 2: "delta"}) + c := NewCdc(int32(types.T_int64)) + c.Delete(int64(1)) + idx := buildTailIndexLWW(t, base, c, 1000) + + require.Empty(t, queryIDs(t, idx, "alpha"), "deleted doc is gone") + require.Equal(t, []any{int64(2)}, queryIDs(t, idx, "delta")) +} diff --git a/pkg/fulltext2/cdc_test.go b/pkg/fulltext2/cdc_test.go new file mode 100644 index 0000000000000..70c7d56957d83 --- /dev/null +++ b/pkg/fulltext2/cdc_test.go @@ -0,0 +1,200 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// CDC write-path round trip (engine layer, no live DB): Cdc/DeleteLog codecs and +// the streaming TailBuilder -> frames -> Index-with-liveness path that the ISCP +// consumer drives end-to-end. +package fulltext2 + +import ( + "strings" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +func wsTokenize(s string) []WordPos { + fields := strings.Fields(s) + out := make([]WordPos, 0, len(fields)) + i := 0 + for _, f := range fields { + pos := i + strings.Index(s[i:], f) + out = append(out, WordPos{Word: f, Pos: int32(pos)}) + i = pos + len(f) + } + return out +} + +func TestCdcCodecRoundTrip(t *testing.T) { + c := NewCdc(int32(types.T_int64)) + c.Insert(int64(2), "cherry pie", nil) + c.Delete(int64(0)) + c.Upsert(int64(1), "blueberry", nil) + require.Equal(t, 3, c.Len()) + + blob, err := c.Encode() + require.NoError(t, err) + got, err := DecodeCdc(blob) + require.NoError(t, err) + require.Equal(t, c.PkType, got.PkType) + require.Equal(t, c.Events, got.Events) + + // corruption is caught. + blob[len(blob)-1] ^= 0xFF + _, err = DecodeCdc(blob) + require.Error(t, err) +} + +func TestDeleteLogRoundTrip(t *testing.T) { + // varlena pk (length-prefixed) and int pk (bare) both round-trip. + for _, tc := range []struct { + pkType int32 + recs []DeleteRecord + }{ + {int32(types.T_int64), []DeleteRecord{{int64(0)}, {int64(7)}}}, + {int32(types.T_varchar), []DeleteRecord{{[]byte("a")}, {[]byte("bb")}}}, + } { + blob, err := EncodeDeleteLog(tc.pkType, tc.recs) + require.NoError(t, err) + got, err := DecodeDeleteLog(blob) + require.NoError(t, err) + require.Len(t, got, len(tc.recs)) + for i := range tc.recs { + require.Equal(t, normalizeKey(tc.recs[i].Pk), normalizeKey(got[i].Pk)) + } + } +} + +// TestTailBuilderLiveness drives the whole write path: a base segment + a CDC +// batch (insert new / delete old / update existing) -> TailBuilder frames -> +// Index with liveness. The deleted doc disappears, the updated doc's new text +// wins over the base copy, and the new doc appears. +func TestTailBuilderLiveness(t *testing.T) { + // tag=0 base (recency 0): doc 0 apple, doc 1 banana. + bb := NewBuilder("base", int32(types.T_int64)) + feed(t, bb, int64(0), "apple") + feed(t, bb, int64(1), "banana") + base, err := bb.Finish() + require.NoError(t, err) + base.Recency = 0 + + // One CDC flush: insert doc 2, delete doc 0, update doc 1's text. + c := NewCdc(int32(types.T_int64)) + c.Insert(int64(2), "cherry", nil) + c.Delete(int64(0)) + c.Upsert(int64(1), "blueberry", nil) + + tb, err := NewTailBuilder(int32(types.T_int64), 1000, 0, "", wsTokenize) + require.NoError(t, err) + defer tb.Cleanup() + require.NoError(t, tb.AddBatch(c)) + segs, err := tb.Finish() + require.NoError(t, err) + require.NotEmpty(t, segs) + + // Persist simulation: assign recencies in Finish() order (delete frame first, + // then insert segments), all above the base recency (0). + tails := []*Segment{base} + deletes := map[any]int64{} + recency := int64(100) + for i, ts := range segs { + framed := readTailFrame(t, ts) + seg, dels, uerr := UnframeTail("tail", framed) + require.NoError(t, uerr) + switch { + case seg != nil: + seg.Recency = recency + int64(i) + tails = append(tails, seg) + case dels != nil: + deletes = foldDeleteFrame(deletes, dels, recency+int64(i)) + } + } + + idx := NewIndex(tails, deletes) + q := func(w string) []any { + res, qerr := idx.SearchQuery([]byte(w), false, ParserDefault, BM25, 100, nil) + require.NoError(t, qerr) + return resultIDs(res) + } + require.Empty(t, q("apple"), "deleted doc 0 must be gone") + require.Empty(t, q("banana"), "updated doc 1's old text must be superseded") + require.Equal(t, []any{int64(1)}, q("blueberry"), "updated doc 1's new text wins") + require.Equal(t, []any{int64(2)}, q("cherry"), "inserted doc 2 present") +} + +// TestTailBuilderDeleteSpill: at a small capacity the delete batch spills into +// MULTIPLE delete frames instead of one. Folded liveness must be identical to the +// single-frame case — every tombstone still sorts below every insert — including a +// base-doc delete that lands in the last delete frame and an update that crosses the +// spill boundary. +func TestTailBuilderDeleteSpill(t *testing.T) { + c := NewCdc(int32(types.T_int64)) + // 5 no-op deletes + an update of base doc 1 (insert-side) + delete of base doc 0 + // + a fresh insert. Interleaved so deletes cross several spill boundaries. + c.Delete(int64(10)) + c.Delete(int64(11)) + c.Delete(int64(12)) + c.Delete(int64(13)) + c.Delete(int64(14)) + c.Upsert(int64(1), "blueberry muffin", nil) + c.Delete(int64(0)) + c.Insert(int64(2), "cherry cake", nil) + + // base (recency 0): doc 0 "apple pie", doc 1 "banana bread". + bb := NewBuilder("base", int32(types.T_int64)) + feed(t, bb, int64(0), "apple", "pie") + feed(t, bb, int64(1), "banana", "bread") + base, err := bb.Finish() + require.NoError(t, err) + base.Recency = 0 + + // capacity 2 => 6 deletes (10,11,12,13,14,0) seal into >=3 delete frames. + tb, err := NewTailBuilder(int32(types.T_int64), 2, 0, "", wsTokenize) + require.NoError(t, err) + defer tb.Cleanup() + require.NoError(t, tb.AddBatch(c)) + segs, err := tb.Finish() + require.NoError(t, err) + + tails := []*Segment{base} + deletes := map[any]int64{} + nDeleteFrames := 0 + recency := int64(100) + for i, ts := range segs { + framed := readTailFrame(t, ts) + seg, dels, uerr := UnframeTail("tail", framed) + require.NoError(t, uerr) + switch { + case seg != nil: + seg.Recency = recency + int64(i) + tails = append(tails, seg) + case dels != nil: + nDeleteFrames++ + deletes = foldDeleteFrame(deletes, dels, recency+int64(i)) + } + } + require.GreaterOrEqual(t, nDeleteFrames, 3, "6 deletes at capacity 2 must spill into multiple delete frames") + + idx := NewIndex(tails, deletes) + q := func(w string) []any { + res, qerr := idx.SearchQuery([]byte(w), false, ParserDefault, BM25, 100, nil) + require.NoError(t, qerr) + return resultIDs(res) + } + require.Empty(t, q("apple"), "deleted base doc 0 (tombstone in the last spilled frame) must be gone") + require.Empty(t, q("banana"), "updated doc 1's old base text superseded") + require.Equal(t, []any{int64(1)}, q("blueberry"), "updated doc 1's new text wins across the delete spill") + require.Equal(t, []any{int64(2)}, q("cherry"), "inserted doc 2 present") +} diff --git a/pkg/fulltext2/chinese_test.go b/pkg/fulltext2/chinese_test.go new file mode 100644 index 0000000000000..78a1b167fa6d1 --- /dev/null +++ b/pkg/fulltext2/chinese_test.go @@ -0,0 +1,105 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Chinese (CJK) search coverage over a jieba-friendly corpus (ported from the +// classic fulltext jieba tests' vocabulary: 北京/清华大学/苹果/香蕉/天安门). gojieba +// segments into dictionary words so NL is exact-phrase and boolean operands are +// whole words; ngram tiles CJK into 3-grams so NL is bag-of-words. +package fulltext2 + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// zhSimplified indexes the corpus under parser and returns a queryable Index. +// +// 0: 我来到北京清华大学 (我 来到 北京 清华大学) +// 1: 苹果香蕉都好吃 (苹果 香蕉 都 好吃) +// 2: 我爱北京天安门 (我 爱 北京 天安门) +// 3: 清华大学在北京 (清华大学 在 北京) +// 4: 香蕉和苹果 (香蕉 和 苹果) +func zhSimplified(t *testing.T, parser string) *Index { + docs := []Doc{ + {int64(0), []byte("我来到北京清华大学")}, + {int64(1), []byte("苹果香蕉都好吃")}, + {int64(2), []byte("我爱北京天安门")}, + {int64(3), []byte("清华大学在北京")}, + {int64(4), []byte("香蕉和苹果")}, + } + seg, err := BuildSegmentFromDocsParser("zh", int32(types.T_int64), docs, parser) + require.NoError(t, err) + return NewIndex([]*Segment{seg}, nil) +} + +// --- gojieba: dictionary word segmentation --- + +func TestGojiebaWordSearch(t *testing.T) { + requireJieba(t) + idx := zhSimplified(t, ParserGojieba) + // single jieba words. + require.ElementsMatch(t, []any{int64(0), int64(2), int64(3)}, nlIDs(t, idx, ParserGojieba, "北京")) + require.ElementsMatch(t, []any{int64(0), int64(3)}, nlIDs(t, idx, ParserGojieba, "清华大学")) + require.ElementsMatch(t, []any{int64(1), int64(4)}, nlIDs(t, idx, ParserGojieba, "苹果")) + require.ElementsMatch(t, []any{int64(2)}, nlIDs(t, idx, ParserGojieba, "天安门")) +} + +func TestGojiebaPhrase(t *testing.T) { + requireJieba(t) + idx := zhSimplified(t, ParserGojieba) + // NL is exact phrase of jieba words: 北京·清华大学 is contiguous only in doc 0 + // (doc 3 is 清华大学·在·北京 — the words appear in the other order). + require.ElementsMatch(t, []any{int64(0)}, nlIDs(t, idx, ParserGojieba, "北京清华大学")) +} + +func TestGojiebaBoolean(t *testing.T) { + requireJieba(t) + idx := zhSimplified(t, ParserGojieba) + // +北京 -苹果: 北京 present, 苹果 absent. + require.ElementsMatch(t, []any{int64(0), int64(2), int64(3)}, boolIDs(t, idx, ParserGojieba, "+北京 -苹果")) + // +苹果 +香蕉: both present. + require.ElementsMatch(t, []any{int64(1), int64(4)}, boolIDs(t, idx, ParserGojieba, "+苹果 +香蕉")) + // bag-of-words OR. + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4)}, boolIDs(t, idx, ParserGojieba, "苹果 天安门")) + // group: +北京 +(清华大学 天安门). + require.ElementsMatch(t, []any{int64(0), int64(2), int64(3)}, boolIDs(t, idx, ParserGojieba, "+北京 +(清华大学 天安门)")) +} + +// --- ngram: 3-gram tiling, CJK NL = bag-of-words --- + +func TestNgramChineseSearch(t *testing.T) { + idx := zhSimplified(t, ParserNgram) + // 清华大学 -> 3-grams 清华大/华大学 (bag); docs 0 and 3 contain them. + require.ElementsMatch(t, []any{int64(0), int64(3)}, nlIDs(t, idx, ParserNgram, "清华大学")) + // +苹果 +香蕉 boolean over ngrams: docs 1 and 4. + require.ElementsMatch(t, []any{int64(1), int64(4)}, boolIDs(t, idx, ParserNgram, "+苹果 +香蕉")) +} + +// --- non-int pk carrying Chinese (covers builderCopyPk/[]byte pk path) --- + +func TestGojiebaBytesPk(t *testing.T) { + requireJieba(t) + docs := []Doc{ + {[]byte("doc-甲"), []byte("北京清华大学")}, + {[]byte("doc-乙"), []byte("上海交通大学")}, + } + seg, err := BuildSegmentFromDocsParser("zhpk", int32(types.T_varchar), docs, ParserGojieba) + require.NoError(t, err) + idx := NewIndex([]*Segment{seg}, nil) + got := nlIDs(t, idx, ParserGojieba, "清华大学") + require.Len(t, got, 1) + require.Equal(t, "doc-甲", string(got[0].([]byte))) +} diff --git a/pkg/fulltext2/compact_test.go b/pkg/fulltext2/compact_test.go new file mode 100644 index 0000000000000..7fc4ad5866a0b --- /dev/null +++ b/pkg/fulltext2/compact_test.go @@ -0,0 +1,103 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// MERGE-compaction engine: ReconstructLiveDocs recovers each live doc's ordered +// terms from the positional postings (no re-tokenize), dropping dead/superseded +// copies — the input the fulltext2_compact TVF rebuilds a fresh base from. +package fulltext2 + +import ( + "bytes" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// loadedSeg round-trips a built segment through serialize so reconstruction runs +// over the FST/loaded path the storage loader produces (not the build-side map). +func loadedSeg(t *testing.T, b *Builder) *Segment { + seg, err := b.Finish() + require.NoError(t, err) + blob, err := seg.Serialize() + require.NoError(t, err) + loaded, err := Deserialize(seg.Id, bytes.NewReader(blob)) + require.NoError(t, err) + return loaded +} + +// collectLiveDocs drains the ReconstructLiveDocs iterator into a slice for +// assertions (the production consumer streams it one doc at a time). +func collectLiveDocs(idx *Index, positionFree bool) ([]TokenizedDoc, error) { + out := make([]TokenizedDoc, 0, idx.globalN) + for d, err := range idx.ReconstructLiveDocs(positionFree) { + if err != nil { + return nil, err + } + out = append(out, d) + } + return out, nil +} + +func TestReconstructLiveDocs(t *testing.T) { + // base (recency 0): doc 0 "apple pie", doc 1 "banana bread". + bb := NewBuilder("base", int32(types.T_int64)) + feed(t, bb, int64(0), "apple", "pie") + feed(t, bb, int64(1), "banana", "bread") + base := loadedSeg(t, bb) + base.Recency = 0 + + // tail (recency 100): insert doc 2, update doc 1's text (supersedes base copy). + tb := NewBuilder("tail", int32(types.T_int64)) + feed(t, tb, int64(2), "cherry", "cake") + feed(t, tb, int64(1), "blueberry", "muffin") + tail := loadedSeg(t, tb) + tail.Recency = 100 + + // delete doc 0 at recency 100. + idx := NewIndex([]*Segment{base, tail}, map[any]int64{normalizeKey(int64(0)): 100}) + + docs, err := collectLiveDocs(idx, false) + require.NoError(t, err) + + got := map[any][]string{} + for _, d := range docs { + got[normalizeKey(d.Pk)] = d.Terms + } + // doc 0 deleted, doc 1 = updated tail copy, doc 2 inserted. Term ORDER preserved. + require.NotContains(t, got, normalizeKey(int64(0))) + require.Equal(t, []string{"blueberry", "muffin"}, got[normalizeKey(int64(1))]) + require.Equal(t, []string{"cherry", "cake"}, got[normalizeKey(int64(2))]) + require.Len(t, docs, 2) + + // Rebuilding from the reconstruction gives an index with identical results. + nb := NewBuilder("merged", int32(types.T_int64)) + for _, d := range docs { + for i, w := range d.Terms { + require.NoError(t, nb.Add(w, d.Positions[i], d.Pk)) + } + } + merged, err := nb.Finish() + require.NoError(t, err) + midx := NewIndex([]*Segment{merged}, nil) + q := func(w string) []any { + res, qerr := midx.SearchQuery([]byte(w), false, ParserDefault, BM25, 100, nil) + require.NoError(t, qerr) + return resultIDs(res) + } + require.Empty(t, q("apple")) + require.Empty(t, q("banana")) + require.Equal(t, []any{int64(1)}, q("blueberry")) + require.Equal(t, []any{int64(2)}, q("cherry")) +} diff --git a/pkg/fulltext2/coverage_extra_test.go b/pkg/fulltext2/coverage_extra_test.go new file mode 100644 index 0000000000000..f07db3efb2ab9 --- /dev/null +++ b/pkg/fulltext2/coverage_extra_test.go @@ -0,0 +1,129 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/util/executor" + "github.com/stretchr/testify/require" +) + +// TestResultScalarInt64EmptyAndNil covers the two fall-through branches of the generation +// scalar reader: an empty result and a nil batch both yield 0 (never panic on Vecs[0]). +func TestResultScalarInt64EmptyAndNil(t *testing.T) { + require.Equal(t, int64(0), resultScalarInt64(executor.Result{})) // no batches + require.Equal(t, int64(0), resultScalarInt64(executor.Result{Batches: []*batch.Batch{nil}})) // nil batch skipped +} + +// TestCdcReadLenGuards covers the corrupt-input guards of the CDC length-prefixed readers: a +// header too short for the uint32 length, and a length that overruns the remaining bytes. Both +// are defense-in-depth on already-CRC'd data that BVT never exercises. +func TestCdcReadLenGuards(t *testing.T) { + // bytes: only 2 bytes present, uint32 length read fails. + _, err := cdcReadLenBytes(bytes.NewReader([]byte{0x01, 0x02}), int32(types.T_int64)) + require.Error(t, err) + _, err = cdcReadLenString(bytes.NewReader([]byte{0x01, 0x02})) + require.Error(t, err) + + // length=0xFFFF but no payload bytes remain → "truncated". + truncated := []byte{0xFF, 0xFF, 0x00, 0x00} + _, err = cdcReadLenBytes(bytes.NewReader(truncated), int32(types.T_int64)) + require.ErrorContains(t, err, "truncated") + _, err = cdcReadLenString(bytes.NewReader(truncated)) + require.ErrorContains(t, err, "truncated") +} + +// TestDecodePostingsGuards covers the ranking-blob format guards: empty (clears the segment), +// too short for the 1-byte format tag + 8-byte header, and an unsupported format byte. The +// happy V6 path is exercised too so the assignment line is covered. +func TestDecodePostingsGuards(t *testing.T) { + s := &Segment{} + require.NoError(t, s.decodePostings(nil, nil, nil)) // empty → nil postings + require.Nil(t, s.ranking) + + require.ErrorContains(t, s.decodePostings([]byte{1, 2, 3}, nil, nil), "too short") + + bad := append([]byte{0xFF}, make([]byte, 8)...) // 9 bytes, wrong format tag + require.ErrorContains(t, s.decodePostings(bad, nil, nil), "unsupported postings format") + + ok := append([]byte{postingsFormatV1}, make([]byte, 8)...) + require.NoError(t, s.decodePostings(ok, []byte{1}, []byte{2})) + require.Equal(t, ok, s.ranking) +} + +// TestDecodeTermEntryCorrupt covers the out-of-bounds / bad-uvarint guards in the lazy term +// directory decoder — a corrupt FST value must return (nil,false), never panic or index past +// the ranking slice. +func TestDecodeTermEntryCorrupt(t *testing.T) { + // off past the end of the (empty) ranking. + tp, ok := (&Segment{}).decodeTermEntry(0) + require.False(t, ok) + require.Nil(t, tp) + + // leading uvarint overflows (10 continuation bytes) → first read fails. + overflow := bytes.Repeat([]byte{0xFF}, 10) + _, ok = (&Segment{ranking: overflow}).decodeTermEntry(0) + require.False(t, ok) + + // one valid uvarint then EOF → the second read fails. + _, ok = (&Segment{ranking: []byte{0x01}}).decodeTermEntry(0) + require.False(t, ok) + + // four valid uvarints then nothing → the termMaxTf byte read is out of bounds. + _, ok = (&Segment{ranking: []byte{1, 1, 1, 1}}).decodeTermEntry(0) + require.False(t, ok) +} + +// TestFrameSegmentRoundTrip covers the insert-frame codec: FrameSegment → UnframeSegment recovers +// the same doc count, UnframeTail dispatches the insert frame to the segment (not deletes) arm, and +// a corrupt frame is rejected. +func TestFrameSegmentRoundTrip(t *testing.T) { + b := NewBuilder("f", int32(types.T_int64)) + feed(t, b, int64(0), "quick", "brown") + feed(t, b, int64(1), "quick", "fox") + seg, err := b.Finish() + require.NoError(t, err) + + framed, err := FrameSegment(seg) + require.NoError(t, err) + require.NotEmpty(t, framed) + + seg2, err := UnframeSegment("f", framed) + require.NoError(t, err) + require.Equal(t, seg.numDocs(), seg2.numDocs()) + + // UnframeTail on the same insert frame → segment set, no deletes. + s3, dels, err := UnframeTail("f", framed) + require.NoError(t, err) + require.NotNil(t, s3) + require.Nil(t, dels) + + // corrupt frame → error (UnframeCdcChunk rejects it). + _, err = UnframeSegment("f", []byte{0x00, 0x01, 0x02}) + require.Error(t, err) +} + +// TestAsBytes covers the pk raw-bytes coercion: []byte and string pass through, everything else +// (a boxed fixed-width pk) returns nil so callers fall back to typed encoding. +func TestAsBytes(t *testing.T) { + require.Equal(t, []byte("raw"), asBytes([]byte("raw"))) + require.Equal(t, []byte("str"), asBytes("str")) + require.Nil(t, asBytes(int64(5))) + require.Nil(t, asBytes(nil)) +} diff --git a/pkg/fulltext2/coverage_index_test.go b/pkg/fulltext2/coverage_index_test.go new file mode 100644 index 0000000000000..6c098fb42effe --- /dev/null +++ b/pkg/fulltext2/coverage_index_test.go @@ -0,0 +1,80 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "strconv" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// TestSortKey pins the injective tie-break key for every pk shape: []byte/string pass through, +// the temporal types key by their underlying int64 (so DATETIME(6) sub-second precision is not +// truncated), and everything else falls to %v. +func TestSortKey(t *testing.T) { + require.Equal(t, "raw", sortKey([]byte("raw"))) + require.Equal(t, "str", sortKey("str")) + require.Equal(t, strconv.FormatInt(9_000_001, 10), sortKey(types.Datetime(9_000_001))) + require.Equal(t, strconv.FormatInt(42, 10), sortKey(types.Time(42))) + require.Equal(t, strconv.FormatInt(7, 10), sortKey(types.Timestamp(7))) + require.Equal(t, "123", sortKey(int64(123))) // default %v +} + +// TestBoundedTopK covers the min-heap bounded top-k: it retains only the k highest scores, a +// push below the current k-th best is dropped, and k<=0 is a no-op. +func TestBoundedTopK(t *testing.T) { + b := newBoundedTopK(2) + b.push("a", 1.0, nil) + b.push("b", 3.0, nil) + require.Equal(t, 2, b.len()) + b.push("c", 2.0, nil) // evicts "a" (1.0), keeps {b:3, c:2} + b.push("d", 0.5, nil) // below the k-th best → dropped + require.Equal(t, 2, b.len()) + + res := b.resultsDescScaled(1.0) + require.Len(t, res, 2) + require.Equal(t, "b", res[0].Pk) // score-descending + require.Equal(t, "c", res[1].Pk) + require.Greater(t, res[0].Score, res[1].Score) + + // scale multiplies the retained partial scores. + scaled := b.resultsDescScaled(2.0) + require.Equal(t, float32(6.0), scaled[0].Score) + + // k<=0 → push is a no-op. + z := newBoundedTopK(0) + z.push("x", 5.0, nil) + require.Equal(t, 0, z.len()) +} + +// TestTopKResults covers the FastMaxHeap selection: empty/k<=0 → nil, k>len is clamped, and the +// output is score-descending. +func TestTopKResults(t *testing.T) { + require.Nil(t, topKResults(nil, 3)) + require.Nil(t, topKResults([]Result{{Pk: "a", Score: 1}}, 0)) + + in := []Result{{Pk: "a", Score: 1}, {Pk: "b", Score: 3}, {Pk: "c", Score: 2}} + out := topKResults(in, 2) + require.Len(t, out, 2) + require.Equal(t, "b", out[0].Pk) // highest first + require.Equal(t, "c", out[1].Pk) + + // k > len is clamped to len. + all := topKResults(in, 10) + require.Len(t, all, 3) + require.Equal(t, "b", all[0].Pk) +} diff --git a/pkg/fulltext2/coverage_query_membership_test.go b/pkg/fulltext2/coverage_query_membership_test.go new file mode 100644 index 0000000000000..451bc63e6efaf --- /dev/null +++ b/pkg/fulltext2/coverage_query_membership_test.go @@ -0,0 +1,205 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/docfilter" + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/bytejson" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/stretchr/testify/require" +) + +// TestContainsPkTypes exercises the fast per-PK-type encode branches of docFilterMembership.Contains +// (int64 is covered elsewhere): the uint64 / int32 / uint32 arms must encode byte-identically to the +// docfilter build so even pks pass and odd pks reject, and an out-of-range ord returns false. +func TestContainsPkTypes(t *testing.T) { + mp := mpool.MustNewZero() + + // uint64 + t.Run("uint64", func(t *testing.T) { + b := NewBuilder("u64", int32(types.T_uint64)) + for i := 0; i < 6; i++ { + feed(t, b, uint64(i), "x") + } + seg, err := b.Finish() + require.NoError(t, err) + vec := vector.NewVec(types.New(types.T_uint64, 8, 0)) + for i := 0; i < 6; i += 2 { + require.NoError(t, vector.AppendFixed(vec, uint64(i), false, mp)) + } + fb, err := docfilter.Build(vec) + require.NoError(t, err) + f, err := docfilter.New(fb) + require.NoError(t, err) + defer f.Free() + dfm := &docFilterMembership{seg: seg, f: f} + for i := int64(0); i < 6; i++ { + require.Equalf(t, i%2 == 0, dfm.Contains(i), "ord %d", i) + } + require.False(t, dfm.Contains(-1)) // out of range low + require.False(t, dfm.Contains(int64(1<<40))) // out of range high + }) + + // int32 + t.Run("int32", func(t *testing.T) { + b := NewBuilder("i32", int32(types.T_int32)) + for i := 0; i < 6; i++ { + feed(t, b, int32(i), "x") + } + seg, err := b.Finish() + require.NoError(t, err) + vec := vector.NewVec(types.New(types.T_int32, 4, 0)) + for i := 0; i < 6; i += 2 { + require.NoError(t, vector.AppendFixed(vec, int32(i), false, mp)) + } + fb, err := docfilter.Build(vec) + require.NoError(t, err) + f, err := docfilter.New(fb) + require.NoError(t, err) + defer f.Free() + dfm := &docFilterMembership{seg: seg, f: f} + for i := int64(0); i < 6; i++ { + require.Equalf(t, i%2 == 0, dfm.Contains(i), "ord %d", i) + } + }) + + // uint32 + t.Run("uint32", func(t *testing.T) { + b := NewBuilder("u32", int32(types.T_uint32)) + for i := 0; i < 6; i++ { + feed(t, b, uint32(i), "x") + } + seg, err := b.Finish() + require.NoError(t, err) + vec := vector.NewVec(types.New(types.T_uint32, 4, 0)) + for i := 0; i < 6; i += 2 { + require.NoError(t, vector.AppendFixed(vec, uint32(i), false, mp)) + } + fb, err := docfilter.Build(vec) + require.NoError(t, err) + f, err := docfilter.New(fb) + require.NoError(t, err) + defer f.Free() + dfm := &docFilterMembership{seg: seg, f: f} + for i := int64(0); i < 6; i++ { + require.Equalf(t, i%2 == 0, dfm.Contains(i), "ord %d", i) + } + }) + + // uuid: probed as the RAW 16 bytes (not the canonical string) — the dedicated arm. + t.Run("uuid", func(t *testing.T) { + b := NewBuilder("uuid", int32(types.T_uuid)) + mkUuid := func(i int) types.Uuid { var u types.Uuid; u[0] = byte(i); return u } + for i := 0; i < 6; i++ { + feed(t, b, mkUuid(i), "x") + } + seg, err := b.Finish() + require.NoError(t, err) + vec := vector.NewVec(types.New(types.T_uuid, 16, 0)) + for i := 0; i < 6; i += 2 { + require.NoError(t, vector.AppendFixed(vec, mkUuid(i), false, mp)) + } + fb, err := docfilter.Build(vec) + require.NoError(t, err) + f, err := docfilter.New(fb) + require.NoError(t, err) + defer f.Free() + dfm := &docFilterMembership{seg: seg, f: f} + for i := int64(0); i < 6; i++ { + require.Equalf(t, i%2 == 0, dfm.Contains(i), "ord %d", i) + } + }) + + // varchar: the default arm (encodePk), not one of the fast integer/uuid cases. + t.Run("varchar", func(t *testing.T) { + b := NewBuilder("vc", int32(types.T_varchar)) + keys := []string{"k0", "k1", "k2", "k3", "k4", "k5"} + for _, k := range keys { + feed(t, b, k, "x") + } + seg, err := b.Finish() + require.NoError(t, err) + vec := vector.NewVec(types.New(types.T_varchar, 64, 0)) + for i := 0; i < 6; i += 2 { + require.NoError(t, vector.AppendBytes(vec, []byte(keys[i]), false, mp)) + } + fb, err := docfilter.Build(vec) + require.NoError(t, err) + f, err := docfilter.New(fb) + require.NoError(t, err) + defer f.Free() + dfm := &docFilterMembership{seg: seg, f: f} + for i := int64(0); i < 6; i++ { + require.Equalf(t, i%2 == 0, dfm.Contains(i), "ord %d", i) + } + }) +} + +// TestIsJSONParser pins the json-family predicate. +func TestIsJSONParser(t *testing.T) { + require.True(t, IsJSONParser(ParserJSON)) + require.True(t, IsJSONParser(ParserJSONValue)) + require.False(t, IsJSONParser(ParserDefault)) + require.False(t, IsJSONParser("ngram")) + require.True(t, IsJSONValueParser(ParserJSONValue)) + require.False(t, IsJSONValueParser(ParserJSON)) +} + +// TestFlattenJSONColumns covers the input-type dispatch of the two CDC-side json flatteners for +// each accepted form (ByteJson, []byte, string) plus the default (non-json input → nil, nil). +func TestFlattenJSONColumns(t *testing.T) { + raw := `{"a":"hello world","b":42}` + bj, err := bytejson.ParseFromString(raw) + require.NoError(t, err) + + for _, fn := range []func(any) ([]byte, error){FlattenJSONColumn, FlattenJSONValueColumn} { + // ByteJson + out, err := fn(bj) + require.NoError(t, err) + require.NotEmpty(t, out) + // []byte + out, err = fn([]byte(raw)) + require.NoError(t, err) + require.NotEmpty(t, out) + // string + out, err = fn(raw) + require.NoError(t, err) + require.NotEmpty(t, out) + // default (unsupported type) → nil, nil + out, err = fn(12345) + require.NoError(t, err) + require.Nil(t, out) + } +} + +// TestCdcTokenizer covers the three arms: json_value (whole-value tokens), a normal parser (ngram +// words), and an unknown parser (DocTokenizer error propagated). +func TestCdcTokenizer(t *testing.T) { + jv, err := CdcTokenizer(ParserJSONValue) + require.NoError(t, err) + require.NotNil(t, jv) + + def, err := CdcTokenizer(ParserDefault) + require.NoError(t, err) + require.NotNil(t, def) + require.NotEmpty(t, def("hello world")) // tokenizes into words + + _, err = CdcTokenizer("no-such-parser") + require.Error(t, err) +} diff --git a/pkg/fulltext2/docmap_include_test.go b/pkg/fulltext2/docmap_include_test.go new file mode 100644 index 0000000000000..26c7e68b27a6b --- /dev/null +++ b/pkg/fulltext2/docmap_include_test.go @@ -0,0 +1,155 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// TestDocmapIncludeRoundTrip pins the INCLUDE-values docmap section: a build-side segment +// with per-doc include values (mixed int64 + varchar, including NULLs) encodes and decodes +// back to the same typed values via the loaded-side offset-table accessor, without +// disturbing the pk / docLen sections. +func TestDocmapIncludeRoundTrip(t *testing.T) { + build := &Segment{ + PkType: int32(types.T_int64), + pks: []any{int64(10), int64(20), int64(30)}, + docLen: []int32{3, 5, 7}, + includeTypes: []int32{int32(types.T_int64), int32(types.T_varchar)}, + includeVals: [][]any{ + {int64(100), []byte("active")}, + {int64(200), nil}, // NULL varchar + {nil, []byte("archived")}, // NULL int + }, + } + data, err := build.encodeDocmap() + require.NoError(t, err) + + loaded := &Segment{} + require.NoError(t, loaded.decodeDocmap(data)) + require.EqualValues(t, 3, loaded.N) + require.Equal(t, 2, loaded.nIncludeCols()) + + check := func(ord int64, col int, wantVal any, wantNull bool) { + v, isNull, verr := loaded.includeVal(ord, col) + require.NoError(t, verr) + require.Equal(t, wantNull, isNull, "ord=%d col=%d null", ord, col) + if !wantNull { + require.Equal(t, wantVal, v, "ord=%d col=%d val", ord, col) + } + } + check(0, 0, int64(100), false) + check(0, 1, []byte("active"), false) + check(1, 0, int64(200), false) + check(1, 1, nil, true) // NULL varchar + check(2, 0, nil, true) // NULL int + check(2, 1, []byte("archived"), false) + + // pk + docLen sections are unaffected by the trailing INCLUDE section. + require.Equal(t, int64(20), loaded.pk(1)) + require.Equal(t, int32(7), loaded.docLen[2]) + + // out-of-range column returns null, not a panic. + _, isNull, verr := loaded.includeVal(0, 5) + require.NoError(t, verr) + require.True(t, isNull) +} + +// TestDocmapNoIncludeRoundTrip: a segment with no INCLUDE columns produces a docmap with no +// trailing section (byte-identical to before), and decode leaves the include fields nil. +func TestDocmapNoIncludeRoundTrip(t *testing.T) { + build := &Segment{ + PkType: int32(types.T_int64), + pks: []any{int64(1), int64(2)}, + docLen: []int32{4, 4}, + } + data, err := build.encodeDocmap() + require.NoError(t, err) + + loaded := &Segment{} + require.NoError(t, loaded.decodeDocmap(data)) + require.Equal(t, 0, loaded.nIncludeCols()) + require.Nil(t, loaded.includeTypes) + + v, isNull, err := loaded.includeVal(0, 0) + require.NoError(t, err) + require.Nil(t, v) + require.True(t, isNull) +} + +// TestDocmapIncludeMismatchLen: encode rejects a build-side segment whose includeVals count +// does not match the doc count (a builder bug), rather than writing a corrupt section. +func TestDocmapIncludeMismatchLen(t *testing.T) { + build := &Segment{ + PkType: int32(types.T_int64), + pks: []any{int64(1), int64(2)}, + docLen: []int32{1, 1}, + includeTypes: []int32{int32(types.T_int64)}, + includeVals: [][]any{{int64(9)}}, // only 1 row for 2 docs + } + _, err := build.encodeDocmap() + require.Error(t, err) +} + +// TestDocmapIncludeFixedNoOffsetTable pins the fixed-width optimization: an all-integer +// INCLUDE index builds NO resident varlena offset table (fixed cols are stride-addressed), +// while an index with a varchar col builds an offset table only for the varlena column(s). +func TestDocmapIncludeFixedNoOffsetTable(t *testing.T) { + // All fixed (int64 + int32): zero varlena offsets. + allFixed := &Segment{ + PkType: int32(types.T_int64), + pks: []any{int64(1), int64(2)}, + docLen: []int32{1, 1}, + includeTypes: []int32{int32(types.T_int64), int32(types.T_int32)}, + includeVals: [][]any{{int64(10), int32(100)}, {nil, int32(200)}}, + } + data, err := allFixed.encodeDocmap() + require.NoError(t, err) + loaded := &Segment{} + require.NoError(t, loaded.decodeDocmap(data)) + require.Nil(t, loaded.includeVarOffsets, "numeric-only INCLUDE index must build no offset table") + require.Equal(t, 2, loaded.includeLay.nFixed) + require.Equal(t, 0, loaded.includeLay.nVarlena) + // values round-trip (incl. NULL int64 at ord1 col0). + _, isNull, _ := loaded.includeVal(1, 0) + require.True(t, isNull) + v, _, _ := loaded.includeVal(1, 1) + require.Equal(t, int32(200), v) + v, _, _ = loaded.includeVal(0, 0) + require.Equal(t, int64(10), v) + + // Mixed (int64 + varchar): offset table sized only for the 1 varlena column. + mixed := &Segment{ + PkType: int32(types.T_int64), + pks: []any{int64(1), int64(2)}, + docLen: []int32{1, 1}, + includeTypes: []int32{int32(types.T_int64), int32(types.T_varchar)}, + includeVals: [][]any{{int64(10), []byte("a")}, {int64(20), nil}}, + } + data, err = mixed.encodeDocmap() + require.NoError(t, err) + lm := &Segment{} + require.NoError(t, lm.decodeDocmap(data)) + require.Len(t, lm.includeVarOffsets, 2, "offset table sized N*nVarlena = 2*1") // only the varchar col + require.Equal(t, 1, lm.includeLay.nFixed) + require.Equal(t, 1, lm.includeLay.nVarlena) + v, _, _ = lm.includeVal(0, 1) + require.Equal(t, []byte("a"), v) + _, isNull, _ = lm.includeVal(1, 1) + require.True(t, isNull) +} diff --git a/pkg/fulltext2/frames_test.go b/pkg/fulltext2/frames_test.go new file mode 100644 index 0000000000000..462e1670c36c5 --- /dev/null +++ b/pkg/fulltext2/frames_test.go @@ -0,0 +1,123 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "encoding/binary" + "fmt" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/stretchr/testify/require" +) + +// TestReassembleFramesRejectsCorruptLength pins the OOM guard: a chunk whose START MAGIC is +// intact but whose length fields are garbage (declaring a ~8GiB frame) must be rejected up +// front by the remaining-bytes check, NOT passed to make([]byte, 0, total) which would +// OOM-kill the CN. checkTailLoadBudget cannot catch this — it measures the real stored bytes, +// not the length a corrupt header claims. +func TestReassembleFramesRejectsCorruptLength(t *testing.T) { + const cdcChunkMagic uint32 = 0xCDC51A11 // must match pkg/vectorindex/cuvs cdcChunkMagic + data := make([]byte, 28) // cdcHeaderSize + binary.LittleEndian.PutUint32(data[0:4], cdcChunkMagic) + binary.LittleEndian.PutUint32(data[20:24], 0xFFFFFFF0) // plen: absurd + binary.LittleEndian.PutUint32(data[24:28], 0xFFFFFFF0) // hlen: absurd + _, err := reassembleFrames([]TailChunk{{ChunkId: 7, Data: data}}) + require.Error(t, err) + require.Contains(t, err.Error(), "corrupt frame header") +} + +// bigSegment builds an n-doc segment plus one doc with a unique needle token, so +// its serialization is large enough to span multiple storage chunks. +func bigSegment(t *testing.T, n int) (*Segment, int64) { + t.Helper() + docs := make([]Doc, 0, n+1) + for i := 1; i <= n; i++ { + docs = append(docs, Doc{int64(i), []byte(fmt.Sprintf("word%d alpha beta", i))}) + } + needle := int64(n + 1) + docs = append(docs, Doc{needle, []byte("uniqueneedletoken")}) + s, err := BuildSegmentFromDocs("big", int32(types.T_int64), docs, tokenizer.NewSimpleTokenizer()) + require.NoError(t, err) + return s, needle +} + +// TestFramesMultiChunkRoundtrip: a large segment framed, split into chunks, +// shuffled, re-ordered O(n), reassembled, and unframed back to a queryable +// segment. Exercises the multi-chunk path. +func TestFramesMultiChunkRoundtrip(t *testing.T) { + seg, needle := bigSegment(t, 4000) + framed, err := FrameSegment(seg) + require.NoError(t, err) + + chunks := splitFrameChunks(0, framed) + if len(framed) > vectorindex.MaxChunkSize { + require.Greater(t, len(chunks), 1, "a segment larger than MaxChunkSize must span multiple chunks") + } + + // storage returns rows in arbitrary order → reverse, then O(n) place. + shuffled := make([]TailChunk, len(chunks)) + for i := range chunks { + shuffled[i] = chunks[len(chunks)-1-i] + } + ordered, err := orderTailChunks(shuffled) + require.NoError(t, err) + frames, err := reassembleFrames(ordered) + require.NoError(t, err) + require.Len(t, frames, 1) + require.EqualValues(t, 0, frames[0].ChunkId) + + loaded, err := UnframeSegment("big", frames[0].Data) + require.NoError(t, err) + t.Cleanup(func() { _ = loaded.dict.Close() }) + + idx := NewIndex([]*Segment{loaded}, nil) + require.Equal(t, int64(4001), idx.NumDocs()) + require.Equal(t, []any{needle}, iquery(t, idx, "uniqueneedletoken")) +} + +// TestFramesSingleChunk: a small segment yields one chunk at its start chunk_id. +func TestFramesSingleChunk(t *testing.T) { + s, err := BuildSegmentFromDocs("s", int32(types.T_int64), + []Doc{{int64(1), []byte("quick brown fox")}}, tokenizer.NewSimpleTokenizer()) + require.NoError(t, err) + + framed, err := FrameSegment(s) + require.NoError(t, err) + chunks := splitFrameChunks(5, framed) + require.Len(t, chunks, 1) + require.EqualValues(t, 5, chunks[0].ChunkId) + + frames, err := reassembleFrames(chunks) + require.NoError(t, err) + loaded, err := UnframeSegment("s", frames[0].Data) + require.NoError(t, err) + t.Cleanup(func() { _ = loaded.dict.Close() }) + require.Equal(t, int64(1), loaded.N) +} + +// TestOrderTailChunksGap: a missing/duplicate chunk_id is corruption, reported. +func TestOrderTailChunksGap(t *testing.T) { + _, err := orderTailChunks([]TailChunk{{ChunkId: 0}, {ChunkId: 2}}) // gap at 1 + require.Error(t, err) + + ordered, err := orderTailChunks([]TailChunk{{ChunkId: 7}, {ChunkId: 5}, {ChunkId: 6}}) + require.NoError(t, err) + require.EqualValues(t, 5, ordered[0].ChunkId) + require.EqualValues(t, 6, ordered[1].ChunkId) + require.EqualValues(t, 7, ordered[2].ChunkId) +} diff --git a/pkg/fulltext2/fulltext_boolean_port_test.go b/pkg/fulltext2/fulltext_boolean_port_test.go new file mode 100644 index 0000000000000..185249df902ee --- /dev/null +++ b/pkg/fulltext2/fulltext_boolean_port_test.go @@ -0,0 +1,215 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// COMPLETE port of classic fulltext's boolean-mode tests +// (pkg/fulltext/fulltext_test.go: TestFullText{Or,PlusPlus,PlusOr,Minus,Tilda, +// 1,2,3,5,Group,JoinGroupTilda,GroupTilda,Star,Phrase}). The classic tests unit- +// test Pattern.Eval with synthetic per-term-frequency docvecs; here each docvec is +// reconstructed into a real corpus (word repeated tf times) and fulltext2's +// boolean membership is asserted against the SAME expected doc set — proving +// MySQL boolean-mode parity through fulltext2's own parser + WAND evaluator. +package fulltext2 + +import ( + "sort" + "strings" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" + "github.com/stretchr/testify/require" +) + +// tfSegment reconstructs a classic docvec corpus: doc id contains words[j] +// repeated tf[j] times (space-joined). Docs are added in ascending-id order. +func tfSegment(t *testing.T, words []string, docs map[int64][]int) *Segment { + ids := make([]int64, 0, len(docs)) + for id := range docs { + ids = append(ids, id) + } + sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] }) + ds := make([]Doc, 0, len(ids)) + for _, id := range ids { + var b strings.Builder + for j, w := range words { + for k := 0; k < docs[id][j]; k++ { + if b.Len() > 0 { + b.WriteByte(' ') + } + b.WriteString(w) + } + } + ds = append(ds, Doc{id, []byte(b.String())}) + } + seg, err := BuildSegmentFromDocs("port", int32(types.T_int64), ds, tokenizer.NewSimpleTokenizer()) + require.NoError(t, err) + return seg +} + +// apple/banana corpus: doc 0 has both, 1 apple-only, 11/12 banana-only. +func abSeg(t *testing.T) *Segment { + return tfSegment(t, []string{"apple", "banana"}, map[int64][]int{ + 0: {2, 2}, 1: {3, 0}, 11: {0, 3}, 12: {0, 4}, + }) +} + +// we/are/so/happy corpus (TestFullText1 shape): one term per doc-group. +func washSeg(t *testing.T) *Segment { + return tfSegment(t, []string{"we", "are", "so", "happy"}, map[int64][]int{ + 0: {2, 0, 0, 0}, 1: {3, 0, 0, 0}, + 10: {0, 2, 0, 0}, 11: {0, 3, 0, 0}, 12: {0, 4, 0, 0}, + 20: {0, 0, 5, 0}, 21: {0, 0, 6, 0}, 22: {0, 0, 7, 0}, 23: {0, 0, 8, 0}, + 30: {0, 0, 0, 1}, 31: {0, 0, 0, 2}, 32: {0, 0, 0, 3}, 33: {0, 0, 0, 4}, + }) +} + +// ---- OR / +/- / ~ (apple-banana corpus) ---- + +// TestFullTextOr: "apple banana" — pure OR; every doc with apple OR banana. +func TestPortFullTextOr(t *testing.T) { + require.ElementsMatch(t, []any{int64(0), int64(1), int64(11), int64(12)}, + bquery(t, abSeg(t), "apple banana")) +} + +// TestFullTextPlusOr: "+apple banana" — apple MUST, banana SHOULD → {0,1}. +func TestPortFullTextPlusOr(t *testing.T) { + require.ElementsMatch(t, []any{int64(0), int64(1)}, bquery(t, abSeg(t), "+apple banana")) +} + +// TestFullTextMinus: "+apple -banana" — apple MUST, banana MUST-NOT → {1}. +func TestPortFullTextMinus(t *testing.T) { + require.Equal(t, []any{int64(1)}, bquery(t, abSeg(t), "+apple -banana")) +} + +// TestFullTextPlusPlus (classic apple/orange): "+apple -orange" — orange is in +// doc 0 (docvec col 1), so doc 0 is excluded; only doc 1 matches. +func TestPortFullTextPlusMinus(t *testing.T) { + seg := tfSegment(t, []string{"apple", "orange"}, map[int64][]int{ + 0: {2, 2}, 1: {3, 0}, 11: {0, 3}, 12: {0, 4}, + }) + require.Equal(t, []any{int64(1)}, bquery(t, seg, "+apple -orange")) +} + +// "+apple +banana" — both MUST → only doc 0. +func TestPortFullTextPlusPlus(t *testing.T) { + require.Equal(t, []any{int64(0)}, bquery(t, abSeg(t), "+apple +banana")) +} + +// TestFullTextTilda: "+apple ~banana" — apple MUST, banana a score penalty, not an +// exclusion → docs 0 and 1 both match, the banana-free doc 1 ranked first. +func TestPortFullTextTilda(t *testing.T) { + got := bqueryOrdered(t, abSeg(t), "+apple ~banana") + require.ElementsMatch(t, []any{int64(0), int64(1)}, got) + require.Equal(t, int64(1), got[0]) +} + +// ---- multi-term MUST/SHOULD/MUST-NOT (we/are/so/happy corpus) ---- + +// TestFullText1: "we are so happy" — all SHOULD (OR); every doc matches. Uses +// the Index path (k=100) since all 13 docs match — more than bquery's top-10. +func TestPortFullText1AllOr(t *testing.T) { + idx := NewIndex([]*Segment{washSeg(t)}, nil) + require.ElementsMatch(t, + []any{int64(0), int64(1), int64(10), int64(11), int64(12), + int64(20), int64(21), int64(22), int64(23), + int64(30), int64(31), int64(32), int64(33)}, + boolIDs(t, idx, "", "we are so happy")) +} + +// TestFullText2: "+we +are +so +happy" — all MUST; only the doc with all four. +func TestPortFullText2AllMust(t *testing.T) { + seg := tfSegment(t, []string{"we", "are", "so", "happy"}, map[int64][]int{ + 0: {2, 2, 2, 2}, 1: {3, 0, 0, 0}, 11: {0, 3, 0, 0}, 12: {0, 4, 0, 0}, + 21: {0, 0, 6, 0}, 22: {0, 0, 7, 0}, 23: {0, 0, 8, 0}, + 31: {0, 0, 0, 2}, 32: {0, 0, 0, 3}, 33: {0, 0, 0, 4}, + }) + require.Equal(t, []any{int64(0)}, bquery(t, seg, "+we +are +so +happy")) +} + +// TestFullText3: "+we -are -so -happy" — we MUST, the rest MUST-NOT → doc 1 only +// (doc 0 has 'are', so it is excluded). +func TestPortFullText3MustNot(t *testing.T) { + seg := tfSegment(t, []string{"we", "are", "so", "happy"}, map[int64][]int{ + 0: {2, 2, 0, 0}, 1: {3, 0, 0, 0}, 11: {0, 3, 0, 0}, 12: {0, 4, 0, 0}, + 20: {0, 0, 5, 0}, 21: {0, 0, 6, 0}, 22: {0, 0, 7, 0}, 23: {0, 0, 8, 0}, + 30: {0, 0, 0, 1}, 31: {0, 0, 0, 2}, 32: {0, 0, 0, 3}, 33: {0, 0, 0, 4}, + }) + require.Equal(t, []any{int64(1)}, bquery(t, seg, "+we -are -so -happy")) +} + +// TestFullText5: "we are so +happy" — happy MUST but no doc contains it → empty. +func TestPortFullText5NoMatch(t *testing.T) { + seg := tfSegment(t, []string{"we", "are", "so", "happy"}, map[int64][]int{ + 0: {2, 0, 0, 0}, 1: {3, 0, 0, 0}, 11: {0, 3, 0, 0}, 12: {0, 4, 0, 0}, + }) + require.Empty(t, bquery(t, seg, "we are so +happy")) +} + +// ---- groups + weighted / tilda groups (we/are/so corpus) ---- + +// wasSeg: doc 0 has we+are, doc 1 has we+so, 11/12 are-only, 20-23 so-only. +func wasSeg(t *testing.T) *Segment { + return tfSegment(t, []string{"we", "are", "so"}, map[int64][]int{ + 0: {2, 2, 0}, 1: {3, 0, 5}, 11: {0, 3, 0}, 12: {0, 4, 0}, + 20: {0, 0, 5}, 21: {0, 0, 6}, 22: {0, 0, 7}, 23: {0, 0, 8}, + }) +} + +// TestFullTextGroup: "+we +(so)" — we MUST and (are demoted OR so boosted) +// present → {0,1} (the are-only / so-only docs lack 'we'). +func TestPortFullTextGroup(t *testing.T) { + require.ElementsMatch(t, []any{int64(0), int64(1)}, bquery(t, wasSeg(t), "+we +(so)")) +} + +// TestFullTextGroupTilda: "+we ~(so)" — we MUST, the group only adjusts +// score (no inclusion/exclusion) → still {0,1}. +func TestPortFullTextGroupTilda(t *testing.T) { + require.ElementsMatch(t, []any{int64(0), int64(1)}, bquery(t, wasSeg(t), "+we ~(so)")) +} + +// TestFullTextJoinGroupTilda: "+we +also ~(so)" — we MUST and also MUST → +// only the docs carrying both (0,1); the ~group merely adjusts score. +func TestPortFullTextJoinGroupTilda(t *testing.T) { + seg := tfSegment(t, []string{"we", "also", "are", "so"}, map[int64][]int{ + 0: {2, 2, 2, 0}, 1: {2, 2, 0, 5}, 11: {0, 0, 3, 0}, 12: {0, 0, 4, 0}, + 20: {0, 0, 0, 5}, 21: {0, 0, 0, 6}, 22: {0, 0, 0, 7}, 23: {0, 0, 0, 8}, + }) + require.ElementsMatch(t, []any{int64(0), int64(1)}, bquery(t, seg, "+we +also ~(so)")) +} + +// ---- prefix + phrase ---- + +// TestFullTextStar: "apple*" — prefix matches apple and applesauce, not banana. +func TestPortFullTextStar(t *testing.T) { + seg, err := BuildSegmentFromDocs("star", int32(types.T_int64), []Doc{ + {int64(0), []byte("apple")}, + {int64(1), []byte("applesauce pie")}, + {int64(2), []byte("banana bread")}, + }, tokenizer.NewSimpleTokenizer()) + require.NoError(t, err) + require.ElementsMatch(t, []any{int64(0), int64(1)}, bquery(t, seg, "apple*")) +} + +// TestFullTextPhrase: "\"we are so happy\"" — exact contiguous phrase; only doc 0 +// has the four words adjacent in order. +func TestPortFullTextPhrase(t *testing.T) { + seg, err := BuildSegmentFromDocs("phrase", int32(types.T_int64), []Doc{ + {int64(0), []byte("we are so happy")}, + {int64(1), []byte("we are happy so")}, + {int64(2), []byte("we are so")}, + {int64(3), []byte("so happy we are")}, + }, tokenizer.NewSimpleTokenizer()) + require.NoError(t, err) + require.Equal(t, []any{int64(0)}, bquery(t, seg, `"we are so happy"`)) +} diff --git a/pkg/fulltext2/include_boxfree_test.go b/pkg/fulltext2/include_boxfree_test.go new file mode 100644 index 0000000000000..4ab8213cbb140 --- /dev/null +++ b/pkg/fulltext2/include_boxfree_test.go @@ -0,0 +1,153 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/stretchr/testify/require" +) + +// boxfreeSeg builds a 4-doc segment with two INCLUDE columns (int64, varchar) and NULLs +// arranged so both the "first value is NULL after non-nulls" (backfill) and "non-null after +// NULL" (parallel append) branches of cbAppendNull/cbAppendVal are exercised: +// +// col0 int64: [100, NULL, 300, NULL] +// col1 varchar: ["active", "b", NULL, NULL] +func boxfreeSeg() *Segment { + return &Segment{ + PkType: int32(types.T_int64), + pks: []any{int64(1), int64(2), int64(3), int64(4)}, + docLen: []int32{1, 1, 1, 1}, + includeTypes: []int32{int32(types.T_int64), int32(types.T_varchar)}, + includeVals: [][]any{ + {int64(100), []byte("active")}, + {nil, []byte("b")}, + {int64(300), nil}, + {nil, nil}, + }, + } +} + +// TestAppendIncludeToBoxFree pins the box-free covered-output path (F4): appendIncludeTo +// encodes each INCLUDE column into a nullable ColumnBuffer without boxing, and +// AppendColumnBuffer decodes it straight into a typed vector preserving values AND NULLs. +// Runs on BOTH a build-side segment (includeVals resident) and a loaded segment +// (includeRaw / offset tables), which take the two arms of appendIncludeTo. +func TestAppendIncludeToBoxFree(t *testing.T) { + mp := mpool.MustNewZero() + + run := func(t *testing.T, seg *Segment) { + // col0: int64 with NULLs at ord 1 and 3. + k0 := &vectorindex.ColumnBuffer{Type: types.T_int64} + for ord := int64(0); ord < 4; ord++ { + require.NoError(t, seg.appendIncludeTo(k0, 0, ord)) + } + require.Equal(t, 4, k0.N) + require.Equal(t, []bool{false, true, false, true}, k0.Nulls) + vec0 := vector.NewVec(types.T_int64.ToType()) + require.NoError(t, vectorindex.AppendColumnBuffer(k0, vec0, mp)) + require.Equal(t, 4, vec0.Length()) + vals0 := vector.MustFixedColWithTypeCheck[int64](vec0) + require.Equal(t, int64(100), vals0[0]) + require.Equal(t, int64(300), vals0[2]) + require.False(t, vec0.IsNull(0)) + require.True(t, vec0.IsNull(1)) + require.False(t, vec0.IsNull(2)) + require.True(t, vec0.IsNull(3)) + + // col1: varchar with NULLs at ord 2 and 3 (first NULL comes AFTER two non-nulls, + // exercising the cbAppendNull backfill of the prior all-non-null Nulls slice). + k1 := &vectorindex.ColumnBuffer{Type: types.T_varchar} + for ord := int64(0); ord < 4; ord++ { + require.NoError(t, seg.appendIncludeTo(k1, 1, ord)) + } + require.Equal(t, []bool{false, false, true, true}, k1.Nulls) + vec1 := vector.NewVec(types.T_varchar.ToType()) + require.NoError(t, vectorindex.AppendColumnBuffer(k1, vec1, mp)) + require.Equal(t, "active", vec1.GetStringAt(0)) + require.Equal(t, "b", vec1.GetStringAt(1)) + require.False(t, vec1.IsNull(0)) + require.False(t, vec1.IsNull(1)) + require.True(t, vec1.IsNull(2)) + require.True(t, vec1.IsNull(3)) + } + + t.Run("build-side", func(t *testing.T) { run(t, boxfreeSeg()) }) + + t.Run("loaded-side", func(t *testing.T) { + data, err := boxfreeSeg().encodeDocmap() + require.NoError(t, err) + loaded := &Segment{} + require.NoError(t, loaded.decodeDocmap(data)) + run(t, loaded) + }) +} + +// TestColumnBufferAllNonNull: a column with NO NULLs never materializes k.Nulls (stays nil, +// so AppendColumnBuffer's nul(i) is always false — the pk path is byte-for-byte unchanged). +func TestColumnBufferAllNonNull(t *testing.T) { + mp := mpool.MustNewZero() + seg := boxfreeSeg() + k := &vectorindex.ColumnBuffer{Type: types.T_varchar} + // ords 0,1 have non-null varchar; append only those. + require.NoError(t, seg.appendIncludeTo(k, 1, 0)) + require.NoError(t, seg.appendIncludeTo(k, 1, 1)) + require.Nil(t, k.Nulls, "no NULL seen -> Nulls stays nil") + vec := vector.NewVec(types.T_varchar.ToType()) + require.NoError(t, vectorindex.AppendColumnBuffer(k, vec, mp)) + require.False(t, vec.IsNull(0)) + require.False(t, vec.IsNull(1)) +} + +// TestIncludeRawAtBounds pins the belt-and-suspenders bounds (F3): a malformed / truncated +// loaded segment makes includeRawAt (and thus includeVal / appendIncludeTo) return an error +// instead of panicking on an out-of-range slice. +func TestIncludeRawAtBounds(t *testing.T) { + data, err := boxfreeSeg().encodeDocmap() + require.NoError(t, err) + + // Fixed column (col0 int64): truncate includeRaw so the stride-computed row overruns. + trunc := &Segment{} + require.NoError(t, trunc.decodeDocmap(data)) + trunc.includeRaw = trunc.includeRaw[:2] + _, _, err = trunc.includeRawAt(3, 0) + require.Error(t, err) + _, _, err = trunc.includeVal(3, 0) + require.Error(t, err) + require.Error(t, trunc.appendIncludeTo(&vectorindex.ColumnBuffer{Type: types.T_int64}, 0, 3)) + + // Varlena column (col1 varchar): corrupt the offset table so it points past includeRaw. + badoff := &Segment{} + require.NoError(t, badoff.decodeDocmap(data)) + for i := range badoff.includeVarOffsets { + badoff.includeVarOffsets[i] = int32(len(badoff.includeRaw) + 1000) + } + _, _, err = badoff.includeRawAt(0, 1) + require.Error(t, err) + require.Error(t, badoff.appendIncludeTo(&vectorindex.ColumnBuffer{Type: types.T_varchar}, 1, 0)) + + // Out-of-range column / negative ord are rejected, not panicked. + good := &Segment{} + require.NoError(t, good.decodeDocmap(data)) + _, _, err = good.includeRawAt(0, 99) + require.Error(t, err) + _, _, err = good.includeRawAt(-1, 0) + require.Error(t, err) +} diff --git a/pkg/fulltext2/include_build_test.go b/pkg/fulltext2/include_build_test.go new file mode 100644 index 0000000000000..7b541841db414 --- /dev/null +++ b/pkg/fulltext2/include_build_test.go @@ -0,0 +1,98 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +var incTypesTest = []int32{int32(types.T_int64), int32(types.T_varchar)} + +// TestCdcIncludeRoundTrip: a CDC batch with an INCLUDE schema encodes+decodes its per-event +// include values (typed, incl. NULL) — the wire path from the sink into the tail builder. +func TestCdcIncludeRoundTrip(t *testing.T) { + c := NewCdc(int32(types.T_int64)) + c.IncludeTypes = incTypesTest + c.Insert(int64(1), "alpha beta", []any{int64(100), []byte("active")}) + c.Upsert(int64(2), "gamma", []any{int64(200), nil}) // NULL varchar + c.Delete(int64(3)) // delete carries no include + + buf, err := c.Encode() + require.NoError(t, err) + got, err := DecodeCdc(buf) + require.NoError(t, err) + + require.Equal(t, incTypesTest, got.IncludeTypes) + require.Len(t, got.Events, 3) + require.Equal(t, []any{int64(100), []byte("active")}, got.Events[0].Include) + require.Equal(t, int64(200), got.Events[1].Include[0]) + require.Nil(t, got.Events[1].Include[1]) // NULL + require.Equal(t, cdcDelete, got.Events[2].Op) +} + +// TestBuilderIncludeSetDoc: a Builder with WithIncludeTypes carries per-doc include values +// (via SetDoc and SetInclude) into the finished segment, decodable by includeVal. +func TestBuilderIncludeSetDoc(t *testing.T) { + b := NewBuilder("b", int32(types.T_int64), WithIncludeTypes(incTypesTest)) + // SetDoc (the CDC upsert primitive) sets terms + include together. + b.SetDoc(int64(1), []WordPos{{Word: "alpha", Pos: 0}}, []any{int64(10), []byte("x")}) + // Add + SetInclude (the base-build primitive). + require.NoError(t, b.Add("beta", 0, int64(2))) + b.SetInclude(int64(2), []any{int64(20), []byte("y")}) + + seg, err := b.Finish() + require.NoError(t, err) + + // Round-trip through serialize so we exercise the loaded-side decode path. + data, err := seg.encodeDocmap() + require.NoError(t, err) + loaded := &Segment{} + require.NoError(t, loaded.decodeDocmap(data)) + require.Equal(t, 2, loaded.nIncludeCols()) + + // ords are pk-assignment order: pk1 -> ord0, pk2 -> ord1. + v0, null0, _ := loaded.includeVal(0, 0) + require.False(t, null0) + require.Equal(t, int64(10), v0) + v1s, _, _ := loaded.includeVal(1, 1) + require.Equal(t, []byte("y"), v1s) +} + +// TestReconstructPreservesInclude: ReconstructLiveDocs (the MERGE input) yields each live +// doc's INCLUDE values, so a compaction folds them into the rebuilt base instead of dropping +// them — the key MERGE correctness invariant for covering/prefilter. +func TestReconstructPreservesInclude(t *testing.T) { + docs := []TokenizedDoc{ + {Pk: int64(1), Terms: []string{"alpha"}, Positions: []int32{0}, Include: []any{int64(10), []byte("a")}}, + {Pk: int64(2), Terms: []string{"beta"}, Positions: []int32{0}, Include: []any{int64(20), nil}}, + } + seg, err := BuildSegmentFromTokenized("s", int32(types.T_int64), docs, WithIncludeTypes(incTypesTest)) + require.NoError(t, err) + idx := NewIndex([]*Segment{seg}, nil) + + got := map[int64][]any{} + for d, derr := range idx.ReconstructLiveDocs(false) { + require.NoError(t, derr) + require.Len(t, d.Include, 2) + got[d.Pk.(int64)] = d.Include + } + require.Equal(t, int64(10), got[1][0]) + require.Equal(t, []byte("a"), got[1][1]) + require.Equal(t, int64(20), got[2][0]) + require.Nil(t, got[2][1]) // NULL preserved +} diff --git a/pkg/fulltext2/include_coverage_test.go b/pkg/fulltext2/include_coverage_test.go new file mode 100644 index 0000000000000..b5cf9fc2ace8c --- /dev/null +++ b/pkg/fulltext2/include_coverage_test.go @@ -0,0 +1,73 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/stretchr/testify/require" +) + +// TestFillInclude pins the box-free covered side of SearchInto: fillInclude builds one +// nullable ColumnBuffer per FULL index INCLUDE column (segment order) from each result's +// positional Include values, into out.Include — so the TVF emits the projected INCLUDE +// columns (mapped by segment position) with no base-table JOIN, no map[string][]any, and no +// per-row reflection. NULLs round-trip to SQL NULL, and out is reused across calls. +func TestFillInclude(t *testing.T) { + mp := mpool.MustNewZero() + idx := incIdx(t) // include cols: [0]=status(varchar), [1]=prio(int64) + s := &Fulltext2Search{idx: idx, cfg: TableConfig{IncludeColumns: []string{"status", "prio"}}} + results := []Result{ + {Pk: int64(1), Score: 1, Include: []any{[]byte("active"), int64(10)}}, + {Pk: int64(2), Score: 1, Include: []any{nil, int64(20)}}, // NULL status + } + + out := &vectorindex.SearchOutput{} + // The requested set only gates the no-op; buffers are built in FULL include order. + rt := vectorindex.RuntimeConfig{RequestedIncludeColumns: []string{"prio", "status"}} + require.NoError(t, s.fillInclude(rt, results, out)) + require.Len(t, out.Include, 2) + + // Col 0 = status (varchar): ["active", NULL]. + statusVec := vector.NewVec(types.T_varchar.ToType()) + require.NoError(t, vectorindex.AppendColumnBuffer(out.Include[0], statusVec, mp)) + require.Equal(t, "active", statusVec.GetStringAt(0)) + require.False(t, statusVec.IsNull(0)) + require.True(t, statusVec.IsNull(1)) // pk2 status is NULL + + // Col 1 = prio (int64): [10, 20], no NULLs. + prioVec := vector.NewVec(types.T_int64.ToType()) + require.NoError(t, vectorindex.AppendColumnBuffer(out.Include[1], prioVec, mp)) + require.Equal(t, []int64{10, 20}, vector.MustFixedColWithTypeCheck[int64](prioVec)) + require.False(t, prioVec.IsNull(0)) + require.False(t, prioVec.IsNull(1)) + + // AppendColumnBufferRange pages a subset: row [1,2) of prio = just 20. + pageVec := vector.NewVec(types.T_int64.ToType()) + require.NoError(t, vectorindex.AppendColumnBufferRange(out.Include[1], pageVec, 1, 1, mp)) + require.Equal(t, []int64{20}, vector.MustFixedColWithTypeCheck[int64](pageVec)) + + // Reuse: a second fill on the SAME out Resets its buffers (no stale rows appended). + require.NoError(t, s.fillInclude(rt, results[:1], out)) + require.Equal(t, 1, out.Include[1].N) + + // No requested columns → out.Include emptied (no panic). + require.NoError(t, s.fillInclude(vectorindex.RuntimeConfig{}, results, out)) + require.Empty(t, out.Include) +} diff --git a/pkg/fulltext2/include_predicate_test.go b/pkg/fulltext2/include_predicate_test.go new file mode 100644 index 0000000000000..8c0460c64d6aa --- /dev/null +++ b/pkg/fulltext2/include_predicate_test.go @@ -0,0 +1,211 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "fmt" + "math" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// incIdx builds a 5-doc index where every doc contains the term "x" (so a disjunctive query +// "x" matches all) plus two INCLUDE columns: col0 status(varchar), col1 prio(int64). +func incIdx(t *testing.T) *Index { + docs := []TokenizedDoc{ + {Pk: int64(1), Terms: []string{"x"}, Positions: []int32{0}, Include: []any{[]byte("active"), int64(10)}}, + {Pk: int64(2), Terms: []string{"x"}, Positions: []int32{0}, Include: []any{[]byte("inactive"), int64(20)}}, + {Pk: int64(3), Terms: []string{"x"}, Positions: []int32{0}, Include: []any{[]byte("active"), int64(30)}}, + {Pk: int64(4), Terms: []string{"x"}, Positions: []int32{0}, Include: []any{nil, int64(40)}}, // NULL status + {Pk: int64(5), Terms: []string{"x"}, Positions: []int32{0}, Include: []any{[]byte("archived"), int64(5)}}, + } + incTypes := []int32{int32(types.T_varchar), int32(types.T_int64)} + seg, err := BuildSegmentFromTokenized("inc", int32(types.T_int64), docs, WithIncludeTypes(incTypes)) + require.NoError(t, err) + return NewIndex([]*Segment{seg}, nil) +} + +// runPrefilter compiles specJSON and runs a disjunctive "x" search with the resulting +// INCLUDE prefilter, returning the matched pk set. +func runPrefilter(t *testing.T, idx *Index, specJSON string) []int64 { + preds, err := compileIncludePredicates([]byte(specJSON), idx.includeTypes(), idx.pkType()) + require.NoError(t, err) + res, err := idx.SearchQuery([]byte("x"), true, ParserDefault, BM25, 100, &prefilter{include: preds}) + require.NoError(t, err) + ids := resultIDs(res) + out := make([]int64, len(ids)) + for i, v := range ids { + out[i] = v.(int64) + } + return out +} + +// TestIncludePrefilterOps exercises the in-index INCLUDE prefilter end-to-end (compile → +// membership → search) across the op set, including string prefix, ranges, IN, and 3-valued +// NULL logic. status: 1=active 2=inactive 3=active 4=NULL 5=archived; prio: 1=10..5=5. +func TestIncludePrefilterOps(t *testing.T) { + idx := incIdx(t) + + require.ElementsMatch(t, []int64{1, 3}, runPrefilter(t, idx, `[{"col":0,"op":"=","val":"active"}]`)) + require.ElementsMatch(t, []int64{1, 3, 5}, runPrefilter(t, idx, `[{"col":0,"op":"prefix","val":"a"}]`)) // active/archived + require.ElementsMatch(t, []int64{1, 3, 5}, runPrefilter(t, idx, `[{"col":0,"op":"in","vals":["active","archived"]}]`)) + require.ElementsMatch(t, []int64{2, 5}, runPrefilter(t, idx, `[{"col":0,"op":"!=","val":"active"}]`)) // inactive/archived (NULL excluded) + require.ElementsMatch(t, []int64{4}, runPrefilter(t, idx, `[{"col":0,"op":"is_null"}]`)) + require.ElementsMatch(t, []int64{1, 2, 3, 5}, runPrefilter(t, idx, `[{"col":0,"op":"is_not_null"}]`)) + + // int column: range / comparisons (bare JSON numbers). + require.ElementsMatch(t, []int64{1, 2, 3}, runPrefilter(t, idx, `[{"col":1,"op":"between","lo":10,"hi":30}]`)) + require.ElementsMatch(t, []int64{4}, runPrefilter(t, idx, `[{"col":1,"op":">","val":30}]`)) + require.Empty(t, runPrefilter(t, idx, `[{"col":1,"op":">","val":100}]`)) + + // conjunction: status=active AND prio>=30 → only pk3. + require.ElementsMatch(t, []int64{3}, runPrefilter(t, idx, + `[{"col":0,"op":"=","val":"active"},{"col":1,"op":">=","val":30}]`)) + + // empty array → all 5. + require.Len(t, runPrefilter(t, idx, `[]`), 5) + + // PRIMARY KEY predicates (col -1, the pk sentinel) — evaluated inline against the stored + // pk, no docfilter second-scan. + require.ElementsMatch(t, []int64{2, 3, 4}, runPrefilter(t, idx, `[{"col":-1,"op":"between","lo":2,"hi":4}]`)) + require.ElementsMatch(t, []int64{1, 5}, runPrefilter(t, idx, `[{"col":-1,"op":"in","vals":[1,5]}]`)) + require.ElementsMatch(t, []int64{4, 5}, runPrefilter(t, idx, `[{"col":-1,"op":">","val":3}]`)) + // pk predicate ANDed with an INCLUDE predicate: id>2 AND status=active → {3}. + require.ElementsMatch(t, []int64{3}, runPrefilter(t, idx, + `[{"col":-1,"op":">","val":2},{"col":0,"op":"=","val":"active"}]`)) +} + +// TestIncludePredicateUnit covers the compiled-predicate evaluator directly, incl. the +// NULL 3-valued rule (a value comparison against NULL is UNKNOWN ⇒ not admitted). +func TestIncludePredicateUnit(t *testing.T) { + // varchar eq + p := compiledIncludePred{col: 0, kind: incEq, isStr: true, strs: [][]byte{[]byte("active")}} + require.True(t, p.test([]byte("active"), false)) + require.False(t, p.test([]byte("other"), false)) + require.False(t, p.test(nil, true)) // NULL vs value ⇒ excluded + + // int between + pb := compiledIncludePred{col: 1, kind: incBetween, ints: []int64{10, 20}} + require.True(t, pb.test(int64(15), false)) + require.True(t, pb.test(int32(10), false)) // narrower int type normalized + require.False(t, pb.test(int64(21), false)) + require.False(t, pb.test(nil, true)) + + // isnull / isnotnull + require.True(t, (&compiledIncludePred{kind: incIsNull}).test(nil, true)) + require.False(t, (&compiledIncludePred{kind: incIsNull}).test([]byte("x"), false)) + require.True(t, (&compiledIncludePred{kind: incIsNotNull}).test([]byte("x"), false)) +} + +// TestIncludePredicateUint64 pins the unsigned path: a uint64 column with values ABOVE +// MaxInt64 must compare as uint64. The old int64 path wrapped such a stored value to +// negative (and saturated the operand), silently dropping a matching row. +func TestIncludePredicateUint64(t *testing.T) { + const big = uint64(math.MaxInt64) + 100 // 9223372036854775907, > MaxInt64 + + // compile a pk predicate (col=-1) on a T_uint64 pk with a huge operand: unsigned path, + // operand parsed into uints (not wrapped/saturated). + preds, err := compileIncludePredicates( + []byte(fmt.Sprintf(`[{"col":-1,"op":"=","val":%d}]`, big)), nil, int32(types.T_uint64)) + require.NoError(t, err) + require.Len(t, preds, 1) + require.True(t, preds[0].isUnsigned) + require.Equal(t, []uint64{big}, preds[0].uints) + require.True(t, preds[0].test(big, false)) // exact match (int64 path would miss it) + require.False(t, preds[0].test(big-1, false)) + require.False(t, preds[0].test(uint64(1), false)) + + // '>' at the MaxInt64 boundary: big > MaxInt64 must be TRUE (int64 wrap would say false). + gt := compiledIncludePred{col: 0, kind: incGt, isUnsigned: true, uints: []uint64{uint64(math.MaxInt64)}} + require.True(t, gt.test(big, false)) + require.False(t, gt.test(uint64(5), false)) + + // IN including a huge value. + in := compiledIncludePred{col: 0, kind: incIn, isUnsigned: true, uints: []uint64{1, big}} + require.True(t, in.test(big, false)) + require.True(t, in.test(uint64(1), false)) + require.False(t, in.test(uint64(2), false)) +} + +// TestCompileIncludePredicatesErrors: bad op / out-of-range col / bad int literal are rejected. +func TestCompileIncludePredicatesErrors(t *testing.T) { + it := []int32{int32(types.T_varchar), int32(types.T_int64)} + pk := int32(types.T_int64) + _, err := compileIncludePredicates([]byte(`[{"col":0,"op":"bogus","val":"a"}]`), it, pk) + require.Error(t, err) + _, err = compileIncludePredicates([]byte(`[{"col":9,"op":"=","val":"a"}]`), it, pk) + require.Error(t, err) + _, err = compileIncludePredicates([]byte(`[{"col":1,"op":"=","val":"notanint"}]`), it, pk) + require.Error(t, err) + // a pk predicate on a non-comparable pk type (decimal128) is rejected (planner falls back). + _, err = compileIncludePredicates([]byte(`[{"col":-1,"op":"=","val":1}]`), it, int32(types.T_decimal128)) + require.Error(t, err) + // empty spec → nil, no error. + preds, err := compileIncludePredicates(nil, it, pk) + require.NoError(t, err) + require.Nil(t, preds) +} + +// TestBitPkComparable (B2): a BIT primary key is comparable — the planner peels a pk +// predicate on a BIT pk, so compileIncludePredicates must accept T_bit (via the unsigned +// path) rather than erroring "col -1 type not comparable" (which failed the whole query). +func TestBitPkComparable(t *testing.T) { + require.True(t, isComparableIncludeType(int32(types.T_bit))) + preds, err := compileIncludePredicates([]byte(`[{"col":-1,"op":"=","val":5}]`), nil, int32(types.T_bit)) + require.NoError(t, err) + require.Len(t, preds, 1) + require.True(t, preds[0].isUnsigned) + require.True(t, preds[0].test(uint64(5), false)) + require.False(t, preds[0].test(uint64(6), false)) +} + +// TestResultCarriesInclude pins the covering primitive: a top-k search Result carries the +// doc's INCLUDE column values (in index order, NULL-aware) across the WAND/boolean path +// (heapToResults) and the NL phrase path (boundedTopK) — so a covering query is served from +// the index. Uses the incIdx fixture (col0 status varchar, col1 prio int64). +func TestResultCarriesInclude(t *testing.T) { + idx := incIdx(t) + + byPk := func(res []Result) map[int64][]any { + m := map[int64][]any{} + for _, r := range res { + m[r.Pk.(int64)] = r.Include + } + return m + } + + // Boolean/WAND path (disjunctive "x"). + wand, err := idx.SearchQuery([]byte("x"), true, ParserDefault, BM25, 100, nil) + require.NoError(t, err) + mw := byPk(wand) + require.Equal(t, []any{[]byte("active"), int64(10)}, mw[1]) + require.Equal(t, []any{nil, int64(40)}, mw[4]) // NULL status preserved + + // NL phrase path (boundedTopK). + nl, err := idx.SearchQuery([]byte("x"), false, ParserDefault, BM25, 100, nil) + require.NoError(t, err) + mn := byPk(nl) + require.Equal(t, []any{[]byte("archived"), int64(5)}, mn[5]) + + // An index WITHOUT include columns → Result.Include is nil. + seg, err := BuildSegmentFromTokenized("noinc", int32(types.T_int64), + []TokenizedDoc{{Pk: int64(1), Terms: []string{"x"}, Positions: []int32{0}}}) + require.NoError(t, err) + res, err := NewIndex([]*Segment{seg}, nil).SearchQuery([]byte("x"), true, ParserDefault, BM25, 10, nil) + require.NoError(t, err) + require.Nil(t, res[0].Include) +} diff --git a/pkg/fulltext2/index_test.go b/pkg/fulltext2/index_test.go new file mode 100644 index 0000000000000..cac1c7871c4ab --- /dev/null +++ b/pkg/fulltext2/index_test.go @@ -0,0 +1,116 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "math" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" + "github.com/stretchr/testify/require" +) + +// TestIdfSquaredClamp pins the df-in-[1,n] clamp: df is summed over physical (dead+live) +// postings while n is live-only, so churn can push df past n. The clamp keeps idf>=0 and +// monotonic instead of squaring a negative log10(n/df) into a positive impact. +func TestIdfSquaredClamp(t *testing.T) { + // df < 1 clamps up to 1: idf = (log10(n))^2. + require.InDelta(t, math.Log10(10)*math.Log10(10), idfSquared(10, 0), 1e-9) + // df == n: term in every live doc -> idf 0. + require.Equal(t, 0.0, idfSquared(10, 10)) + // df > n (dirty base+tail churn): clamped to n -> idf 0, NOT a positive squared-neg-log. + require.Equal(t, 0.0, idfSquared(10, 25)) + // monotonically non-increasing in df across [1, n]. + prev := math.Inf(1) + for df := 1; df <= 10; df++ { + v := idfSquared(10, df) + require.LessOrEqualf(t, v, prev, "idfSquared not monotonic at df=%d", df) + prev = v + } +} + +func buildSeg(t *testing.T, id string, rec int64, docs []Doc) *Segment { + t.Helper() + s, err := BuildSegmentFromDocs(id, int32(types.T_int64), docs, tokenizer.NewSimpleTokenizer()) + require.NoError(t, err) + s.Recency = rec + return s +} + +func iquery(t *testing.T, idx *Index, q string) []any { + t.Helper() + rs, err := idx.SearchText([]byte(q), tokenizer.NewSimpleTokenizer(), TfIdf, 10, nil) + require.NoError(t, err) + return pkslice(rs) +} + +// TestIndexLiveness: a base segment + a tail that UPDATES pk 2 (new text) and +// adds pk 4, with pk 3 deleted. Queries must see only live copies. +func TestIndexLiveness(t *testing.T) { + base := buildSeg(t, "base", 0, []Doc{ + {int64(1), []byte("alpha beta")}, + {int64(2), []byte("gamma")}, // superseded below + {int64(3), []byte("delta")}, // deleted below + }) + tail := buildSeg(t, "tail", 1, []Doc{ + {int64(2), []byte("omega")}, // new text for pk 2 + {int64(4), []byte("beta")}, + }) + idx := NewIndex([]*Segment{base, tail}, map[any]int64{normalizeKey(int64(3)): 1}) + + require.Equal(t, int64(3), idx.NumDocs()) // pk 1, 2(new), 4 live; pk 3 deleted + + // "beta" is in base:pk1 and tail:pk4 — both live (equal score → order unspecified). + require.ElementsMatch(t, []any{int64(1), int64(4)}, iquery(t, idx, "beta")) + // pk2's OLD text ("gamma") is superseded by the tail → not visible. + require.Empty(t, iquery(t, idx, "gamma")) + // pk2's NEW text ("omega") is the live copy. + require.Equal(t, []any{int64(2)}, iquery(t, idx, "omega")) + // pk3 is deleted. + require.Empty(t, iquery(t, idx, "delta")) + // base-only live doc. + require.Equal(t, []any{int64(1)}, iquery(t, idx, "alpha")) +} + +// TestIndexGlobalStats: df/idf and avgDocLen span segments. A term present once +// in each of two segments has df=2 against the global N. +func TestIndexGlobalStats(t *testing.T) { + s1 := buildSeg(t, "s1", 0, []Doc{{int64(1), []byte("cat")}, {int64(2), []byte("dog")}}) + s2 := buildSeg(t, "s2", 0, []Doc{{int64(3), []byte("cat")}, {int64(4), []byte("bird")}}) + idx := NewIndex([]*Segment{s1, s2}, nil) + + require.Equal(t, int64(4), idx.NumDocs()) + require.EqualValues(t, 1.0, idx.globalAvgDocLen) // every doc has 1 token + // "cat" is in pk1 and pk3 across the two segments (equal score → order unspecified). + require.ElementsMatch(t, []any{int64(1), int64(3)}, iquery(t, idx, "cat")) + // idf uses the GLOBAL df (=2) and N (=4), not a per-segment count. + rs, err := idx.SearchText([]byte("cat"), tokenizer.NewSimpleTokenizer(), TfIdf, 10, nil) + require.NoError(t, err) + want := idfSquared(4, 2) // tf=1 + require.InDelta(t, want, rs[0].Score, 1e-5) +} + +// TestIndexPhraseAcrossSegments: an exact phrase resolves within each segment; +// the live matches across segments merge. +func TestIndexPhraseAcrossSegments(t *testing.T) { + s1 := buildSeg(t, "s1", 0, []Doc{{int64(1), []byte("quick brown fox")}}) + s2 := buildSeg(t, "s2", 0, []Doc{{int64(2), []byte("the quick brown fox runs")}, {int64(3), []byte("brown quick")}}) + idx := NewIndex([]*Segment{s1, s2}, nil) + + // both match with equal TfIdf score → order unspecified. + require.ElementsMatch(t, []any{int64(1), int64(2)}, iquery(t, idx, "quick brown fox")) + require.Empty(t, iquery(t, idx, "fox brown")) // not contiguous anywhere +} diff --git a/pkg/fulltext2/jsonvalue_test.go b/pkg/fulltext2/jsonvalue_test.go new file mode 100644 index 0000000000000..fe50be3f7b9be --- /dev/null +++ b/pkg/fulltext2/jsonvalue_test.go @@ -0,0 +1,79 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// TestJSONValueExactMatch pins the json_value parser's contract to classic fulltext: +// each json leaf value is ONE whole atomic token (no ngram, punctuation/CJK kept), and +// a query operand matches a WHOLE value exactly — never a substring/bag. This is the +// divergence fixed in parity with classic (tokenize.go stores the whole value; sql.go +// queries `word = value`). +func TestJSONValueExactMatch(t *testing.T) { + docs := []Doc{ + {int64(1), []byte(`{"a":"redbluebrownyelloworange","b":"winter"}`)}, + {int64(2), []byte(`{"a":"red","b":"winter"}`)}, + {int64(3), []byte(`{"a":"中文學習教材"}`)}, + {int64(4), []byte(`{"a":"happybirthday.happy-birthday_happybirthday"}`)}, + } + + run := func(t *testing.T, idx *Index) { + bl := func(q string) []any { + rs, err := idx.SearchQuery([]byte(q), true, ParserJSONValue, BM25, 100, nil) + require.NoError(t, err) + return resultIDs(rs) + } + nl := func(q string) []any { + rs, err := idx.SearchQuery([]byte(q), false, ParserJSONValue, BM25, 100, nil) + require.NoError(t, err) + return resultIDs(rs) + } + + // Whole-value exact match — including punctuation and CJK values as ONE token. + require.ElementsMatch(t, []any{int64(1)}, bl("redbluebrownyelloworange")) + require.ElementsMatch(t, []any{int64(3)}, bl("中文學習教材")) + require.ElementsMatch(t, []any{int64(4)}, bl("happybirthday.happy-birthday_happybirthday")) + + // "red" is a WHOLE value only in doc 2 — NOT a substring hit on doc 1. + require.ElementsMatch(t, []any{int64(2)}, bl("red")) + require.ElementsMatch(t, []any{int64(2)}, nl("red")) + + // Distinct-value OR / multi-operand MUST. + require.ElementsMatch(t, []any{int64(1), int64(2)}, bl("winter")) // both docs carry value "winter" + require.ElementsMatch(t, []any{int64(2)}, bl("+red +winter")) // exact red AND exact winter + + // No bag-of-words / no ngram: a substring of a value never matches. + require.Empty(t, bl("bluebrown"), "substring of doc1's value is not a whole value") + require.Empty(t, bl("中文學習"), "substring of doc3's value is not a whole value") + require.Empty(t, bl("happybirthday"), "substring of doc4's value is not a whole value") + } + + seg, err := BuildSegmentFromDocsParser("jv", int32(types.T_int64), docs, ParserJSONValue) + require.NoError(t, err) + t.Run("build-side", func(t *testing.T) { run(t, NewIndex([]*Segment{seg}, nil)) }) + + blob, err := seg.Serialize() + require.NoError(t, err) + loaded, err := Deserialize("jv", bytes.NewReader(blob)) + require.NoError(t, err) + t.Cleanup(func() { _ = loaded.dict.Close() }) + t.Run("loaded-side", func(t *testing.T) { run(t, NewIndex([]*Segment{loaded}, nil)) }) +} diff --git a/pkg/fulltext2/membership_test.go b/pkg/fulltext2/membership_test.go new file mode 100644 index 0000000000000..9248121a77f10 --- /dev/null +++ b/pkg/fulltext2/membership_test.go @@ -0,0 +1,135 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/docfilter" + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/stretchr/testify/require" +) + +// TestPrefilterMembership exercises the WHERE-clause membership prefilter end to end +// against a real C docfilter (the same bytes the runtime-filter pushdown ships): a +// candidate doc is admitted iff its pk passes the filter, at every admit point — the +// WAND walk (disjunctive), the full boolean evaluator (MUST), and NL phrase — and a +// nil filter is unchanged. It also pins the ord→pk encode in docFilterMembership. +func TestPrefilterMembership(t *testing.T) { + mp := mpool.MustNewZero() + b := NewBuilder("pf", int32(types.T_int64)) + for i := int64(0); i < 10; i++ { + feed(t, b, i, "x", "y") // "x" and "y" occur in every doc + } + seg, err := b.Finish() + require.NoError(t, err) + idx := NewIndex([]*Segment{seg}, nil) + + // Membership over the EVEN pks {0,2,4,6,8}, built exactly as the pushdown does. + vec := vector.NewVec(types.New(types.T_int64, 8, 0)) + for i := int64(0); i < 10; i += 2 { + require.NoError(t, vector.AppendFixed(vec, i, false, mp)) + } + fbytes, err := docfilter.Build(vec) + require.NoError(t, err) + filter, err := docfilter.New(fbytes) + require.NoError(t, err) + defer filter.Free() + + // docFilterMembership.Contains: even ords pass, odd rejected (pk-encode is correct). + dfm := &docFilterMembership{seg: seg, f: filter} + for i := int64(0); i < 10; i++ { + require.Equalf(t, i%2 == 0, dfm.Contains(i), "ord %d", i) + } + + even := []any{int64(0), int64(2), int64(4), int64(6), int64(8)} + + // WAND path (pure disjunction "x") with the filter → only even pks. + rWand, err := idx.SearchQuery([]byte("x"), true, ParserDefault, BM25, 100, &prefilter{docFilter: filter}) + require.NoError(t, err) + require.ElementsMatch(t, even, resultIDs(rWand)) + + // Full boolean evaluator (MUST "+x") with the filter → only even pks. + rBool, err := idx.SearchQuery([]byte("+x"), true, ParserDefault, BM25, 100, &prefilter{docFilter: filter}) + require.NoError(t, err) + require.ElementsMatch(t, even, resultIDs(rBool)) + + // NL phrase path ("x") with the filter → only even pks. + rNL, err := idx.SearchQuery([]byte("x"), false, ParserDefault, BM25, 100, &prefilter{docFilter: filter}) + require.NoError(t, err) + require.ElementsMatch(t, even, resultIDs(rNL)) + + // nil filter is unchanged: all 10 docs. + rAll, err := idx.SearchQuery([]byte("x"), true, ParserDefault, BM25, 100, nil) + require.NoError(t, err) + require.Len(t, rAll, 10) + + // The filter also correctly bounds a pushed LIMIT to the FILTERED set: top-2 over + // the even docs returns 2 even pks (not 2 of all 10 then post-filtered to fewer). + rLimit, err := idx.SearchQuery([]byte("x"), true, ParserDefault, BM25, 2, &prefilter{docFilter: filter}) + require.NoError(t, err) + require.Len(t, rLimit, 2) + for _, pk := range resultIDs(rLimit) { + require.Contains(t, even, pk) + } +} + +// TestPhraseDfFilterIndependent pins re-review [0]: an NL phrase's idf df is the +// CORPUS phrase df (all live matches), not the WHERE-filtered subset, so a matched +// doc's score must NOT change when an unrelated WHERE prefilter is applied. +func TestPhraseDfFilterIndependent(t *testing.T) { + mp := mpool.MustNewZero() + b := NewBuilder("df", int32(types.T_int64)) + for i := int64(0); i < 4; i++ { + feed(t, b, i, "alpha") // 4 docs contain the phrase + } + for i := int64(4); i < 8; i++ { + feed(t, b, i, "other") + } + seg, err := b.Finish() + require.NoError(t, err) + idx := NewIndex([]*Segment{seg}, nil) + + scoreOf := func(rs []Result, pk int64) float64 { + for _, r := range rs { + if r.Pk.(int64) == pk { + return float64(r.Score) + } + } + t.Fatalf("pk %d not in results", pk) + return 0 + } + unfiltered := idx.SearchPhrase(phr("alpha"), BM25, 100, nil) + require.Len(t, unfiltered, 4) + s0 := scoreOf(unfiltered, 0) + + // WHERE prefilter allowing only {0,1} — df must stay 4 (corpus), so doc 0's score + // is unchanged; before the fix df shrank to 2 and the score differed. + vec := vector.NewVec(types.New(types.T_int64, 8, 0)) + require.NoError(t, vector.AppendFixed(vec, int64(0), false, mp)) + require.NoError(t, vector.AppendFixed(vec, int64(1), false, mp)) + fb, err := docfilter.Build(vec) + require.NoError(t, err) + filter, err := docfilter.New(fb) + require.NoError(t, err) + defer filter.Free() + + filtered := idx.SearchPhrase(phr("alpha"), BM25, 100, &prefilter{docFilter: filter}) + require.Len(t, filtered, 2, "only the allowed docs are returned") + require.InDelta(t, s0, scoreOf(filtered, 0), 1e-5, + "doc 0's score must be filter-independent (corpus df, not filtered count)") +} diff --git a/pkg/fulltext2/mmap_test.go b/pkg/fulltext2/mmap_test.go new file mode 100644 index 0000000000000..ba99ee3952b89 --- /dev/null +++ b/pkg/fulltext2/mmap_test.go @@ -0,0 +1,151 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "os" + "sort" + "sync" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// TestMmapLoad validates the base-segment mmap path (the storage LoadFromStorage +// shape, without a cluster): serialize a segment to a file, mmap it, decode over +// the mapping — FST + positions are views into the shared mmap, docIDs/tfs expand +// off-heap — then search (ranking + phrase), and confirm Free() munmaps and deletes +// the file. +func TestMmapLoad(t *testing.T) { + b := NewBuilder("mm", int32(types.T_int64)) + feed(t, b, int64(0), "alpha", "beta", "alpha") // "alpha beta" phrase present + feed(t, b, int64(1), "beta", "gamma") + feed(t, b, int64(2), "alpha", "gamma", "beta") + built, err := b.Finish() + require.NoError(t, err) + blob, err := built.Serialize() + require.NoError(t, err) + + f, err := os.CreateTemp("", "ft2mmtest") + require.NoError(t, err) + path := f.Name() + _, err = f.Write(blob) + require.NoError(t, err) + require.NoError(t, f.Sync()) + data, err := mmapReadOnly(f) + f.Close() + require.NoError(t, err) + + s := &Segment{Id: "mm", mmapData: data, mmapPath: path} + require.NoError(t, s.decodeSegment(data)) + require.NotNil(t, s.mmapData, "segment holds the mmap") + + // Every term: docIDs off-heap match; positions (posRaw into the mmap) materialize + // to exactly the build-side positions. + for _, term := range built.sortedTerms { + want, ok := built.Lookup(term) + require.True(t, ok, term) + got, ok := s.LookupLoaded(term) + require.True(t, ok, term) + require.Equal(t, want.docIDs, got.materializeDocIDs(), term) + gp := got.materializePositions() + require.Equal(t, len(want.positions), len(gp), term) + for i := range want.positions { + require.Equalf(t, want.positions[i], gp[i], "%s positions[%d]", term, i) + } + } + + idx := NewIndex([]*Segment{s}, nil) + // ranking (WAND, docIDs off-heap): "beta" hits all three docs. + require.Len(t, idx.SearchPhrase(phr("beta"), BM25, 10, nil), 3) + // phrase (positions from the mmap): only doc 0 has contiguous "alpha beta". + ph := idx.SearchPhrase(phr("alpha", "beta"), BM25, 10, nil) + require.Len(t, ph, 1) + require.Equal(t, int64(0), ph[0].Pk) + + // Free munmaps and deletes the owned file. + idx.Free() + require.Nil(t, s.mmapData) + _, statErr := os.Stat(path) + require.True(t, os.IsNotExist(statErr), "Free must delete the mmap-owned file") +} + +// TestConcurrentBlockDecode proves the loaded-segment design invariant: docID/tf +// blocks live in ONE shared, read-only mmap, and each WAND cursor decodes into its +// OWN per-cursor buffer — so many queries can hit the same segment concurrently with +// no lock and no race. Terms span many blocks (N/BlockSize each) so skipTo crosses +// block boundaries, and results must match a single-threaded reference exactly. Run +// under -race, this is the guard against a shared/mutable block cache. +func TestConcurrentBlockDecode(t *testing.T) { + const N = 4000 // ~31 blocks for "alpha" + b := NewBuilder("cc", int32(types.T_int64)) + for i := 0; i < N; i++ { + toks := []string{"alpha"} + if i%2 == 0 { + toks = append(toks, "beta") + } + if i%3 == 0 { + toks = append(toks, "gamma") + } + feed(t, b, int64(i), toks...) + } + built, err := b.Finish() + require.NoError(t, err) + blob, err := built.Serialize() + require.NoError(t, err) + + f, err := os.CreateTemp("", "cctest") + require.NoError(t, err) + path := f.Name() + _, err = f.Write(blob) + require.NoError(t, err) + require.NoError(t, f.Sync()) + data, err := mmapReadOnly(f) + f.Close() + require.NoError(t, err) + s := &Segment{Id: "cc", mmapData: data, mmapPath: path} + require.NoError(t, s.decodeSegment(data)) + idx := NewIndex([]*Segment{s}, nil) + defer idx.Free() + + // Reference (single-threaded): a disjunctive WAND top-k over all three terms. + pkset := func(rs []Result) []int64 { + out := make([]int64, len(rs)) + for i, r := range rs { + out[i] = r.Pk.(int64) + } + sort.Slice(out, func(a, b int) bool { return out[a] < out[b] }) + return out + } + ref, err := idx.SearchQuery([]byte("alpha beta gamma"), true, "default", BM25, 100, nil) + require.NoError(t, err) + require.NotEmpty(t, ref) + want := pkset(ref) + + var wg sync.WaitGroup + for g := 0; g < 8; g++ { + wg.Add(1) + go func() { + defer wg.Done() + for it := 0; it < 40; it++ { + got, e := idx.SearchQuery([]byte("alpha beta gamma"), true, "default", BM25, 100, nil) + require.NoError(t, e) + require.Equal(t, want, pkset(got)) // same top-k SET (ties aside) + } + }() + } + wg.Wait() +} diff --git a/pkg/fulltext2/multiseg_test.go b/pkg/fulltext2/multiseg_test.go new file mode 100644 index 0000000000000..5e3dab8b392d5 --- /dev/null +++ b/pkg/fulltext2/multiseg_test.go @@ -0,0 +1,217 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "fmt" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// TestTemporalPkNoCollision pins the value-keyed liveness fix: two DATETIME pks that +// differ only in microseconds render to the SAME %v/String() (which truncates to +// whole seconds), so a string-keyed liveness map would collide them and silently drop +// one live doc. normalizeKey keys by the pk VALUE, so both stay distinct. +func TestTemporalPkNoCollision(t *testing.T) { + const sec = int64(63745056000) // arbitrary second count + dt1 := types.Datetime(sec*1_000_000 + 1) // ...000001 microseconds + dt2 := types.Datetime(sec*1_000_000 + 2) // ...000002 microseconds + require.Equal(t, fmt.Sprintf("%v", dt1), fmt.Sprintf("%v", dt2), + "precondition: the two pks DO collide under %%v (String drops microseconds)") + + b := NewBuilder("dt", int32(types.T_datetime)) + feed(t, b, dt1, "alpha") + feed(t, b, dt2, "alpha") + seg, err := b.Finish() + require.NoError(t, err) + idx := NewIndex([]*Segment{seg}, nil) + + require.Equal(t, int64(2), idx.globalN, "both microsecond-distinct pks must be live") + require.Len(t, idx.SearchPhrase(phr("alpha"), BM25, 10, nil), 2, + "both docs must be returned (neither dropped by a lossy key)") +} + +// docWords are 24 docs with skewed term frequencies (alpha common, beta medium, +// gamma/delta rarer) and varying lengths, so idf and the BM25 length-norm both +// matter — a boolean ranking is only correct if scored on GLOBAL corpus stats. +func docWords() [][]string { + out := make([][]string, 24) + for i := 0; i < 24; i++ { + w := []string{"alpha"} // alpha in every doc + if i%2 == 0 { + w = append(w, "beta", "beta") + } + if i%3 == 0 { + w = append(w, "gamma") + } + if i%4 == 0 { + w = append(w, "delta", "delta", "delta") + } + if i%5 == 0 { + w = append(w, "beta") + } + out[i] = w + } + return out +} + +func buildFtSeg(t *testing.T, id string, rec int64, lo, hi int) *Segment { + b := NewBuilder(id, int32(types.T_int64)) + words := docWords() + for i := lo; i < hi; i++ { + feed(t, b, int64(i), words[i]...) + } + s, err := b.Finish() + require.NoError(t, err) + s.Recency = rec + return s +} + +// TestMultiSegmentGlobalStats pins the #5 fix: a boolean query over docs SPLIT across +// a base + CDC-tail (three segments) must return the IDENTICAL ranking + scores as the +// same docs in ONE segment. The global corpus stats (N, avgDocLen, per-term df summed +// across segments) make cross-segment scores comparable; before the fix each segment +// used its LOCAL N/df, so the split index scored — and thus ranked / truncated — docs +// differently from the single-segment truth. +func TestMultiSegmentGlobalStats(t *testing.T) { + // Reference: all 24 docs in one segment. + ref := NewIndex([]*Segment{buildFtSeg(t, "ref", 0, 0, 24)}, nil) + // Split: disjoint pk ranges across three segments (a base + two CDC tails), each + // with its own (smaller) local N/df. + split := NewIndex([]*Segment{ + buildFtSeg(t, "a", 0, 0, 10), + buildFtSeg(t, "b", 1, 10, 18), + buildFtSeg(t, "c", 2, 18, 24), + }, nil) + + scoreByPk := func(rs []Result) map[int64]float64 { + m := make(map[int64]float64, len(rs)) + for _, r := range rs { + m[r.Pk.(int64)] = float64(r.Score) + } + return m + } + for _, q := range []string{"alpha beta gamma delta", "beta gamma", "+alpha beta", "alpha delta"} { + // Full result (k large): the split index must return the SAME set of pks, each + // with the SAME score as the single-segment truth — the direct proof that the + // per-segment idf drift is gone (a dropped or rescored doc would diverge here). + want, err := ref.SearchQuery([]byte(q), true, ParserDefault, BM25, 1000, nil) + require.NoError(t, err) + got, err := split.SearchQuery([]byte(q), true, ParserDefault, BM25, 1000, nil) + require.NoError(t, err) + ws, gsc := scoreByPk(want), scoreByPk(got) + require.Equalf(t, len(ws), len(gsc), "q=%q: same matched-doc count", q) + for pk, s := range ws { + g, ok := gsc[pk] + require.Truef(t, ok, "q=%q: pk %d present in split", q, pk) + require.InDeltaf(t, s, g, 1e-5, "q=%q pk %d: global score matches single-segment", q, pk) + } + // Bounded top-k: the ranked SCORE sequence is identical (which tie member fills + // the k-th slot is arbitrary — the documented WAND tie caveat — but the scores at + // every rank, and the count, must match; a truncation loss would shorten got). + for _, k := range []int{3, 5, 12} { + w, err := ref.SearchQuery([]byte(q), true, ParserDefault, BM25, k, nil) + require.NoError(t, err) + g, err := split.SearchQuery([]byte(q), true, ParserDefault, BM25, k, nil) + require.NoError(t, err) + require.Lenf(t, g, len(w), "q=%q k=%d: same count (no truncation loss)", q, k) + for i := range w { + require.InDeltaf(t, w[i].Score, g[i].Score, 1e-5, "q=%q k=%d rank=%d: score", q, k, i) + } + } + } +} + +// TestMultiSegmentPhraseStats pins the ② fix: a boolean-mode PHRASE clause scores on +// the cross-segment (global) phrase df, so a phrase query over a base + CDC-tail split +// ranks identically to the same docs in one segment (before the fix the phrase clause +// used per-segment idf and diverged). +func TestMultiSegmentPhraseStats(t *testing.T) { + words := func(i int) []string { + w := []string{"filler", "text"} + if i%3 == 0 { + w = append(w, "quick", "brown", "fox") // contiguous "quick brown" phrase + } + if i%2 == 0 { + w = append(w, "quick") // 'quick' alone — not the phrase + } + return w + } + build := func(id string, rec int64, lo, hi int) *Segment { + b := NewBuilder(id, int32(types.T_int64)) + for i := lo; i < hi; i++ { + feed(t, b, int64(i), words(i)...) + } + s, err := b.Finish() + require.NoError(t, err) + s.Recency = rec + return s + } + ref := NewIndex([]*Segment{build("ref", 0, 0, 18)}, nil) + split := NewIndex([]*Segment{build("a", 0, 0, 9), build("b", 1, 9, 18)}, nil) + + byPk := func(rs []Result) map[int64]float64 { + m := make(map[int64]float64, len(rs)) + for _, r := range rs { + m[r.Pk.(int64)] = float64(r.Score) + } + return m + } + q := `"quick brown"` // quoted => boolean-mode phrase clause + want, err := ref.SearchQuery([]byte(q), true, ParserDefault, BM25, 100, nil) + require.NoError(t, err) + got, err := split.SearchQuery([]byte(q), true, ParserDefault, BM25, 100, nil) + require.NoError(t, err) + ws, gsc := byPk(want), byPk(got) + require.NotEmpty(t, ws) + require.Equal(t, len(ws), len(gsc), "same phrase-matched set") + for pk, s := range ws { + g, ok := gsc[pk] + require.Truef(t, ok, "pk %d present in split", pk) + require.InDeltaf(t, s, g, 1e-5, "pk %d: global phrase idf matches single-segment", pk) + } +} + +// TestMultiSegmentLiveness pins the liveness-inside-the-walk half of #5: when a pk is +// UPDATEd (a later, higher-Recency segment holds a fresh copy), a boolean query must +// score/return only the LIVE copy — the dead copy in the older segment must never +// enter a segment's top-k (which would let the merge under-fill by dropping it). +func TestMultiSegmentLiveness(t *testing.T) { + base := NewBuilder("base", int32(types.T_int64)) + feed(t, base, int64(1), "alpha", "alpha") // pk1: old text = alpha + feed(t, base, int64(2), "alpha") // pk2: alpha (never updated) + baseSeg, err := base.Finish() + require.NoError(t, err) + baseSeg.Recency = 0 + + tail := NewBuilder("tail", int32(types.T_int64)) + feed(t, tail, int64(1), "beta", "beta") // pk1 UPDATEd: new text = beta + tailSeg, err := tail.Finish() + require.NoError(t, err) + tailSeg.Recency = 1 + + idx := NewIndex([]*Segment{baseSeg, tailSeg}, nil) + + ids := func(q string) []any { + r, err := idx.SearchQuery([]byte(q), true, ParserDefault, BM25, 100, nil) + require.NoError(t, err) + return resultIDs(r) + } + // pk1's OLD alpha copy is dead → alpha matches only pk2; pk1's live copy is beta. + require.ElementsMatch(t, []any{int64(2)}, ids("alpha")) + require.ElementsMatch(t, []any{int64(1)}, ids("beta")) +} diff --git a/pkg/fulltext2/parser_test.go b/pkg/fulltext2/parser_test.go new file mode 100644 index 0000000000000..e045b17c3ae18 --- /dev/null +++ b/pkg/fulltext2/parser_test.go @@ -0,0 +1,141 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Parser support: the ngram (default), gojieba, and json parsers over a fulltext2 +// index. CJK is the focus — NL exact-phrase over overlapping 3-grams is unreliable, +// so ngram/json NL routes CJK through classic fulltext's redundant-ngram removal +// (bag-of-words), while gojieba word-segmentation keeps exact-phrase. +package fulltext2 + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// zhCorpus indexes five CJK docs under parser and returns a queryable Index. +// +// 0: 遠東兒童中文學習教材 (contains 中文學習, 兒童) +// 1: 中文短篇小說適合初學者 (contains 中文, no 學習/兒童) +// 2: 兒童學習樂趣 (contains 兒童, 學習) +// 3: 教學指引與生字卡 (none of the above) +// 4: 中文學習中文學習 (contains 中文學習 twice) +func zhCorpus(t *testing.T, parser string) *Index { + docs := []Doc{ + {int64(0), []byte("遠東兒童中文學習教材")}, + {int64(1), []byte("中文短篇小說適合初學者")}, + {int64(2), []byte("兒童學習樂趣")}, + {int64(3), []byte("教學指引與生字卡")}, + {int64(4), []byte("中文學習中文學習")}, + } + seg, err := BuildSegmentFromDocsParser("zh", int32(types.T_int64), docs, parser) + require.NoError(t, err) + return NewIndex([]*Segment{seg}, nil) +} + +func nlIDs(t *testing.T, idx *Index, parser, q string) []any { + res, err := idx.SearchQuery([]byte(q), false, parser, BM25, 100, nil) + require.NoError(t, err) + return resultIDs(res) +} + +func boolIDs(t *testing.T, idx *Index, parser, q string) []any { + res, err := idx.SearchQuery([]byte(q), true, parser, BM25, 100, nil) + require.NoError(t, err) + return resultIDs(res) +} + +func resultIDs(res []Result) []any { + out := make([]any, len(res)) + for i, r := range res { + out[i] = r.Pk + } + return out +} + +func requireJieba(t *testing.T) { + if _, err := DocTokenizer(ParserGojieba); err != nil { + t.Skipf("gojieba dictionary unavailable: %v", err) + } +} + +// --- ngram (default) parser: CJK NL is bag-of-words over redundant-removed 3-grams --- + +func TestNgramNLChinese(t *testing.T) { + idx := zhCorpus(t, ParserNgram) + // 中文學習 -> ngrams 中文學, 文學習 (bag-of-words); docs 0 and 4 contain both. + require.ElementsMatch(t, []any{int64(0), int64(4)}, nlIDs(t, idx, ParserNgram, "中文學習")) + // 兒童 (<3 chars) -> prefix 兒童* ; matches 3-grams starting 兒童 in docs 0 and 2. + require.ElementsMatch(t, []any{int64(0), int64(2)}, nlIDs(t, idx, ParserNgram, "兒童")) + // unknown compound -> no hit. + require.Empty(t, nlIDs(t, idx, ParserNgram, "西班牙語")) +} + +func TestNgramBooleanChinese(t *testing.T) { + idx := zhCorpus(t, ParserNgram) + // +中文 +學習 : both compounds' ngrams present -> docs 0, 4 (2 has 學習 but not 中文). + require.ElementsMatch(t, []any{int64(0), int64(4)}, boolIDs(t, idx, ParserNgram, "+中文 +學習")) + // +兒童 -中文 : 兒童 present, 中文 absent -> only doc 2 (doc 0 has both). + require.ElementsMatch(t, []any{int64(2)}, boolIDs(t, idx, ParserNgram, "+兒童 -中文")) +} + +func TestNgramLatinStaysPhrase(t *testing.T) { + // Latin under ngram keeps fulltext2 exact-phrase NL. + docs := []Doc{ + {int64(0), []byte("the quick brown fox")}, + {int64(1), []byte("quick red fox")}, + {int64(2), []byte("brown fox jumps")}, + } + seg, err := BuildSegmentFromDocsParser("en", int32(types.T_int64), docs, ParserNgram) + require.NoError(t, err) + idx := NewIndex([]*Segment{seg}, nil) + // "brown fox" is contiguous only in docs 0 and 2 (not doc 1: quick red fox). + require.ElementsMatch(t, []any{int64(0), int64(2)}, nlIDs(t, idx, ParserNgram, "brown fox")) +} + +// --- gojieba parser: word segmentation, exact-phrase NL works for CJK --- + +func TestGojiebaNLChinese(t *testing.T) { + requireJieba(t) + idx := zhCorpus(t, ParserGojieba) + // jieba segments 中文學習 into words; exact-phrase matches docs 0 and 4. + require.ElementsMatch(t, []any{int64(0), int64(4)}, nlIDs(t, idx, ParserGojieba, "中文學習")) + // 兒童 is a jieba word present in docs 0 and 2. + require.ElementsMatch(t, []any{int64(0), int64(2)}, nlIDs(t, idx, ParserGojieba, "兒童")) +} + +func TestGojiebaBooleanChinese(t *testing.T) { + requireJieba(t) + idx := zhCorpus(t, ParserGojieba) + require.ElementsMatch(t, []any{int64(0), int64(4)}, boolIDs(t, idx, ParserGojieba, "+中文 +學習")) + require.ElementsMatch(t, []any{int64(2)}, boolIDs(t, idx, ParserGojieba, "+兒童 -中文")) +} + +// --- json parser: values flattened to text, then indexed/queried as ngram --- + +func TestJSONParser(t *testing.T) { + docs := []Doc{ + {int64(0), []byte(`{"a":1, "b":"red apple"}`)}, + {int64(1), []byte(`{"a":2, "b":"中文學習教材"}`)}, + {int64(2), []byte(`{"a":3, "b":"red blue"}`)}, + } + seg, err := BuildSegmentFromDocsParser("js", int32(types.T_int64), docs, ParserJSON) + require.NoError(t, err) + idx := NewIndex([]*Segment{seg}, nil) + // Latin value token, boolean. + require.ElementsMatch(t, []any{int64(0), int64(2)}, boolIDs(t, idx, ParserJSON, "red")) + // CJK value: 中文學習 -> ngram bag-of-words, only doc 1. + require.ElementsMatch(t, []any{int64(1)}, nlIDs(t, idx, ParserJSON, "中文學習")) +} diff --git a/pkg/fulltext2/phrase_cursor_test.go b/pkg/fulltext2/phrase_cursor_test.go new file mode 100644 index 0000000000000..be12ef2f22bb9 --- /dev/null +++ b/pkg/fulltext2/phrase_cursor_test.go @@ -0,0 +1,90 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "fmt" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// TestPhraseCursorMatchesFallback proves the block-cursor phrase intersection +// (matchPhraseCursor) returns EXACTLY what the materialize fallback returns, across many +// blocks (> BlockSize docs, so the cursor crosses block boundaries, skips non-overlapping +// blocks, and decodes positions per block) — on both a build-side and a serialized/loaded +// segment. The fallback is the trusted oracle; any divergence is a cursor bug. +func TestPhraseCursorMatchesFallback(t *testing.T) { + phrases := []string{ + "中文學習", "學習教材", "遠東兒童", "兒童中文", "教學指引", + "中文學習教材", "生字卡片", "初學者適合", "短篇小說", "樂趣無窮", + } + // 500 docs (~4 blocks). Each doc concatenates a deterministic rotating subset of the + // phrases (some repeated → tf>1), so phrases appear in scattered, block-spanning docs. + docs := make([]Doc, 0, 500) + for i := 0; i < 500; i++ { + var b bytes.Buffer + for k := 0; k < 3; k++ { + b.WriteString(phrases[(i+k*3)%len(phrases)]) + b.WriteByte(' ') + } + if i%5 == 0 { // repeat one phrase → occurrence count > 1 + b.WriteString(phrases[i%len(phrases)]) + } + docs = append(docs, Doc{int64(i + 1), b.Bytes()}) + } + + seg, err := BuildSegmentFromDocsParser("cur", int32(types.T_int64), docs, ParserNgram) + require.NoError(t, err) + blob, err := seg.Serialize() + require.NoError(t, err) + loaded, err := Deserialize("cur", bytes.NewReader(blob)) + require.NoError(t, err) + t.Cleanup(func() { _ = loaded.dict.Close() }) + + // Multi-trigram queries (each decomposes to >= 2 exact slots → the cursor path). + queries := []string{ + "中文學習", "學習教材", "中文學習教材", "遠東兒童中文", "教學指引", + "初學者適合", "短篇小說樂趣", "生字卡片", "兒童中文學習", "不存在的詞語", + } + key := func(h []docTf) []string { + out := make([]string, len(h)) + for i, d := range h { + out[i] = fmt.Sprintf("%d:%d", d.ord, d.tf) + } + return out + } + + for _, name := range []string{"build", "loaded"} { + sg := seg + if name == "loaded" { + sg = loaded + } + t.Run(name, func(t *testing.T) { + for _, q := range queries { + slots, err := phraseSlots(q, ParserNgram) + require.NoError(t, err) + if len(slots) < 2 { + continue // cursor path is multi-slot only + } + cur := sg.matchPhraseCursor(slots) + fb := sg.matchPhraseFallback(slots) + require.ElementsMatch(t, key(fb), key(cur), "query %q (ord:tf must match the oracle)", q) + } + }) + } +} diff --git a/pkg/fulltext2/phrase_exact_test.go b/pkg/fulltext2/phrase_exact_test.go new file mode 100644 index 0000000000000..db5e5d2d30733 --- /dev/null +++ b/pkg/fulltext2/phrase_exact_test.go @@ -0,0 +1,96 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// TestExactPhraseSemantics pins fulltext2's NL and boolean-phrase EXACT-MATCH contract — +// the regression that previously escaped every unit test because the old tests used only +// English (where the ngram tokenizer degenerates to whole words, so a CJK-specific bug is +// invisible). It exercises the production query path (SearchQuery) so it covers the query +// tokenization + byte-position positional phrase matcher end to end. +// +// - NL multi-term is an EXACT ORDERED phrase: a query matches a doc ONLY where the +// terms appear in order and adjacent — NOT reversed, NOT with a word between. The +// old CJK "bag-of-words" path matched all three (a false positive on reversed / +// non-adjacent docs); a naive raw-ngram phrase matched none of the substring cases. +// - A query that is a contiguous SUBSTRING of a longer doc matches (中文學習 ⊂ +// 中文學習教材) — the case the raw-ngram phrase missed because the query's trailing +// partial n-grams are absent from the longer doc's FST. +// - Byte positions are consistent across the build-side AND the serialized/loaded +// segment (so a query works the same after a restart/merge). +func TestExactPhraseSemantics(t *testing.T) { + docs := []Doc{ + {int64(1), []byte("全文 索引")}, // in order, adjacent + {int64(2), []byte("索引 全文")}, // reversed + {int64(3), []byte("全文 系统 索引")}, // in order but NOT adjacent + {int64(4), []byte("中文學習教材")}, // superset of 中文學習 + {int64(5), []byte("quick brown fox")}, // English, in order + {int64(6), []byte("brown quick")}, // English, reversed + {int64(7), []byte("中文學校")}, // shares trigram 中文學 with 中文學習, NOT a superset + {int64(8), []byte("語文學習")}, // shares trigram 文學習 with 中文學習, NOT a superset + } + + run := func(t *testing.T, idx *Index) { + nl := func(q string) []any { + rs, err := idx.SearchQuery([]byte(q), false, ParserNgram, BM25, 100, nil) + require.NoError(t, err) + return resultIDs(rs) + } + bl := func(q string) []any { + rs, err := idx.SearchQuery([]byte(q), true, ParserNgram, BM25, 100, nil) + require.NoError(t, err) + return resultIDs(rs) + } + + // NL exact ordered phrase — matches ONLY the in-order-adjacent doc. + require.ElementsMatch(t, []any{int64(1)}, nl("全文 索引"), "CJK NL: exact ordered phrase only") + require.ElementsMatch(t, []any{int64(5)}, nl("quick brown"), "English NL: order-sensitive") + require.ElementsMatch(t, []any{int64(6)}, nl("brown quick"), "English NL: reversed is a different phrase") + require.Empty(t, nl("索引 系统"), "not adjacent in doc 3 in this order") + + // Contiguous substring of a longer doc matches — but NOT docs that merely share + // a trigram (中文學校 shares 中文學, 語文學習 shares 文學習): there is NO bag-of-words. + require.ElementsMatch(t, []any{int64(4)}, nl("中文學習"), "CJK substring, not trigram-overlap docs") + require.ElementsMatch(t, []any{int64(4), int64(7)}, nl("中文學"), "中文學 is a substring of docs 4 and 7") + require.ElementsMatch(t, []any{int64(4), int64(7)}, nl("中文")) + + // Boolean: a SINGLE bare CJK compound operand is an EXACT phrase, NOT an OR of its + // ngrams — so +中文學習 matches only the superset doc, never the trigram-overlap docs. + require.ElementsMatch(t, []any{int64(4)}, bl("+中文學習"), "boolean bare CJK operand: exact phrase, not bag") + require.ElementsMatch(t, []any{int64(4)}, bl("中文學習"), "boolean bare CJK operand (SHOULD): exact phrase, not bag") + // Boolean explicit "phrase" is exact-ordered; distinct bare operands OR together. + require.ElementsMatch(t, []any{int64(1)}, bl(`"全文 索引"`), "boolean explicit phrase: exact") + require.ElementsMatch(t, []any{int64(1), int64(2), int64(3)}, bl("全文 索引"), "boolean two bare operands: OR") + } + + seg, err := BuildSegmentFromDocsParser("ex", int32(types.T_int64), docs, ParserNgram) + require.NoError(t, err) + t.Run("build-side", func(t *testing.T) { run(t, NewIndex([]*Segment{seg}, nil)) }) + + // Serialize → deserialize, so the loaded (mmap posRaw) byte positions are exercised too. + blob, err := seg.Serialize() + require.NoError(t, err) + loaded, err := Deserialize("ex", bytes.NewReader(blob)) + require.NoError(t, err) + t.Cleanup(func() { _ = loaded.dict.Close() }) + t.Run("loaded-side", func(t *testing.T) { run(t, NewIndex([]*Segment{loaded}, nil)) }) +} diff --git a/pkg/fulltext2/phrase_perf_bench_test.go b/pkg/fulltext2/phrase_perf_bench_test.go new file mode 100644 index 0000000000000..1bf84386a95a9 --- /dev/null +++ b/pkg/fulltext2/phrase_perf_bench_test.go @@ -0,0 +1,124 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Perf follow-up Phase 0 — BASELINE measurement (no engine change) for the two phrase +// memory concerns: +// +// A. pooled phrase-buffer position retention / per-block make() churn +// (segment.go fillBlockPositions), surfaced under concurrent phrase load. +// B. no-LIMIT phrase materializing globalN results (stream.go StreamQuery falls back +// to SearchQuery(globalN)) vs the disjunctive path that streams heap-free. +package fulltext2 + +import ( + "bytes" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/vectorindex" +) + +// benchPhraseIndex builds a LOADED (serialize→deserialize, so the phrase cursor takes +// the posRaw/fillBlockPositions decode path, not the build-side flat view) single-segment +// index of nDocs docs, each holding `repeats` copies of the phrase "alpha beta" — so every +// term has `repeats` positions per doc (drives the per-doc position arrays) and the phrase +// matches every doc. +func benchPhraseIndex(b *testing.B, nDocs, repeats int) *Index { + b.Helper() + bb := NewBuilder("bench", int32(types.T_int64)) + for d := 0; d < nDocs; d++ { + pos := int32(0) + pk := int64(d) + for r := 0; r < repeats; r++ { + if err := bb.Add("alpha", pos, pk); err != nil { + b.Fatal(err) + } + pos += 6 + if err := bb.Add("beta", pos, pk); err != nil { + b.Fatal(err) + } + pos += 5 + } + } + seg, err := bb.Finish() + if err != nil { + b.Fatal(err) + } + blob, err := seg.Serialize() + if err != nil { + b.Fatal(err) + } + loaded, err := Deserialize(seg.Id, bytes.NewReader(blob)) + if err != nil { + b.Fatal(err) + } + return NewIndex([]*Segment{loaded}, nil) +} + +// BenchmarkPhraseSearchConcurrent — concern A. Repeated NL-phrase top-k under parallel +// load; the alloc/op figure captures the phrase-cursor position churn + pooled-buffer +// behavior. High `repeats` ⇒ long per-doc position lists. +func BenchmarkPhraseSearchConcurrent(b *testing.B) { + idx := benchPhraseIndex(b, 2000, 60) + pat := []byte("alpha beta") + b.ReportAllocs() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + res, err := idx.SearchQuery(pat, false /*NL phrase*/, ParserDefault, BM25, 10, nil) + if err != nil { + b.Fatal(err) + } + _ = res + } + }) +} + +// BenchmarkPhraseStreamNoLimit — concern B, the materializing fallback: a no-LIMIT NL +// phrase that matches ALL docs, so StreamQuery goes through SearchQuery(globalN). +func BenchmarkPhraseStreamNoLimit(b *testing.B) { + idx := benchPhraseIndex(b, 20000, 3) + pat := []byte("alpha beta") + emit := func(o *vectorindex.SearchOutput) error { + PutColumnBuffer(o.Keys) // recycle like the real consumer + return nil + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err := idx.StreamQuery(pat, false /*NL phrase → materializing fallback*/, ParserDefault, BM25, nil, false, emit); err != nil { + b.Fatal(err) + } + } +} + +// BenchmarkDisjunctionStreamNoLimit — concern B CONTROL: the SAME pattern + corpus as a +// boolean OR (disjunctive), which streams heap-free (streamWAND, no globalN slice). The +// gap between this and BenchmarkPhraseStreamNoLimit is the materialization cost Phase 2 +// targets. +func BenchmarkDisjunctionStreamNoLimit(b *testing.B) { + idx := benchPhraseIndex(b, 20000, 3) + pat := []byte("alpha beta") + emit := func(o *vectorindex.SearchOutput) error { + PutColumnBuffer(o.Keys) + return nil + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err := idx.StreamQuery(pat, true /*boolean OR → heap-free stream*/, ParserDefault, BM25, nil, false, emit); err != nil { + b.Fatal(err) + } + } +} diff --git a/pkg/fulltext2/pkcodec_review_test.go b/pkg/fulltext2/pkcodec_review_test.go new file mode 100644 index 0000000000000..0db316c8cf74f --- /dev/null +++ b/pkg/fulltext2/pkcodec_review_test.go @@ -0,0 +1,84 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "encoding/binary" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// TestBitPkRoundTrip pins the self-review blocker: a BIT primary key is accepted by +// build_ddl but the segment pk codec had no T_bit case, so encodePk returned +// "unsupported pk type" and stalled fulltext2 CDC on the first flush. BIT is backed by +// uint64, so it must round-trip through encode -> Serialize -> Deserialize -> pk(ord) +// -> query exactly like T_uint64. +func TestBitPkRoundTrip(t *testing.T) { + // encode/decode round-trip for the raw codec. + for _, v := range []uint64{0, 1, 255, 1 << 40, 1<<64 - 1} { + enc, err := encodePk(int32(types.T_bit), v) + require.NoError(t, err, "encodePk T_bit must not error (was the CDC-stall bug)") + dec, err := decodePk(int32(types.T_bit), enc) + require.NoError(t, err) + require.Equal(t, v, dec, "T_bit pk must round-trip") + } + + // full segment round-trip: build with BIT pks, serialize, reload, query back. + b := NewBuilder("bit", int32(types.T_bit)) + feed(t, b, uint64(10), "alpha", "beta") + feed(t, b, uint64(20), "alpha") + seg, err := b.Finish() + require.NoError(t, err) + + blob, err := seg.Serialize() + require.NoError(t, err) + loaded, err := Deserialize("bit", bytes.NewReader(blob)) + require.NoError(t, err) + + idx := NewIndex([]*Segment{loaded}, nil) + require.Equal(t, int64(2), idx.globalN) + res := idx.SearchPhrase(phr("alpha"), BM25, 10, nil) + require.Len(t, res, 2, "both BIT-pk docs must be queryable after reload") + pks := map[uint64]bool{} + for _, r := range res { + pks[r.Pk.(uint64)] = true + } + require.True(t, pks[10] && pks[20], "both BIT pk values must survive the round-trip") +} + +// TestDecodeDocmapRejectsShortFixedPk pins the corrupt-blob guard: a fixed-width pk whose +// stored length prefix is smaller than the type width must be rejected at load (clean +// error) rather than passing decodeDocmap and later panicking in pk(ord) -> +// LittleEndian.Uint64 on a short slice. +func TestDecodeDocmapRejectsShortFixedPk(t *testing.T) { + // Hand-craft a minimal docmap: [pkType u32][n u64][ per pk: len u32 + bytes ][ docLen n*u32 ]. + // One doc, T_int64 pk, but the stored pk length is 4 (should be 8) -> corrupt. + var buf bytes.Buffer + le := func(v uint32) { _ = binary.Write(&buf, binary.LittleEndian, v) } + le64 := func(v uint64) { _ = binary.Write(&buf, binary.LittleEndian, v) } + le(uint32(types.T_int64)) // pkType + le64(1) // n = 1 doc + le(4) // pk length prefix = 4 (WRONG: T_int64 needs 8) + buf.Write([]byte{1, 2, 3, 4}) + le(7) // docLen[0] + + s := &Segment{} + err := s.decodeDocmap(buf.Bytes()) + require.Error(t, err, "a fixed-width pk with a short stored length must be rejected at load") + require.Contains(t, err.Error(), "width", "error should name the width mismatch") +} diff --git a/pkg/fulltext2/pkcodec_test.go b/pkg/fulltext2/pkcodec_test.go new file mode 100644 index 0000000000000..f05b05e59843e --- /dev/null +++ b/pkg/fulltext2/pkcodec_test.go @@ -0,0 +1,74 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// TestPkCodecAllTypesRoundTrip is the single-source-of-truth guardrail: EVERY supported +// pk type must survive encodePk → decodePk unchanged. A pk type added to one codec switch +// but forgotten in another (the class of bug that once stalled CDC on a BIT pk) fails here +// instead of in production. Add every new pk type to this table. +func TestPkCodecAllTypesRoundTrip(t *testing.T) { + u1, _ := types.ParseUuid("12345678-1234-1234-1234-1234567890ab") + cases := []struct { + typ types.T + val any + }{ + {types.T_int64, int64(-1 << 50)}, + {types.T_uint64, uint64(1<<64 - 1)}, + {types.T_bit, uint64(255)}, + {types.T_int32, int32(-123456)}, + {types.T_uint32, uint32(4000000000)}, + {types.T_int16, int16(-30000)}, + {types.T_uint16, uint16(60000)}, + {types.T_int8, int8(-128)}, + {types.T_uint8, uint8(200)}, + {types.T_date, types.Date(19000)}, + {types.T_datetime, types.Datetime(-42)}, + {types.T_time, types.Time(987654321)}, + {types.T_timestamp, types.Timestamp(1234567890)}, + {types.T_decimal64, types.Decimal64(999999)}, + {types.T_decimal128, types.Decimal128{B0_63: 7, B64_127: 11}}, + {types.T_varchar, []byte("hello pk")}, + {types.T_char, []byte("c")}, + {types.T_text, []byte("")}, + {types.T_blob, []byte{0, 1, 2, 255}}, + {types.T_json, []byte(`{"k":1}`)}, + {types.T_uuid, u1}, + } + for _, c := range cases { + b, err := encodePk(int32(c.typ), c.val) + require.NoErrorf(t, err, "encodePk %v", c.typ) + got, err := decodePk(int32(c.typ), b) + require.NoErrorf(t, err, "decodePk %v", c.typ) + // byte-slice pks: compare as strings so an empty pk's nil-vs-[]byte{} (which + // are semantically identical, and identical once appended to a vector) matches. + if wantB, ok := c.val.([]byte); ok { + require.Equalf(t, string(wantB), string(got.([]byte)), "round-trip %v", c.typ) + } else { + require.Equalf(t, c.val, got, "round-trip %v", c.typ) + } + + // fixedPkByteWidth must agree with the encoded length for fixed types. + if w, fixed := fixedPkByteWidth(int32(c.typ)); fixed { + require.Equalf(t, w, len(b), "fixed width mismatch for %v", c.typ) + } + } +} diff --git a/pkg/fulltext2/plugin/compile/compile_test.go b/pkg/fulltext2/plugin/compile/compile_test.go new file mode 100644 index 0000000000000..5907d3ce775bf --- /dev/null +++ b/pkg/fulltext2/plugin/compile/compile_test.go @@ -0,0 +1,115 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package compile + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/catalog" + compileplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/compile" + "github.com/stretchr/testify/require" +) + +// TestValidateReindexParams covers the fulltext2 ALTER REINDEX param merge — in +// particular that max_postings_capacity is APPLIED (not the silent no-op it was +// before), so `ALTER … REINDEX … max_postings_capacity=N` actually re-bounds the +// rebuild. +func TestValidateReindexParams(t *testing.T) { + up := func(m map[string]string) compileplugin.ReindexParamUpdate { + return compileplugin.ReindexParamUpdate{Params: m} + } + + // No honored key → old returned unchanged (a bare reindex / MERGE). + old := map[string]string{"parser": "ngram", catalog.IndexAlgoParamMaxIndexCapacity: "1000000"} + got, err := Hooks{}.ValidateReindexParams(old, up(nil)) + require.NoError(t, err) + require.Equal(t, old, got) + + // max_postings_capacity present → merged into the rebuild's algo_params. + got, err = Hooks{}.ValidateReindexParams(old, up(map[string]string{ + catalog.IndexAlgoParamMaxPostingsCapacity: "4000000", + })) + require.NoError(t, err) + require.Equal(t, "4000000", got[catalog.IndexAlgoParamMaxPostingsCapacity]) + require.Equal(t, "ngram", got["parser"]) // untouched + require.Equal(t, "1000000", got[catalog.IndexAlgoParamMaxIndexCapacity]) // untouched + require.NotContains(t, old, catalog.IndexAlgoParamMaxPostingsCapacity) // old not mutated + + // position_free + max_postings_capacity together (gojieba): both applied. + oldG := map[string]string{"parser": "gojieba"} + got, err = Hooks{}.ValidateReindexParams(oldG, up(map[string]string{ + catalog.IndexAlgoParamPositionFree: "true", + catalog.IndexAlgoParamMaxPostingsCapacity: "2000000", + })) + require.NoError(t, err) + require.Equal(t, "true", got[catalog.IndexAlgoParamPositionFree]) + require.Equal(t, "2000000", got[catalog.IndexAlgoParamMaxPostingsCapacity]) + + // position_free=true on a non-gojieba parser is still rejected, even when a + // posting-capacity override rides along. + _, err = Hooks{}.ValidateReindexParams(old, up(map[string]string{ + catalog.IndexAlgoParamPositionFree: "true", + catalog.IndexAlgoParamMaxPostingsCapacity: "2000000", + })) + require.Error(t, err) + + // max_index_capacity alone is NOT honored on a reindex (pre-existing fulltext2 + // limitation): old returned unchanged. + got, err = Hooks{}.ValidateReindexParams(old, up(map[string]string{ + catalog.IndexAlgoParamMaxIndexCapacity: "5", + })) + require.NoError(t, err) + require.Equal(t, old, got) +} + +// TestValidateReindexParamsMergePositionFree covers the data-loss guard: a MERGE compacts the tail +// into the EXISTING base in place and cannot change the positional format (turning POSITION_FREE +// OFF has no positions to re-derive; CompactSegments would commit an empty base). So a +// `MERGE POSITION_FREE=…` that flips the format is rejected BEFORE the params are persisted, while +// a REBUILD (Merge=false) with the same change is allowed and a format-preserving MERGE passes. +func TestValidateReindexParamsMergePositionFree(t *testing.T) { + upM := func(m map[string]string, merge bool) compileplugin.ReindexParamUpdate { + return compileplugin.ReindexParamUpdate{Params: m, Merge: merge} + } + posFree := map[string]string{"parser": "gojieba", catalog.IndexAlgoParamPositionFree: "true"} + positional := map[string]string{"parser": "gojieba"} // position_free absent ⇒ positional + + // MERGE turning position_free OFF (the reported data-loss case) → rejected. + _, err := Hooks{}.ValidateReindexParams(posFree, upM(map[string]string{ + catalog.IndexAlgoParamPositionFree: "false"}, true)) + require.ErrorContains(t, err, "REBUILD") + + // MERGE turning position_free ON (also a format change) → rejected. + _, err = Hooks{}.ValidateReindexParams(positional, upM(map[string]string{ + catalog.IndexAlgoParamPositionFree: "true"}, true)) + require.ErrorContains(t, err, "REBUILD") + + // REBUILD (Merge=false) with the same OFF change → allowed (re-derives from source). + got, err := Hooks{}.ValidateReindexParams(posFree, upM(map[string]string{ + catalog.IndexAlgoParamPositionFree: "false"}, false)) + require.NoError(t, err) + require.NotContains(t, got, catalog.IndexAlgoParamPositionFree) // false ⇒ positional (absent) + + // MERGE that does NOT change the format (re-asserts the current true) → allowed. + got, err = Hooks{}.ValidateReindexParams(posFree, upM(map[string]string{ + catalog.IndexAlgoParamPositionFree: "true"}, true)) + require.NoError(t, err) + require.Equal(t, "true", got[catalog.IndexAlgoParamPositionFree]) + + // bare MERGE (no position_free param) → allowed. + got, err = Hooks{}.ValidateReindexParams(posFree, upM(nil, true)) + require.NoError(t, err) + require.Equal(t, posFree, got) +} diff --git a/pkg/fulltext2/plugin/idxcron/idxcron_cover_test.go b/pkg/fulltext2/plugin/idxcron/idxcron_cover_test.go new file mode 100644 index 0000000000000..7696bb198cafc --- /dev/null +++ b/pkg/fulltext2/plugin/idxcron/idxcron_cover_test.go @@ -0,0 +1,134 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package idxcron + +import ( + "testing" + "time" + + "github.com/matrixorigin/matrixone/pkg/container/types" + idxcronplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/idxcron" + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/stretchr/testify/require" +) + +// TestReindexOptionForCounts exercises the pure MERGE-vs-REBUILD decision on both +// sides of the RebuildDeadPct (30%) boundary. dead% = 100 - liveRows*100/baseDocs. +func TestReindexOptionForCounts(t *testing.T) { + const merge = "MERGE" + const rebuild = "" // full REBUILD + + cases := []struct { + name string + liveRows int64 + baseDocs int64 + want string + }{ + // baseDocs guard branches. + {"zero base", 0, 0, merge}, + {"negative base", 100, -1, merge}, + + // dead% low → MERGE. All rows live = 0% dead. + {"no dead docs", 100, 100, merge}, + // 20% dead (< 30) → MERGE. + {"20pct dead", 80, 100, merge}, + // Exactly at boundary: dead = 30% → liveRows*100 == baseDocs*70, NOT strictly + // less-than, so still MERGE (boundary is inclusive of MERGE). + {"exactly 30pct dead", 70, 100, merge}, + + // dead% high → REBUILD. 31% dead → 69*100 < 100*70 → REBUILD. + {"31pct dead", 69, 100, rebuild}, + {"all dead", 0, 100, rebuild}, + {"half dead", 50, 100, rebuild}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := reindexOptionForCounts(tc.liveRows, tc.baseDocs) + require.Equal(t, tc.want, got, "liveRows=%d baseDocs=%d", tc.liveRows, tc.baseDocs) + }) + } +} + +// TestOptLabel covers the human-readable label mapping used in logging. +func TestOptLabel(t *testing.T) { + require.Equal(t, "REBUILD", optLabel("")) + require.Equal(t, "MERGE", optLabel("MERGE")) +} + +// TestUpdatableWithinInterval covers the cadence short-circuit in Updatable: when +// LastUpdateAt + Interval is still in the future, Updatable returns (false, reason) +// BEFORE touching the SqlProcess, so it is reachable with plain inputs. +func TestUpdatableWithinInterval(t *testing.T) { + // LastUpdateAt = now, Interval = 1h → last+interval is in the future → not + // updatable, no engine needed. + now := types.UnixToTimestamp(time.Now().Unix()) + in := idxcronplugin.UpdatableInput{ + IndexName: "idx1", + TableDef: &plan.TableDef{DbName: "db", Name: "t"}, + LastUpdateAt: &now, + Interval: time.Hour, + } + ok, reason, err := Hooks{}.Updatable(in) + require.NoError(t, err) + require.False(t, ok) + require.Contains(t, reason, "within reindex interval") +} + +// TestUpdatableNoStorageTable covers the branch where the interval gate has passed +// (LastUpdateAt in the distant past) but the TableDef has no matching fulltext2 +// storage index, so Updatable bails out before any SQL against the SqlProcess. +func TestUpdatableNoStorageTable(t *testing.T) { + // LastUpdateAt far in the past so last+interval is already elapsed. + past := types.UnixToTimestamp(time.Now().Add(-100 * time.Hour).Unix()) + in := idxcronplugin.UpdatableInput{ + IndexName: "idx_missing", + TableDef: &plan.TableDef{ + DbName: "db", + Name: "t", + Indexes: []*plan.IndexDef{}, // no storage index for idx_missing + }, + LastUpdateAt: &past, + Interval: time.Hour, + } + ok, reason, err := Hooks{}.Updatable(in) + require.NoError(t, err) + require.False(t, ok) + require.Contains(t, reason, "not a fulltext2 index") +} + +// TestReindexOptionNoMetaTable covers the ReindexOption fast-path: when the +// TableDef has no matching metadata index, it returns "MERGE" without consulting +// the SqlProcess. +func TestReindexOptionNoMetaTable(t *testing.T) { + in := idxcronplugin.UpdatableInput{ + IndexName: "idx_missing", + TableDef: &plan.TableDef{ + DbName: "db", + Name: "t", + Indexes: []*plan.IndexDef{}, // no metadata index + }, + } + opt, err := Hooks{}.ReindexOption(in) + require.NoError(t, err) + require.Equal(t, "MERGE", opt) +} + +// TestTailChunkThreshold sanity-checks the default threshold constant is positive +// (the env override path is exercised at package-init time). +func TestTailChunkThreshold(t *testing.T) { + require.Greater(t, TailChunkThreshold, int64(0)) + require.Equal(t, 30, RebuildDeadPct) +} diff --git a/pkg/fulltext2/plugin/iscp/iscp_cover_test.go b/pkg/fulltext2/plugin/iscp/iscp_cover_test.go new file mode 100644 index 0000000000000..60daac99480c8 --- /dev/null +++ b/pkg/fulltext2/plugin/iscp/iscp_cover_test.go @@ -0,0 +1,80 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iscp + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/catalog" + iscppkg "github.com/matrixorigin/matrixone/pkg/iscp" + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/stretchr/testify/require" +) + +// TestHooksRegistered confirms init() registered fulltext2's ISCP Hooks with +// the iscp framework and that the value satisfies the iscp.Hooks interface +// (mirrors the `var _ iscppkg.Hooks = Hooks{}` assertion in iscp.go). +func TestHooksRegistered(t *testing.T) { + var _ iscppkg.Hooks = Hooks{} + + h, ok := iscppkg.GetHooks(catalog.MoIndexFullText2Algo.ToString()) + require.True(t, ok) + require.NotNil(t, h) + require.True(t, iscppkg.HasHooks(catalog.MoIndexFullText2Algo.ToString())) +} + +// TestNewSqlWriterErrors drives Hooks.NewSqlWriter through the two error guards +// reachable with plain inputs (no live txn/engine required): +// - the fulltext2 storage/metadata hidden tables are missing, and +// - the index has no source column (empty Parts). +// +// The success path and the Run hook need a live CDC consumer pipeline / typed +// tabledef pk resolution, so they are exercised by the fulltext2 BVT cases, not +// here. +func TestNewSqlWriterErrors(t *testing.T) { + // Missing storage/metadata hidden tables → guarded error before any tabledef + // dereference, so a nil tabledef is safe here. + _, err := Hooks{}.NewSqlWriter( + iscppkg.JobID{}, + &iscppkg.ConsumerInfo{DBName: "db"}, + nil, + []*plan.IndexDef{{ + IndexTableName: "not_a_hidden_table", + IndexAlgoTableType: "other", + Parts: []string{"body"}, + }}, + ) + require.Error(t, err) + + // Storage + metadata present but the index has no source column (empty Parts) + // → distinct guarded error, again before any tabledef dereference. + _, err = Hooks{}.NewSqlWriter( + iscppkg.JobID{}, + &iscppkg.ConsumerInfo{DBName: "db"}, + nil, + []*plan.IndexDef{ + { + IndexTableName: "storage_tbl", + IndexAlgoTableType: catalog.FullText2Index_TblType_Storage, + Parts: nil, + }, + { + IndexTableName: "meta_tbl", + IndexAlgoTableType: catalog.FullText2Index_TblType_Metadata, + }, + }, + ) + require.Error(t, err) +} diff --git a/pkg/fulltext2/plugin/plan/plan_cover_test.go b/pkg/fulltext2/plugin/plan/plan_cover_test.go new file mode 100644 index 0000000000000..3b394adda1630 --- /dev/null +++ b/pkg/fulltext2/plugin/plan/plan_cover_test.go @@ -0,0 +1,548 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// CPU-only unit coverage for the fulltext2 plan-layer hooks. plan.go's inert +// vector-sort redirects (CanApply / ApplyForSort), schema.go's hidden-table +// builder (BuildFullTextIndexDefs) + buildFullText2Params, and tablefunc.go's +// pure TVF builders (buildFullText2Create / buildFullText2Search / +// getFullText2Params) are all reachable without a live cluster. The +// happy-path builders call two planplugin helper vars (production wires them +// in pkg/sql/plan's init) which the init() below stubs to avoid an import +// cycle. +package plan + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/container/types" + planplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/plan" + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" +) + +func init() { + if planplugin.MakeHiddenColDefByName == nil { + planplugin.MakeHiddenColDefByName = func(name string) *plan.ColDef { + return &plan.ColDef{Name: name, Typ: plan.Type{Id: int32(types.T_varchar)}} + } + } + if planplugin.DeepCopyColDefList == nil { + planplugin.DeepCopyColDefList = func(in []*plan.ColDef) []*plan.ColDef { return in } + } + if planplugin.ValidateIncludeColumns == nil { + // Minimal faithful stub of the production validator (pkg/sql/plan build_ddl.go): + // reject an INCLUDE column whose type is not in supportedTypes. Enough to exercise + // BuildFullTextIndexDefs' include wiring without the pkg/sql/plan import cycle. + planplugin.ValidateIncludeColumns = func(ctx planplugin.CompilerContext, includeCols []*tree.UnresolvedName, + colMap map[string]*plan.ColDef, _, _ string, supportedTypes []types.T) error { + for _, uc := range includeCols { + col, ok := colMap[uc.ColName()] + if !ok { + return moerr.NewInvalidInputNoCtxf("INCLUDE column '%s' is not exist", uc.ColName()) + } + ok = false + for _, st := range supportedTypes { + if types.T(col.Typ.Id) == st { + ok = true + break + } + } + if !ok { + return moerr.NewNotSupportedNoCtxf("INCLUDE column '%s' has unsupported type", uc.ColName()) + } + } + return nil + } + } +} + +// ftIndexInclude builds a *tree.FullTextIndex over one indexed col plus an INCLUDE list. +func ftIndexInclude(name, indexedCol string, includeCols ...string) *tree.FullTextIndex { + idx := ftIndex(name, indexedCol) + inc := make([]*tree.UnresolvedName, 0, len(includeCols)) + for _, c := range includeCols { + inc = append(inc, tree.NewUnresolvedName(tree.NewCStr(c, 0))) + } + idx.IndexOption = &tree.IndexOption{IncludeColumns: inc} + return idx +} + +// --- stubs ----------------------------------------------------------------- + +// stubCompilerContext is the minimal planplugin.CompilerContext (GetContext + +// ResolveVariable). +type stubCompilerContext struct{ ctx context.Context } + +func (c stubCompilerContext) GetContext() context.Context { return c.ctx } +func (c stubCompilerContext) ResolveVariable(string, bool, bool) (interface{}, error) { + return nil, nil +} + +var _ planplugin.CompilerContext = stubCompilerContext{} + +func newStubCompilerContext() stubCompilerContext { + return stubCompilerContext{ctx: context.Background()} +} + +// stubPlanBuilder is the minimal planplugin.PlanBuilder. The fulltext2 +// tablefunc builders only use GenNewBindTag / AppendNode / GetContext; the +// vector-sort redirect methods are never reached from these tests, so they +// panic. +type stubPlanBuilder struct { + ctx context.Context + nodes []*plan.Node + nextTag int32 +} + +func newStubPlanBuilder() *stubPlanBuilder { + return &stubPlanBuilder{ctx: context.Background()} +} + +func (b *stubPlanBuilder) GetContext() context.Context { return b.ctx } + +func (b *stubPlanBuilder) GenNewBindTag() int32 { + b.nextTag++ + return b.nextTag +} + +func (b *stubPlanBuilder) AppendNode(node *plan.Node, _ planplugin.BindContext) int32 { + id := int32(len(b.nodes)) + b.nodes = append(b.nodes, node) + return id +} + +func (b *stubPlanBuilder) ApplyIndicesForSortUsingHnsw(_ *planplugin.VectorSortContext, _ *planplugin.MultiTableIndexRef, _ int32, _ planplugin.ApplyForSortOpts) (int32, bool, error) { + panic("not used") +} +func (b *stubPlanBuilder) ApplyIndicesForSortUsingCagra(_ *planplugin.VectorSortContext, _ *planplugin.MultiTableIndexRef, _ int32, _ planplugin.ApplyForSortOpts) (int32, bool, error) { + panic("not used") +} +func (b *stubPlanBuilder) ApplyIndicesForSortUsingIvfpq(_ *planplugin.VectorSortContext, _ *planplugin.MultiTableIndexRef, _ int32, _ planplugin.ApplyForSortOpts) (int32, bool, error) { + panic("not used") +} +func (b *stubPlanBuilder) ApplyIndicesForSortUsingIvfflat(_ *planplugin.VectorSortContext, _ *planplugin.MultiTableIndexRef, _ int32, _ planplugin.ApplyForSortOpts) (int32, bool, error) { + panic("not used") +} +func (b *stubPlanBuilder) CanApplyHnsw(_ *planplugin.VectorSortContext, _ *planplugin.MultiTableIndexRef) (bool, error) { + panic("not used") +} +func (b *stubPlanBuilder) CanApplyCagra(_ *planplugin.VectorSortContext, _ *planplugin.MultiTableIndexRef) (bool, error) { + panic("not used") +} +func (b *stubPlanBuilder) CanApplyIvfpq(_ *planplugin.VectorSortContext, _ *planplugin.MultiTableIndexRef) (bool, error) { + panic("not used") +} +func (b *stubPlanBuilder) CanApplyIvfflat(_ *planplugin.VectorSortContext, _ *planplugin.MultiTableIndexRef) (bool, error) { + panic("not used") +} + +var _ planplugin.PlanBuilder = (*stubPlanBuilder)(nil) + +// --- builders for AST inputs ---------------------------------------------- + +// ftIndex builds a *tree.FullTextIndex over the given column names (single or +// multi-column) with no IndexOption. +func ftIndex(name string, cols ...string) *tree.FullTextIndex { + kp := make([]*tree.KeyPart, 0, len(cols)) + for _, c := range cols { + kp = append(kp, &tree.KeyPart{ColName: tree.NewUnresolvedName(tree.NewCStr(c, 0))}) + } + return &tree.FullTextIndex{Name: name, KeyParts: kp} +} + +// textColMap returns a colMap with a varchar pk and a text-typed indexed col. +func textColMap(pkName string, cols ...string) map[string]*plan.ColDef { + m := map[string]*plan.ColDef{ + pkName: {Name: pkName, Typ: plan.Type{Id: int32(types.T_varchar)}}, + } + for _, c := range cols { + m[c] = &plan.ColDef{Name: c, Typ: plan.Type{Id: int32(types.T_text)}} + } + return m +} + +func numValTblFunc(s string) *tree.TableFunction { + nv := tree.NewNumVal[string](s, s, false, tree.P_char) + return &tree.TableFunction{Func: &tree.FuncExpr{Exprs: tree.Exprs{nv}}} +} + +func nonNumValTblFunc() *tree.TableFunction { + un := tree.NewUnresolvedName(tree.NewCStr("col", 0)) + return &tree.TableFunction{Func: &tree.FuncExpr{Exprs: tree.Exprs{un}}} +} + +// makeArgs builds n plan.Expr args: [param(varchar), ints...]. +func makeArgs(n int) []*plan.Expr { + out := make([]*plan.Expr, 0, n) + out = append(out, &plan.Expr{ + Typ: plan.Type{Id: int32(types.T_varchar)}, + Expr: &plan.Expr_Lit{Lit: &plan.Literal{Value: &plan.Literal_Sval{Sval: "{}"}}}, + }) + for i := 1; i < n; i++ { + out = append(out, &plan.Expr{ + Typ: plan.Type{Id: int32(types.T_int64)}, + Expr: &plan.Expr_Lit{Lit: &plan.Literal{Value: &plan.Literal_I64Val{I64Val: int64(i)}}}, + }) + } + return out +} + +// --- plan.go: inert vector-sort hooks -------------------------------------- + +func TestCanApply_AlwaysFalse(t *testing.T) { + ok, err := Hooks{}.CanApply(newStubPlanBuilder(), &planplugin.VectorSortContext{}, &planplugin.MultiTableIndexRef{}) + require.NoError(t, err) + require.False(t, ok) +} + +func TestApplyForSort_Inert(t *testing.T) { + id, applied, err := Hooks{}.ApplyForSort(newStubPlanBuilder(), &planplugin.VectorSortContext{}, &planplugin.MultiTableIndexRef{}, 42, planplugin.ApplyForSortOpts{}) + require.NoError(t, err) + require.False(t, applied) + require.Equal(t, int32(42), id) +} + +// --- plan.go: BuildSecondaryIndexDefs (always errors) ---------------------- + +func TestBuildSecondaryIndexDefs_NotSupported(t *testing.T) { + idxDefs, tblDefs, err := Hooks{}.BuildSecondaryIndexDefs(newStubCompilerContext(), nil, nil, nil, "id") + require.Error(t, err) + require.Nil(t, idxDefs) + require.Nil(t, tblDefs) +} + +// --- schema.go: BuildFullTextIndexDefs error paths ------------------------- + +func TestBuildFullTextIndexDefs_EmptyPkey(t *testing.T) { + _, _, err := Hooks{}.BuildFullTextIndexDefs(newStubCompilerContext(), ftIndex("ft", "body"), textColMap("id", "body"), nil, "") + require.Error(t, err) +} + +func TestBuildFullTextIndexDefs_FakePkey(t *testing.T) { + _, _, err := Hooks{}.BuildFullTextIndexDefs(newStubCompilerContext(), ftIndex("ft", "body"), textColMap("id", "body"), nil, catalog.FakePrimaryKeyColName) + require.Error(t, err) +} + +func TestBuildFullTextIndexDefs_DuplicateColumn(t *testing.T) { + existed := []*plan.IndexDef{{ + IndexAlgo: tree.INDEX_TYPE_FULLTEXT2.ToString(), + Parts: []string{"body"}, + }} + _, _, err := Hooks{}.BuildFullTextIndexDefs(newStubCompilerContext(), ftIndex("ft", "body"), textColMap("id", "body"), existed, "id") + require.Error(t, err) +} + +func TestBuildFullTextIndexDefs_ColNotExist(t *testing.T) { + // indexed column missing from colMap. + _, _, err := Hooks{}.BuildFullTextIndexDefs(newStubCompilerContext(), ftIndex("ft", "nope"), textColMap("id", "body"), nil, "id") + require.Error(t, err) +} + +func TestBuildFullTextIndexDefs_UnsupportedColType(t *testing.T) { + m := textColMap("id", "body") + m["body"].Typ.Id = int32(types.T_int64) // fulltext2 only supports char/varchar/text/json/datalink + _, _, err := Hooks{}.BuildFullTextIndexDefs(newStubCompilerContext(), ftIndex("ft", "body"), m, nil, "id") + require.Error(t, err) +} + +// TestBuildFullTextIndexDefs_UnsupportedPkType: a FLOAT/DOUBLE primary key must be +// rejected at CREATE (the segment pk codec can't encode it) rather than accepted and +// failing later at build/CDC — the SupportedPrimaryKeyTypes/encodePk parity guard. +func TestBuildFullTextIndexDefs_UnsupportedPkType(t *testing.T) { + m := textColMap("id", "body") + m["id"].Typ.Id = int32(types.T_float64) // codec has no float case + _, _, err := Hooks{}.BuildFullTextIndexDefs(newStubCompilerContext(), ftIndex("ft", "body"), m, nil, "id") + require.ErrorContains(t, err, "primary key type") + + // a codec-supported pk (int64) clears the pk-type gate. + ok := textColMap("id", "body") + ok["id"].Typ.Id = int32(types.T_int64) + _, _, err = Hooks{}.BuildFullTextIndexDefs(newStubCompilerContext(), ftIndex("ft", "body"), ok, nil, "id") + require.NoError(t, err) +} + +func TestBuildFullTextIndexDefs_BadParser(t *testing.T) { + idx := ftIndex("ft", "body") + idx.IndexOption = &tree.IndexOption{ParserName: "bogus"} + _, _, err := Hooks{}.BuildFullTextIndexDefs(newStubCompilerContext(), idx, textColMap("id", "body"), nil, "id") + require.Error(t, err) +} + +func TestBuildFullTextIndexDefs_PositionFreeNonGojieba(t *testing.T) { + idx := ftIndex("ft", "body") + idx.IndexOption = &tree.IndexOption{ParserName: "ngram", PositionFree: true} + _, _, err := Hooks{}.BuildFullTextIndexDefs(newStubCompilerContext(), idx, textColMap("id", "body"), nil, "id") + require.Error(t, err) +} + +// --- schema.go: BuildFullTextIndexDefs happy path -------------------------- + +func TestBuildFullTextIndexDefs_OK(t *testing.T) { + idxDefs, tblDefs, err := Hooks{}.BuildFullTextIndexDefs(newStubCompilerContext(), ftIndex("ft", "body"), textColMap("id", "body"), nil, "id") + require.NoError(t, err) + require.Len(t, idxDefs, 2) + require.Len(t, tblDefs, 2) + + // index defs: both stamped algo=fulltext2, told apart by IndexAlgoTableType. + algo := tree.INDEX_TYPE_FULLTEXT2.ToString() + require.Equal(t, algo, idxDefs[0].IndexAlgo) + require.Equal(t, algo, idxDefs[1].IndexAlgo) + require.Equal(t, catalog.FullText2Index_TblType_Storage, idxDefs[0].IndexAlgoTableType) + require.Equal(t, catalog.FullText2Index_TblType_Metadata, idxDefs[1].IndexAlgoTableType) + require.Equal(t, []string{"body"}, idxDefs[0].Parts) + require.NotEmpty(t, idxDefs[0].IndexAlgoParams) + + // storage table: 4 declared cols + hidden composite pk; metadata: 6 cols. + require.Equal(t, catalog.FullText2Index_TblType_Storage, tblDefs[0].TableType) + require.Equal(t, catalog.FullText2Index_TblType_Metadata, tblDefs[1].TableType) + require.Len(t, tblDefs[0].Cols, 5) + require.Len(t, tblDefs[1].Cols, 6) + require.NotNil(t, tblDefs[0].Pkey) + require.NotNil(t, tblDefs[1].Pkey) +} + +func TestBuildFullTextIndexDefs_MultiColumnOK(t *testing.T) { + idxDefs, _, err := Hooks{}.BuildFullTextIndexDefs(newStubCompilerContext(), ftIndex("ft", "title", "body"), textColMap("id", "title", "body"), nil, "id") + require.NoError(t, err) + require.Len(t, idxDefs, 2) + require.ElementsMatch(t, []string{"title", "body"}, idxDefs[0].Parts) +} + +func TestBuildFullTextIndexDefs_GojiebaPositionFreeOK(t *testing.T) { + idx := ftIndex("ft", "body") + idx.IndexOption = &tree.IndexOption{ParserName: "gojieba", PositionFree: true} + idxDefs, _, err := Hooks{}.BuildFullTextIndexDefs(newStubCompilerContext(), idx, textColMap("id", "body"), nil, "id") + require.NoError(t, err) + params, perr := catalog.IndexParamsStringToMap(idxDefs[0].IndexAlgoParams) + require.NoError(t, perr) + require.Equal(t, "gojieba", params["parser"]) + require.Equal(t, "true", params[catalog.IndexAlgoParamPositionFree]) +} + +// --- schema.go: INCLUDE columns -------------------------------------------- + +// TestBuildFullTextIndexDefs_IncludeOK: INCLUDE(status, prio) with supported types +// populates IndexDef.IncludedColumns on BOTH hidden-table defs and persists the names in +// algo_params (for reload). The two hidden tables are unchanged — include values live in +// the segment docmap, not a SQL column. +func TestBuildFullTextIndexDefs_IncludeOK(t *testing.T) { + m := textColMap("id", "body") + m["status"] = &plan.ColDef{Name: "status", Typ: plan.Type{Id: int32(types.T_varchar)}} + m["prio"] = &plan.ColDef{Name: "prio", Typ: plan.Type{Id: int32(types.T_int64)}} + + idxDefs, tblDefs, err := Hooks{}.BuildFullTextIndexDefs( + newStubCompilerContext(), ftIndexInclude("ft", "body", "status", "prio"), m, nil, "id") + require.NoError(t, err) + require.Len(t, idxDefs, 2) + + // IncludedColumns set on both defs (storage + metadata). + require.Equal(t, []string{"status", "prio"}, idxDefs[0].IncludedColumns) + require.Equal(t, []string{"status", "prio"}, idxDefs[1].IncludedColumns) + + // Persisted in algo_params under the shared catalog key, and round-trips. + params, perr := catalog.IndexParamsStringToMap(idxDefs[0].IndexAlgoParams) + require.NoError(t, perr) + names, cerr := catalog.ParseIncludeColumnsValue(params[catalog.IncludedColumns]) + require.NoError(t, cerr) + require.Equal(t, []string{"status", "prio"}, names) + + // Hidden tables are unchanged (no per-include SQL column). + require.Len(t, tblDefs[0].Cols, 5) + require.Len(t, tblDefs[1].Cols, 6) +} + +// TestBuildFullTextIndexDefs_IncludeUnsupportedType: an INCLUDE column of a type outside +// the plugin's SupportedIncludeColumnTypes() set is rejected at CREATE. +func TestBuildFullTextIndexDefs_IncludeUnsupportedType(t *testing.T) { + m := textColMap("id", "body") + m["amount"] = &plan.ColDef{Name: "amount", Typ: plan.Type{Id: int32(types.T_float64)}} // float not supported + _, _, err := Hooks{}.BuildFullTextIndexDefs( + newStubCompilerContext(), ftIndexInclude("ft", "body", "amount"), m, nil, "id") + require.Error(t, err) +} + +// TestBuildFullTextIndexDefs_IncludeIndexedColumn: the indexed text column itself cannot be +// an INCLUDE column (it is tokenized into the postings; storing the raw text again is +// redundant). Rejected before the type gate, so the reason is "indexed text column" rather +// than a type error. +func TestBuildFullTextIndexDefs_IncludeIndexedColumn(t *testing.T) { + _, _, err := Hooks{}.BuildFullTextIndexDefs( + newStubCompilerContext(), ftIndexInclude("ft", "body", "body"), textColMap("id", "body"), nil, "id") + require.Error(t, err) + require.Contains(t, err.Error(), "indexed text column") +} + +// TestBuildFullTextIndexDefs_IncludeIndexedColumnMulti: in a multi-column MATCH(body, title) +// index, INCLUDE-ing ANY of the indexed columns is rejected (all KeyParts are excluded). +func TestBuildFullTextIndexDefs_IncludeIndexedColumnMulti(t *testing.T) { + idx := ftIndex("ft", "body", "title") + idx.IndexOption = &tree.IndexOption{IncludeColumns: []*tree.UnresolvedName{ + tree.NewUnresolvedName(tree.NewCStr("title", 0)), + }} + _, _, err := Hooks{}.BuildFullTextIndexDefs( + newStubCompilerContext(), idx, textColMap("id", "body", "title"), nil, "id") + require.Error(t, err) + require.Contains(t, err.Error(), "indexed text column") +} + +// TestBuildFullTextIndexDefs_NoInclude: without INCLUDE, IncludedColumns is empty and no +// included_columns param is written. +func TestBuildFullTextIndexDefs_NoInclude(t *testing.T) { + idxDefs, _, err := Hooks{}.BuildFullTextIndexDefs( + newStubCompilerContext(), ftIndex("ft", "body"), textColMap("id", "body"), nil, "id") + require.NoError(t, err) + require.Empty(t, idxDefs[0].IncludedColumns) + params, perr := catalog.IndexParamsStringToMap(idxDefs[0].IndexAlgoParams) + require.NoError(t, perr) + _, ok := params[catalog.IncludedColumns] + require.False(t, ok) +} + +// --- schema.go: buildFullText2Params directly ------------------------------ + +func TestBuildFullText2Params_DefaultParser(t *testing.T) { + s, err := buildFullText2Params(ftIndex("ft", "body")) + require.NoError(t, err) + m, err := catalog.IndexParamsStringToMap(s) + require.NoError(t, err) + require.Equal(t, "ngram", m["parser"]) // default parser always recorded +} + +func TestBuildFullText2Params_AllOptions(t *testing.T) { + idx := ftIndex("ft", "body") + idx.IndexOption = &tree.IndexOption{ + ParserName: "gojieba", + MaxIndexCapacity: 1000000, + MaxPostingsCapacity: 4000000, + PositionFree: true, + AutoUpdate: true, + Day: 1, + Hour: 2, + Second: 3, + } + s, err := buildFullText2Params(idx) + require.NoError(t, err) + m, err := catalog.IndexParamsStringToMap(s) + require.NoError(t, err) + require.Equal(t, "gojieba", m["parser"]) + require.Equal(t, "1000000", m[catalog.IndexAlgoParamMaxIndexCapacity]) + require.Equal(t, "4000000", m[catalog.IndexAlgoParamMaxPostingsCapacity]) + require.Equal(t, "true", m[catalog.IndexAlgoParamPositionFree]) + require.Equal(t, "true", m[catalog.AutoUpdate]) + require.Equal(t, "1", m[catalog.Day]) + require.Equal(t, "2", m[catalog.Hour]) + require.Equal(t, "3", m[catalog.Second]) +} + +func TestBuildFullText2Params_PositionFreeNonGojiebaError(t *testing.T) { + idx := ftIndex("ft", "body") + idx.IndexOption = &tree.IndexOption{ParserName: "ngram", PositionFree: true} + _, err := buildFullText2Params(idx) + require.Error(t, err) +} + +// --- tablefunc.go: getFullText2Params -------------------------------------- + +func TestGetFullText2Params_OK(t *testing.T) { + b := newStubPlanBuilder() + nv := tree.NewNumVal[string](`{"parser":"ngram"}`, `{"parser":"ngram"}`, false, tree.P_char) + out, err := getFullText2Params(b, &tree.FuncExpr{Exprs: tree.Exprs{nv}}) + require.NoError(t, err) + require.Equal(t, `{"parser":"ngram"}`, out) +} + +func TestGetFullText2Params_Error(t *testing.T) { + b := newStubPlanBuilder() + un := tree.NewUnresolvedName(tree.NewCStr("col", 0)) + _, err := getFullText2Params(b, &tree.FuncExpr{Exprs: tree.Exprs{un}}) + require.Error(t, err) +} + +// --- tablefunc.go: buildFullText2Create ------------------------------------ + +func TestBuildFullText2Create_TooFewArgs(t *testing.T) { + b := newStubPlanBuilder() + _, err := buildFullText2Create(b, numValTblFunc(`{}`), nil, makeArgs(3), nil) + require.Error(t, err) +} + +func TestBuildFullText2Create_BadParams(t *testing.T) { + b := newStubPlanBuilder() + _, err := buildFullText2Create(b, nonNumValTblFunc(), nil, makeArgs(4), nil) + require.Error(t, err) +} + +func TestBuildFullText2Create_OK(t *testing.T) { + b := newStubPlanBuilder() + id, err := buildFullText2Create(b, numValTblFunc(`{"parser":"ngram"}`), nil, makeArgs(4), nil) + require.NoError(t, err) + node := b.nodes[id] + require.Equal(t, plan.Node_FUNCTION_SCAN, node.NodeType) + require.Equal(t, FullText2CreateFuncName, node.TableDef.TblFunc.Name) + require.True(t, node.TableDef.TblFunc.IsSingle) + require.Len(t, node.TblFuncExprList, 3) // param stripped from the 4 args +} + +// --- tablefunc.go: buildFullText2Search ------------------------------------ + +func TestBuildFullText2Search_BadArgCount(t *testing.T) { + b := newStubPlanBuilder() + _, err := buildFullText2Search(b, numValTblFunc(`{}`), nil, makeArgs(3), nil) + require.Error(t, err) + _, err = buildFullText2Search(b, numValTblFunc(`{}`), nil, makeArgs(5), nil) + require.Error(t, err) +} + +func TestBuildFullText2Search_BadParams(t *testing.T) { + b := newStubPlanBuilder() + _, err := buildFullText2Search(b, nonNumValTblFunc(), nil, makeArgs(4), nil) + require.Error(t, err) +} + +func TestBuildFullText2Search_OK(t *testing.T) { + b := newStubPlanBuilder() + id, err := buildFullText2Search(b, numValTblFunc(`{"parser":"ngram"}`), nil, makeArgs(4), nil) + require.NoError(t, err) + node := b.nodes[id] + require.Equal(t, plan.Node_FUNCTION_SCAN, node.NodeType) + require.Equal(t, FullText2SearchFuncName, node.TableDef.TblFunc.Name) + require.False(t, node.TableDef.TblFunc.IsSingle) + require.Len(t, node.TblFuncExprList, 3) // param stripped from the 4 args +} + +// --- tablefunc.go: buildFullText2Compact ----------------------------------- + +func TestBuildFullText2Compact_OK(t *testing.T) { + b := newStubPlanBuilder() + id, err := buildFullText2Compact(b, numValTblFunc(`{}`), nil, makeArgs(4), nil) + require.NoError(t, err) + node := b.nodes[id] + require.Equal(t, plan.Node_FUNCTION_SCAN, node.NodeType) + require.Equal(t, FullText2CompactFuncName, node.TableDef.TblFunc.Name) + require.Len(t, node.TblFuncExprList, 4) // args passed as-is (no param strip) + require.Len(t, node.TableDef.Cols, 1) + require.Equal(t, "live_docs", node.TableDef.Cols[0].Name) +} + +// the compact builder is wired into the plan-side TVF registry, so the plan +// dispatch routes it through indexplugin instead of a hardcoded switch case. +func TestFullText2Compact_Registered(t *testing.T) { + _, ok := planplugin.TableFunc(FullText2CompactFuncName) + require.True(t, ok) +} diff --git a/pkg/fulltext2/plugin/plugin_cover_test.go b/pkg/fulltext2/plugin/plugin_cover_test.go new file mode 100644 index 0000000000000..9ea3945c68b44 --- /dev/null +++ b/pkg/fulltext2/plugin/plugin_cover_test.go @@ -0,0 +1,55 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/catalog" + indexplugin "github.com/matrixorigin/matrixone/pkg/indexplugin" + "github.com/stretchr/testify/require" +) + +// TestPluginHooks exercises New(), Algo() and every hook accessor on the +// fulltext2 Plugin, and confirms it satisfies the indexplugin.AlgoPlugin +// interface (the `var _ ...AlgoPlugin` assertion in plugin.go). +func TestPluginHooks(t *testing.T) { + p := New() + require.NotNil(t, p) + + // Algo string is the fulltext2 algo name. + require.Equal(t, catalog.MoIndexFullText2Algo.ToString(), p.Algo()) + require.Equal(t, "fulltext2", p.Algo()) + + // Every hook accessor returns a usable (non-nil interface) hook struct. + require.NotNil(t, p.Catalog()) + require.NotNil(t, p.Compile()) + require.NotNil(t, p.Plan()) + require.NotNil(t, p.Idxcron()) + + // The Plugin satisfies the AlgoPlugin interface (mirrors the compile-time + // `var _ indexplugin.AlgoPlugin = (*Plugin)(nil)` assertion). + var ap indexplugin.AlgoPlugin = p + require.Equal(t, p.Algo(), ap.Algo()) +} + +// TestPluginRegistered confirms init() registered the fulltext2 plugin so the +// framework can look it up by algo name. +func TestPluginRegistered(t *testing.T) { + got, ok := indexplugin.Get(catalog.MoIndexFullText2Algo.ToString()) + require.True(t, ok) + require.NotNil(t, got) + require.Equal(t, "fulltext2", got.Algo()) +} diff --git a/pkg/fulltext2/plugin/runtime/runtime_test.go b/pkg/fulltext2/plugin/runtime/runtime_test.go new file mode 100644 index 0000000000000..836feabe9d56f --- /dev/null +++ b/pkg/fulltext2/plugin/runtime/runtime_test.go @@ -0,0 +1,105 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package runtime + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/container/types" + catalogplugin "github.com/matrixorigin/matrixone/pkg/indexplugin/catalog" + "github.com/stretchr/testify/require" +) + +// TestCatalogHooks exercises every static accessor on CatalogHooks so the +// fulltext2 runtime metadata contract is pinned (storage+metadata hidden-table +// layout, always-async CDC maintenance, experimental gating, no vector/op/PK +// constraints, and the *tree.Index-parse NotSupported guard). +func TestCatalogHooks(t *testing.T) { + // Interface compliance is asserted in the source; re-assign here too. + var _ catalogplugin.Hooks = CatalogHooks{} + h := CatalogHooks{} + + // HiddenTableTypes — [Storage, Metadata]. + require.Equal(t, []string{ + catalog.FullText2Index_TblType_Storage, + catalog.FullText2Index_TblType_Metadata, + }, h.HiddenTableTypes()) + + // ShouldTruncateHiddenTable — always true for any input. + require.True(t, h.ShouldTruncateHiddenTable("")) + require.True(t, h.ShouldTruncateHiddenTable(catalog.FullText2Index_TblType_Storage)) + require.True(t, h.ShouldTruncateHiddenTable("anything")) + + // AlterTableCloneBehavior — whole index skipped on clone. + require.True(t, h.AlterTableCloneBehavior().SkipWholeIndex) + + // RestoreBehavior — zero value (no delete-before-clone). + require.Equal(t, catalogplugin.RestoreBehavior{}, h.RestoreBehavior()) + + // BuildSessionVars pins lower_case_table_names so the background reindex / + // ISCP build fold identifiers the way the index was created (mirrors the + // vector-index plugins); sql_mode is intentionally NOT captured (defaulted at + // the reindex hook). + require.Equal(t, []string{"lower_case_table_names"}, h.BuildSessionVars()) + + // Nil / empty accessors. + require.Nil(t, h.DefaultOptions()) + require.Nil(t, h.SupportedVectorTypes()) + require.Nil(t, h.SupportedOpTypes()) + // INCLUDE columns are supported (actual-value storage): the integer family + varchar/char, + // exactly the set the pk codec round-trips. Must EXCLUDE float (codec has no float case). + incTypes := h.SupportedIncludeColumnTypes() + require.NotEmpty(t, incTypes) + require.Contains(t, incTypes, types.T_int64) + require.Contains(t, incTypes, types.T_varchar) + require.NotContains(t, incTypes, types.T_float32) + // SupportedPrimaryKeyTypes mirrors the pk codec's domain (NOT nil "any type"): it + // includes int/varchar/uuid/temporal/decimal but must EXCLUDE FLOAT/DOUBLE, which the + // codec cannot encode — else a float pk would pass CREATE and fail at build. + pkTypes := h.SupportedPrimaryKeyTypes() + require.NotEmpty(t, pkTypes) + require.Contains(t, pkTypes, types.T_int64) + require.Contains(t, pkTypes, types.T_varchar) + require.NotContains(t, pkTypes, types.T_float32) + require.NotContains(t, pkTypes, types.T_float64) + + // IsVectorIndex — fulltext2 is a fulltext-family engine, NOT an ANN vector index; + // this is the capability behind indexplugin.IsVectorIndexAlgo, so a misreport here + // would let fulltext2 wrongly enter vector-index collection / ANN dispatch. + require.False(t, h.IsVectorIndex()) + + // ExperimentalFlag — the experimental_fulltext2_index gate. + require.Equal(t, Fulltext2IndexFlag, h.ExperimentalFlag()) + require.Equal(t, "experimental_fulltext2_index", h.ExperimentalFlag()) + + // ValidQuantization — no quantization, never errors. + require.NoError(t, h.ValidQuantization("", "")) + require.NoError(t, h.ValidQuantization("foo", "bar")) + + // ParamsFromTree — always NotSupported (fulltext2 parses *tree.FullTextIndex). + params, err := h.ParamsFromTree(nil) + require.Nil(t, params) + require.Error(t, err) + + // SyncDescriptor — always-async CDC + idxcron MERGE. + sd := h.SyncDescriptor() + require.True(t, sd.UsesCDC) + require.True(t, sd.AlwaysAsync) + require.Equal(t, catalogplugin.SinkerType_IndexSync, sd.SinkerType) + require.Equal(t, "fulltext2_reindex", sd.IdxcronAction) + require.Equal(t, "FULLTEXT2", sd.IdxcronAlgoToken) + require.Equal(t, "MERGE", sd.IdxcronReindexOption) +} diff --git a/pkg/fulltext2/pool_bench_test.go b/pkg/fulltext2/pool_bench_test.go new file mode 100644 index 0000000000000..e1778e91314f7 --- /dev/null +++ b/pkg/fulltext2/pool_bench_test.go @@ -0,0 +1,115 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "math/rand" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/vectorindex" +) + +// benchWandSegment builds a loaded segment with `nterms` distinct terms Zipf-ish +// spread across `ndocs` docs (so several terms span many blocks), then returns the +// deserialized (mmap-shaped) segment the query path actually runs against. +func benchWandSegment(b *testing.B, ndocs, nterms int) (*Segment, []clause) { + b.Helper() + terms := make([]string, nterms) + for i := range terms { + terms[i] = "term" + string(rune('a'+i%26)) + string(rune('0'+i/26)) + } + rng := rand.New(rand.NewSource(11)) + bld := NewBuilder("bench", int32(types.T_int64)) + for d := 0; d < ndocs; d++ { + pos := int32(0) + // each doc gets a handful of terms; earlier terms are more common (block spread). + for w := 0; w < 6; w++ { + ti := int(rng.ExpFloat64()*float64(nterms)/4) % nterms + if ti < 0 { + ti = -ti + } + if e := bld.Add(terms[ti], pos, int64(d)); e != nil { + b.Fatal(e) + } + pos += int32(len(terms[ti])) + 1 + } + } + seg, err := bld.Finish() + if err != nil { + b.Fatal(err) + } + blob, err := seg.Serialize() + if err != nil { + b.Fatal(err) + } + loaded, err := Deserialize("bench", bytes.NewReader(blob)) + if err != nil { + b.Fatal(err) + } + b.Cleanup(func() { _ = loaded.dict.Close() }) + + // a 4-term disjunction (4 wandIters => 4 pooled block-buffer pairs per query). + cs := []clause{ + {kind: clauseTerm, terms: []string{terms[0]}, weight: 1}, + {kind: clauseTerm, terms: []string{terms[1]}, weight: 1}, + {kind: clauseTerm, terms: []string{terms[2]}, weight: 1}, + {kind: clauseTerm, terms: []string{terms[3]}, weight: 1}, + } + return loaded, cs +} + +// BenchmarkSearchWANDAlloc reports allocs/op for the full searchWAND path. With the +// sync.Pool for cursor block buffers, the per-query BlockSize []int64/[]uint8 pairs are +// recycled, so they do NOT show up as steady-state allocations. +func BenchmarkSearchWANDAlloc(b *testing.B) { + seg, cs := benchWandSegment(b, 20000, 40) + gs := (&Index{segments: []*Segment{seg}, globalN: seg.N}).newGlobalStats() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = seg.searchWAND(cs, BM25, 10, nil, gs) + } +} + +// BenchmarkSearchWANDParallel exercises the pool under concurrency — the case where +// per-query buffer churn would otherwise outrun the GC. -race over this proves the +// Get/Put lifecycle has no cross-goroutine buffer sharing. +func BenchmarkSearchWANDParallel(b *testing.B) { + seg, cs := benchWandSegment(b, 20000, 40) + gs := (&Index{segments: []*Segment{seg}, globalN: seg.N}).newGlobalStats() + b.ReportAllocs() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + _ = seg.searchWAND(cs, BM25, 10, nil, gs) + } + }) +} + +// BenchmarkStreamAlloc reports allocs/op for the no-LIMIT streaming path. With typed +// ColumnBuffer the per-pk boxing is gone: a batch allocates only its Data/scores buffers +// (a few allocs per 8192 rows) instead of one boxed interface per doc. +func BenchmarkStreamAlloc(b *testing.B) { + seg, cs := benchWandSegment(b, 20000, 40) + idx := NewIndex([]*Segment{seg}, nil) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + sink := newStreamSink(idx, false, func(o *vectorindex.SearchOutput) error { PutColumnBuffer(o.Keys); return nil }) + _ = idx.streamDisjunction(cs, BM25, nil, sink) + } +} diff --git a/pkg/fulltext2/positionfree_reconstruct_test.go b/pkg/fulltext2/positionfree_reconstruct_test.go new file mode 100644 index 0000000000000..7da66ca9782e5 --- /dev/null +++ b/pkg/fulltext2/positionfree_reconstruct_test.go @@ -0,0 +1,61 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// TestReconstructLiveDocsPositionFree: a position-free segment has no positions, so +// ReconstructLiveDocs(true) must rebuild each doc's terms from tf (each term tf times), +// preserving the term SET + tf so a position-free MERGE keeps bag-of-words results. +func TestReconstructLiveDocsPositionFree(t *testing.T) { + b := NewBuilder("pf", int32(types.T_int64), WithPositionFree()) + feed(t, b, int64(0), "apple", "pie", "apple") // apple x2, pie x1 + feed(t, b, int64(1), "banana", "bread") + seg := loadedSeg(t, b) + idx := NewIndex([]*Segment{seg}, nil) + + docs, err := collectLiveDocs(idx, true) + require.NoError(t, err) + + got := map[any]map[string]int{} + for _, d := range docs { + m := map[string]int{} + for _, w := range d.Terms { + m[w]++ + } + got[normalizeKey(d.Pk)] = m + } + require.Equal(t, map[string]int{"apple": 2, "pie": 1}, got[normalizeKey(int64(0))]) + require.Equal(t, map[string]int{"banana": 1, "bread": 1}, got[normalizeKey(int64(1))]) + + // Rebuild position-free from the reconstruction → bag-of-words queries still work. + nb := NewBuilder("merged", int32(types.T_int64), WithPositionFree()) + for _, d := range docs { + for i, w := range d.Terms { + require.NoError(t, nb.Add(w, d.Positions[i], d.Pk)) + } + } + merged, err := nb.Finish() + require.NoError(t, err) + midx := NewIndex([]*Segment{merged}, nil) + res, err := midx.SearchBagOfWords([]byte("apple"), ParserDefault, BM25, 10, nil) + require.NoError(t, err) + require.Equal(t, []any{int64(0)}, resultIDs(res)) +} diff --git a/pkg/fulltext2/positionfree_test.go b/pkg/fulltext2/positionfree_test.go new file mode 100644 index 0000000000000..9d177c02d3f48 --- /dev/null +++ b/pkg/fulltext2/positionfree_test.go @@ -0,0 +1,110 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "fmt" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// vocabWord is an all-alphabetic term (so the query tokenizer maps it 1:1). +func vocabWord(i int) string { return fmt.Sprintf("word%c%c", 'a'+byte(i/26), 'a'+byte(i%26)) } + +// TestPositionFreeFootprint: WithPositionFree() drops the positional payload while +// keeping the FST + docID/tf postings, so the segment serializes smaller and still +// answers bag-of-words (WAND) queries with the same doc set + tf-based ranking. +func TestPositionFreeFootprint(t *testing.T) { + const ( + ndoc = 300 + ntok = 60 + nterm = 40 // words 0..19 appear twice/doc, 20..39 once — every word in every doc + ) + build := func(opts ...BuildOpt) *Segment { + b := NewBuilder("idx", int32(types.T_int64), opts...) + for d := 0; d < ndoc; d++ { + for i := 0; i < ntok; i++ { + require.NoError(t, b.Add(vocabWord(i%nterm), int32(i), int64(d))) + } + } + seg, err := b.Finish() + require.NoError(t, err) + return seg + } + + full := build() + pf := build(WithPositionFree()) + + fullBytes, err := full.Serialize() + require.NoError(t, err) + pfBytes, err := pf.Serialize() + require.NoError(t, err) + + saved := 100 * float64(len(fullBytes)-len(pfBytes)) / float64(len(fullBytes)) + t.Logf("serialized: full=%d bytes position-free=%d bytes saved=%.1f%%", + len(fullBytes), len(pfBytes), saved) + require.Less(t, len(pfBytes), len(fullBytes), "position-free segment must be smaller") + + // The position-free segment round-trips and answers a bag-of-words WAND query + // (boolean bare term → searchWAND, which never touches positions) with the full + // doc set — proving docID/tf postings and the FST survive. + loaded, err := Deserialize("idx", bytes.NewReader(pfBytes)) + require.NoError(t, err) + idx := NewIndex([]*Segment{loaded}, nil) + + res, err := idx.SearchQuery([]byte(vocabWord(1)), true, ParserDefault, BM25, ndoc+10, nil) + require.NoError(t, err) + require.Len(t, res, ndoc, "bag-of-words query must match every doc on the position-free index") + + // Parity: the full (positional) segment returns the identical doc set for the + // same bag-of-words query. + fidx := NewIndex([]*Segment{full}, nil) + fres, err := fidx.SearchQuery([]byte(vocabWord(1)), true, ParserDefault, BM25, ndoc+10, nil) + require.NoError(t, err) + require.ElementsMatch(t, resultIDs(fres), resultIDs(res)) +} + +// TestSearchBagOfWordsCJK: the IN BM25 MODE engine path (SearchBagOfWords) turns a CJK +// query into a disjunction of its gojieba tokens, so it returns the BAG-OF-WORDS +// superset — every doc containing any of 我家/有/三个/人 — whereas NL/phrase mode +// returns only the exact contiguous phrase. (Mirrors the SQL pre-flight.) +func TestSearchBagOfWordsCJK(t *testing.T) { + docs := []Doc{ + {Pk: int64(1), Text: []byte("我家有三个人")}, + {Pk: int64(2), Text: []byte("三个人都住在我家")}, + {Pk: int64(3), Text: []byte("我家的花园很漂亮")}, + {Pk: int64(4), Text: []byte("教室里有三个人")}, + {Pk: int64(5), Text: []byte("昨天我家有三个人来吃饭")}, + } + seg, err := BuildSegmentFromDocsParser("zh", int32(types.T_int64), docs, ParserGojieba) + require.NoError(t, err) + idx := NewIndex([]*Segment{seg}, nil) + + phrase, err := idx.SearchQuery([]byte("我家有三个人"), false, ParserGojieba, BM25, 10, nil) + require.NoError(t, err) + phraseIDs := resultIDs(phrase) + + bow, err := idx.SearchBagOfWords([]byte("我家有三个人"), ParserGojieba, BM25, 10, nil) + require.NoError(t, err) + bowIDs := resultIDs(bow) + + t.Logf("phrase(exact)=%v bag-of-words=%v", phraseIDs, bowIDs) + require.Contains(t, phraseIDs, int64(1), "doc 1 is the exact phrase") + require.Subset(t, bowIDs, phraseIDs, "bag-of-words must be a superset of the exact phrase") + require.Greater(t, len(bowIDs), len(phraseIDs), "bag-of-words must match strictly more docs than the phrase") +} diff --git a/pkg/fulltext2/search_cache_test.go b/pkg/fulltext2/search_cache_test.go new file mode 100644 index 0000000000000..049ae32b50c53 --- /dev/null +++ b/pkg/fulltext2/search_cache_test.go @@ -0,0 +1,110 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "fmt" + "sync" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// TestConcurrentSearchQuery is the concurrency contract behind Fulltext2Search / +// VectorIndexCache: a single loaded Index is shared (RW-locked) across concurrent +// queries, so SearchQuery must be pure-read — no mutation of segments, the FST, the +// loaded posting slices, or the liveness maps. It builds a loaded (serialized -> +// deserialized, i.e. FST + materialized `loaded` path) Index and hammers it from +// many goroutines with NL-phrase, boolean, and prefix queries at once, asserting +// every goroutine gets the SAME result as a single-threaded baseline. Run under +// -race, a shared write on the query path trips the detector. +func TestConcurrentSearchQuery(t *testing.T) { + // A loaded multi-segment Index (base + a tail delta) so liveness/global-stats and + // both the FST get() and the prefix Iterator() paths are exercised concurrently. + bb := NewBuilder("base", int32(types.T_int64)) + feed(t, bb, int64(0), "quick", "brown", "fox") + feed(t, bb, int64(1), "quick", "brown", "dog") + feed(t, bb, int64(2), "lazy", "fox", "sleeps") + base := loadedSeg(t, bb) + base.Recency = 0 + + tb := NewBuilder("tail", int32(types.T_int64)) + feed(t, tb, int64(3), "brown", "bear", "lazy") + feed(t, tb, int64(4), "quick", "quick", "fox") + tail := loadedSeg(t, tb) + tail.Recency = 1 + + idx := NewIndex([]*Segment{base, tail}, nil) + + type query struct { + pattern string + boolean bool + } + queries := []query{ + {"quick brown fox", false}, // NL exact phrase (FST get) + {"fox", false}, // single term + {"+quick +fox", true}, // boolean AND + {"+quick -fox", true}, // boolean AND-NOT + {"quic*", true}, // boolean prefix (FST Iterator) + {"lazy", false}, // term hitting both segments + } + + // Single-threaded baseline: the canonical (pk-set) answer for each query. + baseline := make([]map[any]struct{}, len(queries)) + for i, q := range queries { + res, err := idx.SearchQuery([]byte(q.pattern), q.boolean, ParserDefault, BM25, 100, nil) + require.NoError(t, err, "baseline %q", q.pattern) + m := make(map[any]struct{}, len(res)) + for _, r := range res { + m[r.Pk] = struct{}{} + } + baseline[i] = m + } + + const goroutines, iters = 24, 200 + var wg sync.WaitGroup + errCh := make(chan error, goroutines) + for g := 0; g < goroutines; g++ { + wg.Add(1) + go func() { + defer wg.Done() + for it := 0; it < iters; it++ { + q := queries[it%len(queries)] + res, err := idx.SearchQuery([]byte(q.pattern), q.boolean, ParserDefault, BM25, 100, nil) + if err != nil { + errCh <- fmt.Errorf("query %q: %w", q.pattern, err) + return + } + want := baseline[it%len(queries)] + if len(res) != len(want) { + errCh <- fmt.Errorf("query %q: got %d hits, want %d", q.pattern, len(res), len(want)) + return + } + for _, r := range res { + if _, ok := want[r.Pk]; !ok { + errCh <- fmt.Errorf("query %q: unexpected pk %v", q.pattern, r.Pk) + return + } + } + } + }() + } + wg.Wait() + close(errCh) + for err := range errCh { + t.Fatal(err) + } +} diff --git a/pkg/fulltext2/search_cache_unit_test.go b/pkg/fulltext2/search_cache_unit_test.go new file mode 100644 index 0000000000000..ad73bcb246447 --- /dev/null +++ b/pkg/fulltext2/search_cache_unit_test.go @@ -0,0 +1,310 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "math" + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/testutil" + "github.com/matrixorigin/matrixone/pkg/util/executor" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" + "github.com/stretchr/testify/require" +) + +func newSearchProc(t *testing.T) *sqlexec.SqlProcess { + return sqlexec.NewSqlProcess(testutil.NewProc(t)) +} + +// loadedSearch builds a Fulltext2Search whose Index is assembled in memory (via the +// serialize→deserialize loadedSeg path), bypassing Load/DB. +func loadedSearch(t *testing.T) *Fulltext2Search { + bb := NewBuilder("base", int32(types.T_int64)) + feed(t, bb, int64(0), "quick", "brown", "fox") + feed(t, bb, int64(1), "quick", "brown", "dog") + feed(t, bb, int64(2), "lazy", "fox", "sleeps") + seg := loadedSeg(t, bb) + s := NewFulltext2Search(TableConfig{IndexTable: "__store", Parser: ParserDefault}) + s.idx = NewIndex([]*Segment{seg}, nil) + s.loaded = true + return s +} + +func TestFulltext2SearchNewAndUnloaded(t *testing.T) { + proc := newSearchProc(t) + s := NewFulltext2Search(TableConfig{IndexTable: "__store"}) + require.Equal(t, "__store", s.cfg.IndexTable) + require.False(t, s.loaded) + + // Search before Load → "not loaded". + _, _, err := s.Search(proc, Fulltext2Query{Pattern: []byte("fox")}, vectorindex.RuntimeConfig{}) + require.ErrorContains(t, err, "not loaded") + + // SearchFloat32 is unsupported. + require.ErrorContains(t, s.SearchFloat32(proc, nil, vectorindex.RuntimeConfig{}, nil, nil), "not supported") +} + +// TestStaleGenSqls pins the cache-freshness generation queries: MAX(timestamp) over the +// metadata table (REBUILD/MERGE signal) and MAX(chunk_id) over the tag=1 CdcTail (CDC-append +// signal), scoped to (CdcTailId, tag=1) so a base sub-index cannot mask a fresh append. +func TestStaleGenSqls(t *testing.T) { + cfg := TableConfig{DbName: "db", IndexTable: "__store", MetadataTable: "__meta"} + tsSQL, tailSQL := StaleGenSqls(cfg) + require.Contains(t, tsSQL, "MAX(timestamp)") + require.Contains(t, tsSQL, "`db`.`__meta`") + require.Contains(t, tailSQL, "MAX(chunk_id)") + require.Contains(t, tailSQL, "`db`.`__store`") + require.Contains(t, tailSQL, "tag = 1") // tag=Tag_CdcEvents + require.Contains(t, tailSQL, vectorindex.CdcTailId) // scoped to the single CDC tail +} + +// TestIsStaleUncheckableEvicts: the index loaded fine (loadedSearch assembles segments in memory) +// but no generation was captured (genValid=false — the load-time capture failed). IsStale must +// report stale, NOT a no-op: an uncheckable entry whose TTL keeps sliding on every search would +// otherwise serve pre-CDC/rebuild data forever. Reporting stale forces a bounded evict+reload +// that retries capture and self-heals once it succeeds. +func TestIsStaleUncheckableEvicts(t *testing.T) { + s := loadedSearch(t) + require.False(t, s.genValid) // loaded, but generation never captured + stale, err := s.IsStale() + require.NoError(t, err) + require.True(t, stale, "an entry that can't self-check freshness must be evicted, not pinned") +} + +func TestFulltext2SearchEmptyIndex(t *testing.T) { + proc := newSearchProc(t) + s := NewFulltext2Search(TableConfig{IndexTable: "__store", Parser: ParserDefault}) + s.idx = NewIndex(nil, nil) // loaded but doc-less + s.loaded = true + + keys, dists, err := s.Search(proc, Fulltext2Query{Pattern: []byte("fox")}, vectorindex.RuntimeConfig{}) + require.NoError(t, err) + require.Empty(t, keys) + require.Empty(t, dists) +} + +func TestFulltext2SearchInvalidPayload(t *testing.T) { + proc := newSearchProc(t) + s := loadedSearch(t) + defer s.Destroy() + + _, _, err := s.Search(proc, "not a query", vectorindex.RuntimeConfig{}) + require.ErrorContains(t, err, "invalid query payload") +} + +func TestFulltext2SearchTopK(t *testing.T) { + proc := newSearchProc(t) + s := loadedSearch(t) + defer s.Destroy() + + // single-term NL query with a pushed LIMIT. + keys, dists, err := s.Search(proc, Fulltext2Query{Pattern: []byte("fox"), Algo: BM25}, + vectorindex.RuntimeConfig{Limit: 10}) + require.NoError(t, err) + ks, ok := keys.([]any) + require.True(t, ok) + require.Len(t, dists, len(ks)) + require.NotEmpty(t, ks) // "fox" hits docs 0 and 2 + + // k <= 0 (no pushed LIMIT) falls back to NumDocs. + keys, _, err = s.Search(proc, Fulltext2Query{Pattern: []byte("fox"), Algo: BM25}, + vectorindex.RuntimeConfig{Limit: 0}) + require.NoError(t, err) + require.NotEmpty(t, keys.([]any)) + + // an absurd LIMIT past MaxInt32 is clamped, not wrapped negative. + keys, _, err = s.Search(proc, Fulltext2Query{Pattern: []byte("fox"), Algo: BM25}, + vectorindex.RuntimeConfig{Limit: uint(math.MaxInt32) + 100}) + require.NoError(t, err) + require.NotEmpty(t, keys.([]any)) + + // bag-of-words (IN BM25 MODE) path. + keys, _, err = s.Search(proc, Fulltext2Query{Pattern: []byte("quick fox"), BagOfWords: true, Algo: BM25}, + vectorindex.RuntimeConfig{Limit: 10}) + require.NoError(t, err) + require.NotEmpty(t, keys.([]any)) +} + +func TestFulltext2SearchStreamingEmit(t *testing.T) { + proc := newSearchProc(t) + s := loadedSearch(t) + defer s.Destroy() + + // Emit set + no pushed LIMIT → streaming: results handed off via Emit, empty return. + for _, bagOfWords := range []bool{false, true} { + emitted := 0 + emit := func(o *vectorindex.SearchOutput) error { + emitted += o.Keys.N + PutColumnBuffer(o.Keys) // recycle like the real consumer + return nil + } + keys, dists, err := s.Search(proc, + Fulltext2Query{Pattern: []byte("fox"), BagOfWords: bagOfWords, Algo: BM25}, + vectorindex.RuntimeConfig{Emit: emit}) + require.NoError(t, err) + require.Empty(t, keys) + require.Empty(t, dists) + require.Positive(t, emitted, "bagOfWords=%v should emit docs", bagOfWords) + } +} + +func TestFulltext2SearchDestroy(t *testing.T) { + s := loadedSearch(t) + + // The cached config is immutable for the entry's lifetime (no UpdateConfig hook — + // a config change evicts the entry), so Search is pure-read; here we just pin that + // the constructed cfg is what Load queries with and that Destroy tears down cleanly. + require.Equal(t, ParserDefault, s.cfg.Parser) + + // Destroy frees and clears the loaded index. + s.Destroy() + require.Nil(t, s.idx) + require.False(t, s.loaded) +} + +// TestLoadGenerationHappy stubs the package runSql to cover the fulltext2 generation reader +// (StaleGenSqls + resultScalarInt64 + the two-read LoadGeneration body). +func TestLoadGenerationHappy(t *testing.T) { + mp := mpool.MustNewZero() + old := runSql + defer func() { runSql = old }() + n := 0 + runSql = func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + n++ + bat := batch.NewWithSize(1) + bat.Vecs[0] = vector.NewVec(types.T_int64.ToType()) + v := int64(11) + if n == 2 { + v = 22 + } + require.NoError(t, vector.AppendFixed[int64](bat.Vecs[0], v, false, mp)) + bat.SetRowCount(1) + return executor.Result{Mp: mp, Batches: []*batch.Batch{bat}}, nil + } + ts, tail, err := LoadGeneration(nil, TableConfig{DbName: "db", MetadataTable: "m", IndexTable: "s"}) + require.NoError(t, err) + require.Equal(t, int64(11), ts) // MAX(timestamp) + require.Equal(t, int64(22), tail) // MAX(chunk_id) tag=1 +} + +// TestLoadGenerationRecover: if the generation read panics (e.g. a background housekeeping call +// hits a torn-down executor), LoadGeneration must recover it into an error — never let it crash +// the caller. The caller then leaves genValid=false, and IsStale evicts to retry (see +// TestIsStaleUncheckableEvicts). +func TestLoadGenerationRecover(t *testing.T) { + old := runSql + defer func() { runSql = old }() + runSql = func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + panic("simulated executor teardown") + } + _, _, err := LoadGeneration(nil, TableConfig{DbName: "db", MetadataTable: "m", IndexTable: "s"}) + require.Error(t, err) + require.Contains(t, err.Error(), "LoadGeneration recovered") +} + +// TestFulltext2IsStaleQueryError: with a captured generation but an unresolvable CN service, the +// background QueryGeneration read errors (its recover turns the ServiceRuntime panic into an +// error), and IsStale treats a query error as stale so a dropped/rebuilt index's dead cache entry +// is reclaimed — while surfacing the error for logging. +func TestFulltext2IsStaleQueryError(t *testing.T) { + s := NewFulltext2Search(TableConfig{DbName: "db", MetadataTable: "m", IndexTable: "s"}) + s.genValid = true + s.cnUUID = "no-such-cn-uuid" + stale, err := s.IsStale() + require.Error(t, err) + require.True(t, stale) +} + +// TestFulltext2SearchInto pins the box-free LIMIT path: SearchInto fills the caller-owned +// SearchOutput (pk column, float32 scores, one nullable ColumnBuffer per FULL INCLUDE column) +// — box-free and reused across calls. incIdx has 5 docs all matching "x", includes +// [status varchar, prio int64] with a NULL status (pk4). +func TestFulltext2SearchInto(t *testing.T) { + proc := newSearchProc(t) + idx := incIdx(t) + s := &Fulltext2Search{idx: idx, loaded: true, cfg: TableConfig{IndexTable: "__store", Parser: ParserDefault}} + mp := mpool.MustNewZero() + + out := &vectorindex.SearchOutput{} + rt := vectorindex.RuntimeConfig{Limit: 10, RequestedIncludeColumns: []string{"status", "prio"}} + require.NoError(t, s.SearchInto(proc, Fulltext2Query{Pattern: []byte("x"), Algo: BM25}, rt, out)) + + require.Equal(t, 5, out.Keys.N) // all 5 docs contain "x" + require.Len(t, out.Dists, 5) + require.Len(t, out.Include, 2) // status, prio (FULL include order) + + // Decode the box-free buffers into vectors and zip by row (Keys[i] <-> Include[*][i]) into + // a pk -> (status, prio) map (result order is score-desc; equal scores are unspecified). + keyVec := vector.NewVec(types.T_int64.ToType()) + require.NoError(t, vectorindex.AppendColumnBuffer(out.Keys, keyVec, mp)) + statusVec := vector.NewVec(types.T_varchar.ToType()) + require.NoError(t, vectorindex.AppendColumnBuffer(out.Include[0], statusVec, mp)) + prioVec := vector.NewVec(types.T_int64.ToType()) + require.NoError(t, vectorindex.AppendColumnBuffer(out.Include[1], prioVec, mp)) + + pks := vector.MustFixedColWithTypeCheck[int64](keyVec) + prios := vector.MustFixedColWithTypeCheck[int64](prioVec) + require.Len(t, pks, 5) + type sv struct { + status any + prio int64 + } + got := map[int64]sv{} + for i := range pks { + var st any + if !statusVec.IsNull(uint64(i)) { + st = statusVec.GetStringAt(i) + } + got[pks[i]] = sv{st, prios[i]} + } + require.Equal(t, sv{"active", int64(10)}, got[1]) + require.Equal(t, sv{"inactive", int64(20)}, got[2]) + require.Equal(t, sv{"active", int64(30)}, got[3]) + require.Equal(t, sv{nil, int64(40)}, got[4]) // NULL status preserved + require.Equal(t, sv{"archived", int64(5)}, got[5]) + + // Reuse: a second SearchInto Resets out and refills (no stale rows accumulated). + require.NoError(t, s.SearchInto(proc, Fulltext2Query{Pattern: []byte("x"), Algo: BM25}, rt, out)) + require.Equal(t, 5, out.Keys.N) + require.Len(t, out.Dists, 5) + + // No requested INCLUDE columns → out.Include emptied. + require.NoError(t, s.SearchInto(proc, Fulltext2Query{Pattern: []byte("x"), Algo: BM25}, + vectorindex.RuntimeConfig{Limit: 10}, out)) + require.Equal(t, 5, out.Keys.N) + require.Empty(t, out.Include) + + // nil out → error, not a nil-deref. + require.Error(t, s.SearchInto(proc, Fulltext2Query{Pattern: []byte("x")}, rt, nil)) +} + +// TestFulltext2SearchIntoNotLoaded / empty: the two prepare() early-outs on the SearchInto path. +func TestFulltext2SearchIntoNotLoadedAndEmpty(t *testing.T) { + proc := newSearchProc(t) + out := &vectorindex.SearchOutput{} + + // not loaded → error. + s := NewFulltext2Search(TableConfig{IndexTable: "__store"}) + require.ErrorContains(t, s.SearchInto(proc, Fulltext2Query{Pattern: []byte("x")}, vectorindex.RuntimeConfig{Limit: 10}, out), "not loaded") + + // loaded but empty index → no rows, no error, out emptied. + s2 := &Fulltext2Search{idx: NewIndex(nil, nil), loaded: true, cfg: TableConfig{IndexTable: "__store"}} + require.NoError(t, s2.SearchInto(proc, Fulltext2Query{Pattern: []byte("x")}, vectorindex.RuntimeConfig{Limit: 10}, out)) +} diff --git a/pkg/fulltext2/search_test.go b/pkg/fulltext2/search_test.go new file mode 100644 index 0000000000000..f5ebabf8713f3 --- /dev/null +++ b/pkg/fulltext2/search_test.go @@ -0,0 +1,114 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" + "github.com/stretchr/testify/require" +) + +// A small fulltext corpus (int64 pk -> text), tokenized with the dict-free +// SimpleTokenizer — the same tokenizer used for both docs and queries so phrase +// adjacency is consistent. +func fulltextCorpus(t *testing.T) *Segment { + t.Helper() + docs := []Doc{ + {int64(1), []byte("the quick brown fox jumps over the lazy dog")}, // len 9 + {int64(2), []byte("a quick brown fox")}, // len 4 + {int64(3), []byte("the lazy dog sleeps all day")}, // len 6 + {int64(4), []byte("quick brown fox jumps high")}, // len 5 + {int64(5), []byte("the dog and the fox")}, // len 5 + } + s, err := BuildSegmentFromDocs("seg", int32(types.T_int64), docs, tokenizer.NewSimpleTokenizer()) + require.NoError(t, err) + return s +} + +func pkslice(rs []Result) []any { + out := make([]any, len(rs)) + for i, r := range rs { + out[i] = r.Pk + } + return out +} + +func query(t *testing.T, s *Segment, q string, algo ScoreAlgo, k int) []any { + t.Helper() + rs, err := s.SearchText([]byte(q), tokenizer.NewSimpleTokenizer(), algo, k) + require.NoError(t, err) + return pkslice(rs) +} + +// TestNLPhraseMatching: MATCH(col) AGAINST('...') in NL mode = exact contiguous +// phrase. Asserts the matched doc SET across representative fulltext cases. +func TestNLPhraseMatching(t *testing.T) { + s := fulltextCorpus(t) + + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4)}, query(t, s, "quick brown fox", TfIdf, 10)) + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4)}, query(t, s, "brown fox", TfIdf, 10)) + require.ElementsMatch(t, []any{int64(1), int64(3)}, query(t, s, "lazy dog", TfIdf, 10)) + require.ElementsMatch(t, []any{int64(1), int64(4)}, query(t, s, "fox jumps", TfIdf, 10)) + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4), int64(5)}, query(t, s, "fox", TfIdf, 10)) + + // "the fox" is contiguous only in doc 5 ("...the fox"); doc 1 has "the" and + // "fox" but not adjacent. + require.Equal(t, []any{int64(5)}, query(t, s, "the fox", TfIdf, 10)) + + // non-contiguous phrase — "quick" and "fox" are never adjacent (brown between) + require.Empty(t, query(t, s, "quick fox", TfIdf, 10)) + // absent term + require.Empty(t, query(t, s, "dog cat", TfIdf, 10)) + require.Empty(t, query(t, s, "unicorn", TfIdf, 10)) +} + +// TestTfIdfOrdering: equal-tf hits tie-break by ascending ord (deterministic). +func TestTfIdfOrdering(t *testing.T) { + s := fulltextCorpus(t) + // The three docs contain the phrase once each → equal TfIdf, so order is unspecified. + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4)}, query(t, s, "quick brown fox", TfIdf, 10)) +} + +// TestBM25Ordering: BM25 length-normalizes, so among equal-tf phrase hits the +// shorter document ranks higher: doc2(len4) > doc4(len5) > doc1(len9). +func TestBM25Ordering(t *testing.T) { + s := fulltextCorpus(t) + require.Equal(t, []any{int64(2), int64(4), int64(1)}, query(t, s, "quick brown fox", BM25, 10)) +} + +// TestTopK caps the result count. +func TestTopK(t *testing.T) { + s := fulltextCorpus(t) + require.Len(t, query(t, s, "fox", TfIdf, 2), 2) // 4 hits, top-2 + require.Len(t, query(t, s, "fox", TfIdf, 10), 4) +} + +// TestSearchOnLoadedSegment: build → serialize → deserialize → query, so the +// loaded (FST + ordinal postings) path produces identical results. +func TestSearchOnLoadedSegment(t *testing.T) { + data, err := fulltextCorpus(t).Serialize() + require.NoError(t, err) + loaded, err := Deserialize("seg", bytes.NewReader(data)) + require.NoError(t, err) + t.Cleanup(func() { _ = loaded.dict.Close() }) + + require.ElementsMatch(t, []any{int64(1), int64(2), int64(4)}, // equal TfIdf → order unspecified + query(t, loaded, "quick brown fox", TfIdf, 10)) + require.Equal(t, []any{int64(5)}, query(t, loaded, "the fox", TfIdf, 10)) + require.Empty(t, query(t, loaded, "quick fox", TfIdf, 10)) +} diff --git a/pkg/fulltext2/segment_test.go b/pkg/fulltext2/segment_test.go new file mode 100644 index 0000000000000..d6835e86b15c0 --- /dev/null +++ b/pkg/fulltext2/segment_test.go @@ -0,0 +1,87 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/require" +) + +func segmentWithTerms(terms ...string) *Segment { + s := NewSegment("seg0", 0) + m := make(map[string]*termPostings, len(terms)) + for i, t := range terms { + // distinct df per term so we can assert Lookup returns the right posting + m[t] = &termPostings{docIDs: make([]int64, i+1)} + } + s.setTerms(m) + return s +} + +// TestSetTermsSorted: setTerms derives an ascending key list consistent with the +// map (the invariant PrefixRange's binary search relies on). +func TestSetTermsSorted(t *testing.T) { + s := segmentWithTerms("banana", "apple", "cherry", "apricot") + require.Equal(t, 4, s.NumTerms()) + require.True(t, sort.StringsAreSorted(s.sortedTerms), "sortedTerms must be ascending") + require.Equal(t, []string{"apple", "apricot", "banana", "cherry"}, s.sortedTerms) + // every sorted key resolves in the map + for _, term := range s.sortedTerms { + _, ok := s.Lookup(term) + require.True(t, ok, term) + } +} + +// TestLookup: exact hit returns the term's own posting; miss returns false. +func TestLookup(t *testing.T) { + s := segmentWithTerms("apple", "apricot") + p, ok := s.Lookup("apple") + require.True(t, ok) + require.Equal(t, 1, p.df()) // "apple" was index 0 -> df 1 + p, ok = s.Lookup("apricot") + require.True(t, ok) + require.Equal(t, 2, p.df()) + _, ok = s.Lookup("avocado") + require.False(t, ok) + _, ok = s.Lookup("") + require.False(t, ok) +} + +// TestPrefixRange: the `word*` enumeration — prefix hit, boundary, no-match, and +// empty prefix (all terms). +func TestPrefixRange(t *testing.T) { + s := segmentWithTerms("app", "apple", "apply", "banana", "band", "bat") + + require.Equal(t, []string{"app", "apple", "apply"}, s.PrefixRange("app")) + require.Equal(t, []string{"banana", "band"}, s.PrefixRange("ban")) + require.Equal(t, []string{"bat"}, s.PrefixRange("bat")) + // prefix matches a whole run + require.Equal(t, []string{"banana", "band", "bat"}, s.PrefixRange("b")) + // no match — before, between, and after the key range + require.Empty(t, s.PrefixRange("aa")) + require.Empty(t, s.PrefixRange("az")) + require.Empty(t, s.PrefixRange("z")) + // empty prefix returns everything, in order + require.Equal(t, s.sortedTerms, s.PrefixRange("")) +} + +func TestPrefixRangeEmptySegment(t *testing.T) { + s := NewSegment("empty", 0) + require.Equal(t, 0, s.NumTerms()) + require.Empty(t, s.PrefixRange("x")) + require.Empty(t, s.PrefixRange("")) +} diff --git a/pkg/fulltext2/serialize_test.go b/pkg/fulltext2/serialize_test.go new file mode 100644 index 0000000000000..be265eeb15153 --- /dev/null +++ b/pkg/fulltext2/serialize_test.go @@ -0,0 +1,183 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// buildSegment assembles a build-side segment directly (as the build sink will). +func buildSegment(pkType int32, pks []any, docLen []int32, terms map[string]*termPostings) *Segment { + s := NewSegment("seg0", pkType) + s.pks = pks + s.docLen = docLen + s.N = int64(len(pks)) + s.setTerms(terms) + return s +} + +func roundtrip(t *testing.T, s *Segment) *Segment { + t.Helper() + data, err := s.Serialize() + require.NoError(t, err) + // deterministic output (FST + leBuf + tar all deterministic) + data2, err := s.Serialize() + require.NoError(t, err) + require.Equal(t, Checksum(data), Checksum(data2), "Serialize must be deterministic") + + loaded, err := Deserialize(s.Id, bytes.NewReader(data)) + require.NoError(t, err) + t.Cleanup(func() { _ = loaded.dict.Close() }) + return loaded +} + +// assertPostingsEqual compares a loaded term's posting list to the original. The +// loaded side keeps docIDs/tfs block-compressed on the mmap and positions +// compressed, so compare via materialize* rather than the (nil) build-side fields. +func assertPostingsEqual(t *testing.T, want, got *termPostings) { + t.Helper() + require.Equal(t, want.docIDs, got.materializeDocIDs()) + require.Equal(t, want.tfs, got.materializeTfs()) + gotPos := got.materializePositions() + require.Equal(t, len(want.positions), len(gotPos)) + for i := range want.positions { + require.Equalf(t, want.positions[i], gotPos[i], "positions[%d]", i) + } +} + +// allPks materializes a loaded segment's pks via the on-demand accessor (loaded +// segments hold no resident []any — pk(ord) decodes from the mmap-backed docmap). +func allPks(s *Segment) []any { + out := make([]any, s.numDocs()) + for i := range out { + out[i] = s.pk(int64(i)) + } + return out +} + +func TestSegmentRoundtripInt64PK(t *testing.T) { + terms := map[string]*termPostings{ + "apple": { + docIDs: []int64{0, 2}, + tfs: []uint8{3, 1}, + positions: [][]int32{{1, 5, 9}, {4}}, + }, + "banana": { + docIDs: []int64{1}, + tfs: []uint8{2}, + positions: [][]int32{{0, 7}}, + }, + "中文": { + docIDs: []int64{0, 1, 2}, + tfs: []uint8{1, 1, 1}, + positions: [][]int32{{2}, {3}, {6}}, + }, + } + orig := buildSegment(int32(types.T_int64), []any{int64(100), int64(200), int64(300)}, + []int32{5, 7, 3}, terms) + + loaded := roundtrip(t, orig) + + require.Equal(t, int32(types.T_int64), loaded.PkType) + require.Equal(t, int64(3), loaded.N) + require.Equal(t, []any{int64(100), int64(200), int64(300)}, allPks(loaded)) + require.Equal(t, []int32{5, 7, 3}, loaded.docLen) + + for term, want := range terms { + got, ok := loaded.LookupLoaded(term) + require.True(t, ok, term) + assertPostingsEqual(t, want, got) + } + // a missing term + _, ok := loaded.LookupLoaded("cherry") + require.False(t, ok) + // prefix still works over the loaded FST + it, ok, err := loaded.dict.prefixIter("app") + require.NoError(t, err) + require.True(t, ok) + term, _ := it.Current() + require.Equal(t, "apple", string(term)) + _ = it.Close() +} + +func TestSegmentRoundtripVarcharPK(t *testing.T) { + terms := map[string]*termPostings{ + "x": {docIDs: []int64{0, 1}, tfs: []uint8{1, 4}, positions: [][]int32{{0}, {1, 2, 3, 8}}}, + } + orig := buildSegment(int32(types.T_varchar), + []any{[]byte("alpha"), []byte("beta")}, []int32{2, 9}, terms) + + loaded := roundtrip(t, orig) + require.Equal(t, int32(types.T_varchar), loaded.PkType) + require.Equal(t, []any{[]byte("alpha"), []byte("beta")}, allPks(loaded)) + require.Equal(t, []int32{2, 9}, loaded.docLen) + got, ok := loaded.LookupLoaded("x") + require.True(t, ok) + assertPostingsEqual(t, terms["x"], got) +} + +func TestSegmentRoundtripDatetimePK(t *testing.T) { + terms := map[string]*termPostings{ + "t": {docIDs: []int64{0}, tfs: []uint8{1}, positions: [][]int32{{0}}}, + } + orig := buildSegment(int32(types.T_datetime), + []any{types.Datetime(1234567890)}, []int32{1}, terms) + + loaded := roundtrip(t, orig) + require.Equal(t, []any{types.Datetime(1234567890)}, allPks(loaded)) +} + +// Narrow integer PKs (TINYINT/SMALLINT, signed + unsigned) must round-trip: the +// build path (vector.GetAny) delivers them natively and no DDL guard rejects them, +// so the pk codec has to handle T_int8/T_int16/T_uint8/T_uint16 or CREATE FULLTEXT2 +// crashes with "unsupported pk type". Regression cover for the codec gap. +func TestSegmentRoundtripNarrowIntPK(t *testing.T) { + terms := map[string]*termPostings{ + "n": {docIDs: []int64{0, 1}, tfs: []uint8{1, 1}, positions: [][]int32{{0}, {0}}}, + } + cases := []struct { + typ types.T + pks []any + }{ + {types.T_int8, []any{int8(-128), int8(127)}}, + {types.T_uint8, []any{uint8(0), uint8(255)}}, + {types.T_int16, []any{int16(-32768), int16(32767)}}, + {types.T_uint16, []any{uint16(0), uint16(65535)}}, + } + for _, c := range cases { + t.Run(c.typ.String(), func(t *testing.T) { + orig := buildSegment(int32(c.typ), c.pks, []int32{1, 1}, terms) + loaded := roundtrip(t, orig) + require.Equal(t, int32(c.typ), loaded.PkType) + require.Equal(t, c.pks, allPks(loaded)) + got, ok := loaded.LookupLoaded("n") + require.True(t, ok) + assertPostingsEqual(t, terms["n"], got) + }) + } +} + +func TestSegmentRoundtripEmpty(t *testing.T) { + orig := buildSegment(int32(types.T_int64), []any{}, []int32{}, map[string]*termPostings{}) + loaded := roundtrip(t, orig) + require.Equal(t, int64(0), loaded.N) + require.Equal(t, 0, loaded.dict.len()) + _, ok := loaded.LookupLoaded("anything") + require.False(t, ok) +} diff --git a/pkg/fulltext2/sink_test.go b/pkg/fulltext2/sink_test.go new file mode 100644 index 0000000000000..34f547af98e45 --- /dev/null +++ b/pkg/fulltext2/sink_test.go @@ -0,0 +1,82 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" + "github.com/stretchr/testify/require" +) + +// TestSinkInsertUpdateDelete: a base build then a CDC batch that updates pk 2, +// deletes pk 3, and inserts pk 4. The assembled Index reflects the converged +// state: old text of an updated row gone, its new text visible, deleted row +// absent, new row present. +func TestSinkInsertUpdateDelete(t *testing.T) { + tok := tokenizer.NewSimpleTokenizer() + + // Base build (recency 0): full source scan. + base := NewSink(int32(types.T_int64), tok) + base.Add(RowEvent{Pk: int64(1), Text: []byte("alpha")}) + base.Add(RowEvent{Pk: int64(2), Text: []byte("beta")}) + base.Add(RowEvent{Pk: int64(3), Text: []byte("gamma")}) + baseSeg, baseDel, err := base.Build("base", 0) + require.NoError(t, err) + require.Empty(t, baseDel) + + // CDC batch (recency 1): UPDATE pk2 (delete+insert), DELETE pk3, INSERT pk4. + tail := NewSink(int32(types.T_int64), tok) + tail.Add(RowEvent{Delete: true, Pk: int64(2)}) + tail.Add(RowEvent{Pk: int64(2), Text: []byte("omega")}) + tail.Add(RowEvent{Delete: true, Pk: int64(3)}) + tail.Add(RowEvent{Pk: int64(4), Text: []byte("delta")}) + tailSeg, tailDel, err := tail.Build("tail", 1) + require.NoError(t, err) + // pk2 was reinserted (UPDATE) → no tombstone; pk3 is a pure delete. + require.Equal(t, map[any]int64{normalizeKey(int64(3)): 1}, tailDel) + + idx := NewIndex([]*Segment{baseSeg, tailSeg}, MergeDeletes(baseDel, tailDel)) + + require.Equal(t, int64(3), idx.NumDocs()) // pk 1, 2(new), 4 + require.Equal(t, []any{int64(1)}, iquery(t, idx, "alpha")) + require.Empty(t, iquery(t, idx, "beta")) // pk2 old text superseded + require.Equal(t, []any{int64(2)}, iquery(t, idx, "omega")) // pk2 new text + require.Empty(t, iquery(t, idx, "gamma")) // pk3 deleted + require.Equal(t, []any{int64(4)}, iquery(t, idx, "delta")) +} + +// TestSinkThroughSerialize: sink → serialize → deserialize → Index still +// converges (the maintenance path persists segments). +func TestSinkThroughSerialize(t *testing.T) { + tok := tokenizer.NewSimpleTokenizer() + sink := NewSink(int32(types.T_int64), tok) + sink.Add(RowEvent{Pk: int64(10), Text: []byte("quick brown fox")}) + sink.Add(RowEvent{Pk: int64(11), Text: []byte("lazy dog")}) + seg, _, err := sink.Build("base", 0) + require.NoError(t, err) + + data, err := seg.Serialize() + require.NoError(t, err) + loaded, err := Deserialize("base", bytes.NewReader(data)) + require.NoError(t, err) + t.Cleanup(func() { _ = loaded.dict.Close() }) + + idx := NewIndex([]*Segment{loaded}, nil) + require.Equal(t, []any{int64(10)}, iquery(t, idx, "quick brown fox")) + require.Equal(t, []any{int64(11)}, iquery(t, idx, "lazy dog")) +} diff --git a/pkg/fulltext2/storage_mock_test.go b/pkg/fulltext2/storage_mock_test.go new file mode 100644 index 0000000000000..7b89a643dc99e --- /dev/null +++ b/pkg/fulltext2/storage_mock_test.go @@ -0,0 +1,412 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "context" + "strings" + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/testutil" + "github.com/matrixorigin/matrixone/pkg/util/executor" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" + "github.com/stretchr/testify/require" +) + +// swapRunSql / swapRunStreamingSql install a mock and return a restore func. The engine +// indirects sqlexec.RunSql / RunStreamingSql through these package vars so DB round-trips +// are mockable. +func swapRunSql(t *testing.T, fn func(*sqlexec.SqlProcess, string) (executor.Result, error)) { + prev := runSql + runSql = fn + t.Cleanup(func() { runSql = prev }) +} + +func swapRunStreamingSql(t *testing.T, fn func(context.Context, *sqlexec.SqlProcess, string, chan executor.Result, chan error) (executor.Result, error)) { + prev := runStreamingSql + runStreamingSql = fn + t.Cleanup(func() { runStreamingSql = prev }) +} + +func int64Batch(mp *mpool.MPool, v int64) *batch.Batch { + b := batch.NewWithSize(1) + b.Vecs[0] = vector.NewVec(types.T_int64.ToType()) + _ = vector.AppendFixed[int64](b.Vecs[0], v, false, mp) + b.SetRowCount(1) + return b +} + +func metaBatch(mp *mpool.MPool, checksum string, filesize, recency int64) *batch.Batch { + b := batch.NewWithSize(3) + b.Vecs[0] = vector.NewVec(types.T_varchar.ToType()) + b.Vecs[1] = vector.NewVec(types.T_int64.ToType()) + b.Vecs[2] = vector.NewVec(types.T_int64.ToType()) + _ = vector.AppendBytes(b.Vecs[0], []byte(checksum), false, mp) + _ = vector.AppendFixed[int64](b.Vecs[1], filesize, false, mp) + _ = vector.AppendFixed[int64](b.Vecs[2], recency, false, mp) + b.SetRowCount(1) + return b +} + +// chunkBatch splits buf into (chunk_id, data) rows of <= MaxChunkSize bytes, the shape +// the base-chunk streaming reader consumes. +func chunkBatch(mp *mpool.MPool, buf []byte) *batch.Batch { + nchunks := (len(buf) + vectorindex.MaxChunkSize - 1) / vectorindex.MaxChunkSize + if nchunks == 0 { + nchunks = 0 + } + b := batch.NewWithSize(2) + b.Vecs[0] = vector.NewVec(types.T_int64.ToType()) + b.Vecs[1] = vector.NewVec(types.T_varchar.ToType()) + for i := 0; i < nchunks; i++ { + off := i * vectorindex.MaxChunkSize + end := off + vectorindex.MaxChunkSize + if end > len(buf) { + end = len(buf) + } + _ = vector.AppendFixed[int64](b.Vecs[0], int64(i), false, mp) + _ = vector.AppendBytes(b.Vecs[1], buf[off:end], false, mp) + } + b.SetRowCount(nchunks) + return b +} + +func mockSqlProc(t *testing.T) (*sqlexec.SqlProcess, *mpool.MPool) { + proc := testutil.NewProc(t) + return sqlexec.NewSqlProcess(proc), proc.Mp() +} + +func TestReadMetadata(t *testing.T) { + sp, mp := mockSqlProc(t) + cfg := testStorageCfg() + + // found row. + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{Mp: mp, Batches: []*batch.Batch{metaBatch(mp, "chk", 42, 7)}}, nil + }) + checksum, filesize, recency, found, err := readMetadata(sp, cfg, "id0") + require.NoError(t, err) + require.True(t, found) + require.Equal(t, "chk", checksum) + require.Equal(t, int64(42), filesize) + require.Equal(t, int64(7), recency) + + // no rows → found=false. + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{Mp: mp, Batches: nil}, nil + }) + _, _, _, found, err = readMetadata(sp, cfg, "id0") + require.NoError(t, err) + require.False(t, found) + + // runSql error is surfaced. + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{}, moerr.NewInternalErrorNoCtx("boom") + }) + _, _, _, _, err = readMetadata(sp, cfg, "id0") + require.Error(t, err) +} + +func TestScanHelpers(t *testing.T) { + sp, mp := mockSqlProc(t) + cfg := testStorageCfg() + + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, 13)}}, nil + }) + + n, err := CountTailChunks(sp, cfg) + require.NoError(t, err) + require.Equal(t, int64(13), n) + + n, err = SumBaseNrow(sp, cfg) + require.NoError(t, err) + require.Equal(t, int64(13), n) + + n, err = NextTailChunkId(sp, cfg) + require.NoError(t, err) + require.Equal(t, int64(13), n) + + // error path. + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{}, moerr.NewInternalErrorNoCtx("boom") + }) + _, err = SumBaseNrow(sp, cfg) + require.Error(t, err) +} + +func TestLoadBudgetGates(t *testing.T) { + sp, mp := mockSqlProc(t) + cfg := testStorageCfg() + + // small doc/byte counts fit comfortably → nil. + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, 100)}}, nil + }) + require.NoError(t, checkBaseLoadBudget(sp, cfg)) + require.NoError(t, checkTailLoadBudget(sp, cfg)) + + // an enormous count exceeds the CN budget → actionable error (no int64 overflow). + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, int64(1)<<40)}}, nil + }) + require.Error(t, checkBaseLoadBudget(sp, cfg)) + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, int64(1)<<50)}}, nil + }) + require.Error(t, checkTailLoadBudget(sp, cfg)) +} + +func TestLoadAllBasesEmpty(t *testing.T) { + sp, mp := mockSqlProc(t) + cfg := testStorageCfg() + + // enumerate returns no ids → no bases (never touches LoadFromStorage). + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{Mp: mp, Batches: nil}, nil + }) + bases, err := LoadAllBases(sp, cfg) + require.NoError(t, err) + require.Empty(t, bases) +} + +func TestLoadTailSegmentsEmpty(t *testing.T) { + sp, mp := mockSqlProc(t) + cfg := testStorageCfg() + + // dispatch by SQL: the budget gate is a SUM(LENGTH(...)) 1-col scan; the tail SELECT + // (chunk_id, data) returns no rows → empty tail. + swapRunSql(t, func(_ *sqlexec.SqlProcess, sql string) (executor.Result, error) { + if strings.Contains(sql, "LENGTH(") { + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, 0)}}, nil + } + return executor.Result{Mp: mp, Batches: nil}, nil + }) + segs, deletes, err := LoadTailSegments(sp, cfg) + require.NoError(t, err) + require.Empty(t, segs) + require.Empty(t, deletes) +} + +// TestLoadFromStorageRoundTrip mocks the metadata read + base-chunk stream with a REAL +// serialized segment, exercising the full decode path (spill → mmap → checksum → decode). +func TestLoadFromStorageRoundTrip(t *testing.T) { + sp, mp := mockSqlProc(t) + cfg := testStorageCfg() + + b := NewBuilder("seg0", int32(types.T_int64)) + feed(t, b, int64(1), "hello", "world") + feed(t, b, int64(2), "hello", "matrix") + seg, err := b.Finish() + require.NoError(t, err) + seg.Id = "seg0" + seg.Recency = 5 + buf, err := seg.Serialize() + require.NoError(t, err) + checksum := vectorindex.CheckSumFromBuffer(buf) + filesize := int64(len(buf)) + + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{Mp: mp, Batches: []*batch.Batch{metaBatch(mp, checksum, filesize, 5)}}, nil + }) + swapRunStreamingSql(t, func(_ context.Context, _ *sqlexec.SqlProcess, _ string, sc chan executor.Result, _ chan error) (executor.Result, error) { + sc <- executor.Result{Mp: mp, Batches: []*batch.Batch{chunkBatch(mp, buf)}} + return executor.Result{}, nil + }) + + loaded, err := LoadFromStorage(sp, cfg, "seg0") + require.NoError(t, err) + require.Equal(t, "seg0", loaded.Id) + require.Equal(t, int64(5), loaded.Recency) + require.Equal(t, seg.N, loaded.N) + loaded.Free() + + // a checksum mismatch (corrupt stream) is detected and rejected. + swapRunStreamingSql(t, func(_ context.Context, _ *sqlexec.SqlProcess, _ string, sc chan executor.Result, _ chan error) (executor.Result, error) { + bad := append([]byte(nil), buf...) + bad[0] ^= 0xFF + sc <- executor.Result{Mp: mp, Batches: []*batch.Batch{chunkBatch(mp, bad)}} + return executor.Result{}, nil + }) + _, err = LoadFromStorage(sp, cfg, "seg0") + require.ErrorContains(t, err, "checksum mismatch") + + // missing metadata → clear error. + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{Mp: mp, Batches: nil}, nil + }) + _, err = LoadFromStorage(sp, cfg, "seg0") + require.ErrorContains(t, err, "metadata not found") +} + +// tailChunkBatch renders forged (chunk_id, data) rows as the tail-data result batch. +func tailChunkBatch(mp *mpool.MPool, chunks []TailChunk) *batch.Batch { + b := batch.NewWithSize(2) + b.Vecs[0] = vector.NewVec(types.T_int64.ToType()) + b.Vecs[1] = vector.NewVec(types.T_varchar.ToType()) + for _, c := range chunks { + _ = vector.AppendFixed[int64](b.Vecs[0], c.ChunkId, false, mp) + _ = vector.AppendBytes(b.Vecs[1], c.Data, false, mp) + } + b.SetRowCount(len(chunks)) + return b +} + +// TestCompactSegmentsFoldsTail drives the full MERGE: no bases + a forged tag=1 tail +// insert frame ⇒ the tail is folded into a fresh base. runSql dispatches by SQL text +// (the user's "different runSql for different SQLs") and DELETE/INSERT writes succeed. +func TestCompactSegmentsFoldsTail(t *testing.T) { + sp, mp := mockSqlProc(t) + cfg := testStorageCfg() + + // forge a tail insert frame carrying two docs. + tb := NewBuilder("tail", int32(types.T_int64)) + feed(t, tb, int64(1), "hello", "world") + feed(t, tb, int64(2), "hello", "matrix") + tseg, err := tb.Finish() + require.NoError(t, err) + framed, err := FrameSegment(tseg) + require.NoError(t, err) + chunks := splitFrameChunks(1, framed) + + var deleteAllRan, insertRan bool + swapRunSql(t, func(_ *sqlexec.SqlProcess, sql string) (executor.Result, error) { + switch { + case strings.Contains(sql, "GREATEST"): // NextTailChunkId + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, 100)}}, nil + case strings.Contains(sql, "LENGTH("): // checkTailLoadBudget + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, 1)}}, nil + case strings.Contains(sql, vectorindex.CdcTailId) && strings.Contains(sql, "SELECT"): // tail data + return executor.Result{Mp: mp, Batches: []*batch.Batch{tailChunkBatch(mp, chunks)}}, nil + case strings.HasPrefix(strings.TrimSpace(sql), "SELECT"): // LoadAllBases enumerate → no bases + return executor.Result{Mp: mp, Batches: nil}, nil + default: // DELETE / INSERT writes succeed + if strings.HasPrefix(sql, "DELETE") && strings.Contains(sql, "TRUE") { + deleteAllRan = true + } + if strings.HasPrefix(sql, "INSERT") { + insertRan = true + } + return executor.Result{Mp: mp}, nil + } + }) + + nlive, err := CompactSegments(sp, cfg, 0, 0) + require.NoError(t, err) + require.Equal(t, 2, nlive) // both docs are live → folded into the fresh base + require.True(t, deleteAllRan, "MERGE must clear prior bases first") + require.True(t, insertRan, "MERGE must persist the rebuilt base") +} + +// twoIdBatch renders a 2-row index_id enumerate result. +func twoIdBatch(mp *mpool.MPool, a, b string) *batch.Batch { + bat := batch.NewWithSize(1) + bat.Vecs[0] = vector.NewVec(types.T_varchar.ToType()) + _ = vector.AppendBytes(bat.Vecs[0], []byte(a), false, mp) + _ = vector.AppendBytes(bat.Vecs[0], []byte(b), false, mp) + bat.SetRowCount(2) + return bat +} + +// loadOneBase round-trips a real serialized segment through LoadFromStorage (mocked +// metadata + chunk stream), returning the mapped segment. +func loadOneBase(t *testing.T, sp *sqlexec.SqlProcess, mp *mpool.MPool, cfg TableConfig, id string) *Segment { + t.Helper() + b := NewBuilder(id, int32(types.T_int64)) + feed(t, b, int64(1), "hello") + seg, err := b.Finish() + require.NoError(t, err) + seg.Id = id + buf, err := seg.Serialize() + require.NoError(t, err) + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{Mp: mp, Batches: []*batch.Batch{metaBatch(mp, vectorindex.CheckSumFromBuffer(buf), int64(len(buf)), 0)}}, nil + }) + swapRunStreamingSql(t, func(_ context.Context, _ *sqlexec.SqlProcess, _ string, sc chan executor.Result, _ chan error) (executor.Result, error) { + sc <- executor.Result{Mp: mp, Batches: []*batch.Batch{chunkBatch(mp, buf)}} + return executor.Result{}, nil + }) + m, err := LoadFromStorage(sp, cfg, id) + require.NoError(t, err) + return m +} + +// TestFreeSegsReleasesMmap pins that freeSegs munmaps a loaded segment (nils mmapData), +// the primitive LoadAllBases now uses to avoid leaking on a partial load. +func TestFreeSegsReleasesMmap(t *testing.T) { + sp, mp := mockSqlProc(t) + cfg := testStorageCfg() + m := loadOneBase(t, sp, mp, cfg, "s0") + require.NotNil(t, m.mmapData) // mapped + freeSegs([]*Segment{m, nil}) // nil entry must be a no-op + require.Nil(t, m.mmapData) // munmapped +} + +// TestLoadAllBasesFreesOnPartialFailure: enumerate returns two ids; base "s0" maps, then +// base "s1" fails (metadata missing). LoadAllBases must free the already-mapped s0 before +// returning the error rather than leaking its mmap (+ spill file) — the #4 fix. +func TestLoadAllBasesFreesOnPartialFailure(t *testing.T) { + sp, mp := mockSqlProc(t) + cfg := testStorageCfg() + + b := NewBuilder("s0", int32(types.T_int64)) + feed(t, b, int64(1), "hello") + seg, err := b.Finish() + require.NoError(t, err) + buf, err := seg.Serialize() + require.NoError(t, err) + + swapRunSql(t, func(_ *sqlexec.SqlProcess, sql string) (executor.Result, error) { + switch { + case strings.Contains(sql, "'s1'"): // readMetadata for s1 → missing → LoadFromStorage errors + return executor.Result{Mp: mp, Batches: nil}, nil + case strings.Contains(sql, "checksum"): // readMetadata for s0 + return executor.Result{Mp: mp, Batches: []*batch.Batch{metaBatch(mp, vectorindex.CheckSumFromBuffer(buf), int64(len(buf)), 0)}}, nil + default: // enumerate index_id → [s0, s1] + return executor.Result{Mp: mp, Batches: []*batch.Batch{twoIdBatch(mp, "s0", "s1")}}, nil + } + }) + swapRunStreamingSql(t, func(_ context.Context, _ *sqlexec.SqlProcess, _ string, sc chan executor.Result, _ chan error) (executor.Result, error) { + sc <- executor.Result{Mp: mp, Batches: []*batch.Batch{chunkBatch(mp, buf)}} + return executor.Result{}, nil + }) + + bases, err := LoadAllBases(sp, cfg) + require.Error(t, err, "s1 fails to load") + require.Nil(t, bases, "no partial slice is returned (s0 was freed)") +} + +// TestCompactSegmentsNoDelta covers the early-out: empty bases + empty tail ⇒ nothing to +// compact. +func TestCompactSegmentsNoDelta(t *testing.T) { + sp, mp := mockSqlProc(t) + cfg := testStorageCfg() + + swapRunSql(t, func(_ *sqlexec.SqlProcess, sql string) (executor.Result, error) { + if strings.Contains(sql, "LENGTH(") { + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, 0)}}, nil + } + return executor.Result{Mp: mp, Batches: nil}, nil // empty enumerate + empty tail + }) + nlive, err := CompactSegments(sp, cfg, 0, 0) + require.NoError(t, err) + require.Equal(t, 0, nlive) +} diff --git a/pkg/fulltext2/storage_test.go b/pkg/fulltext2/storage_test.go new file mode 100644 index 0000000000000..d4cba6b7c6d64 --- /dev/null +++ b/pkg/fulltext2/storage_test.go @@ -0,0 +1,189 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "context" + "fmt" + "os" + "strings" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/stretchr/testify/require" +) + +func testStorageCfg() TableConfig { + return TableConfig{DbName: "db", SrcTable: "src", IndexTable: "__store", MetadataTable: "__meta"} +} + +func TestSubIndexId(t *testing.T) { + require.Equal(t, "build:7", SubIndexId("build", 7)) + require.Equal(t, "a:b:c:0", SubIndexId("a:b:c", 0)) +} + +func TestDeleteSqls(t *testing.T) { + cfg := testStorageCfg() + + del := DeleteSqls(cfg, "idx:1") + require.Len(t, del, 2) + require.Contains(t, del[0], "__store") + require.Contains(t, del[0], "idx:1") + require.Contains(t, del[1], "__meta") + + all := DeleteAllBasesSqls(cfg) + require.Len(t, all, 2) + require.Contains(t, all[0], "__store") + require.Contains(t, all[1], "__meta") + + tail := DeleteTailSqls(cfg) + require.Len(t, tail, 1) + require.Contains(t, tail[0], "__store") +} + +func TestFileChunkInsertSqls(t *testing.T) { + cfg := testStorageCfg() + + // a small whole-file (baseOffset 0) fits in ONE chunk row and ONE INSERT. + one := fileChunkInsertSqls(cfg, "idx:0", 0, "/tmp/spill", 0, 10, int(vectorindex.Tag_ModelChunk)) + require.Len(t, one, 1) + require.Contains(t, one[0], "load_file(") + require.Contains(t, one[0], "offset=0") + require.Contains(t, one[0], "size=10") + + // a file spanning > maxInsertTuples chunks splits into multiple INSERT statements: + // maxInsertTuples+1 chunks ⇒ one full INSERT + one trailing INSERT. + nchunks := maxInsertTuples + 1 + dataLen := nchunks * vectorindex.MaxChunkSize + many := fileChunkInsertSqls(cfg, "idx:0", 0, "/tmp/spill", 0, dataLen, int(vectorindex.Tag_ModelChunk)) + require.Len(t, many, 2) + // every chunk contributes one VALUES tuple. + require.Equal(t, nchunks, strings.Count(many[0], "load_file(")+strings.Count(many[1], "load_file(")) + + // a PACKED frame at a non-zero baseOffset: the load_file range is shifted by the offset, so a + // two-chunk frame reads [off, off+MaxChunkSize) then [off+MaxChunkSize, off+len). + off := int64(4096) + packed := fileChunkInsertSqls(cfg, "idx:0", 0, "/tmp/spool", off, vectorindex.MaxChunkSize+7, int(vectorindex.Tag_CdcEvents)) + require.Len(t, packed, 1) + require.Contains(t, packed[0], fmt.Sprintf("offset=%d", off)) + require.Contains(t, packed[0], fmt.Sprintf("offset=%d", off+int64(vectorindex.MaxChunkSize))) + require.Contains(t, packed[0], "size=7") + + // the tail helper delegates to fileChunkInsertSqls with CdcTailId + Tag_CdcEvents, threading the frame offset. + tail := TailFileInsertSqls(cfg, 5, "/tmp/spool", 200, vectorindex.MaxChunkSize+1) + require.Len(t, tail, 1) + require.Contains(t, tail[0], vectorindex.CdcTailId) + require.Contains(t, tail[0], "offset=200") +} + +// TestTailFramesInsertSqls pins the cross-frame batching: N tiny (1-chunk) frames must NOT become N +// INSERT statements — they batch at maxInsertTuples rows per statement ACROSS frame +// boundaries, with contiguous chunk_ids and the right next-chunk-id returned. This is what turns a +// burst of ~360k op-run frames into ~3.6k RunSql round-trips instead of 360k. +func TestTailFramesInsertSqls(t *testing.T) { + cfg := testStorageCfg() + + // Enough tiny 1-chunk frames to force exactly 3 statements regardless of maxInsertTuples: + // two full batches + a 50-row remainder. (Robust to the maxInsertTuples value.) + n := 2*maxInsertTuples + 50 + frames := make([]TailSegment, n) + for i := 0; i < n; i++ { + frames[i] = TailSegment{Path: "/tmp/spool", Offset: int64(i * 8), FrameLen: 8} + } + startChunk := int64(7) + sqls, next := TailFramesInsertSqls(cfg, startChunk, frames) + + // n rows batched at maxInsertTuples/statement ⇒ 3 statements, NOT n (one-per-frame). + require.Len(t, sqls, 3) + total := 0 + for _, s := range sqls { + total += strings.Count(s, "load_file(") + require.Contains(t, s, vectorindex.CdcTailId) + } + require.Equal(t, n, total, "every frame contributes exactly one chunk row") + // chunk_ids are contiguous from startChunk; next = startChunk + total chunks. + require.Equal(t, startChunk+int64(n), next) + require.Contains(t, sqls[0], fmt.Sprintf(", %d, load_file", startChunk)) // first chunk id + require.Contains(t, sqls[2], fmt.Sprintf(", %d, load_file", startChunk+int64(n)-1)) // last chunk id + require.Contains(t, sqls[2], fmt.Sprintf("offset=%d", (n-1)*8)) // last frame's offset in the last stmt + + // empty input ⇒ no statements, next == start. + sqls0, next0 := TailFramesInsertSqls(cfg, startChunk, nil) + require.Empty(t, sqls0) + require.Equal(t, startChunk, next0) +} + +func TestNextTailChunkIdSql(t *testing.T) { + sql := NextTailChunkIdSql(testStorageCfg()) + require.Contains(t, sql, "GREATEST") + require.Contains(t, sql, vectorindex.CdcTailId) + require.Contains(t, sql, "__store") + require.Contains(t, sql, "__meta") +} + +func TestFrameChunkCount(t *testing.T) { + require.Equal(t, int64(1), FrameChunkCount(0)) // empty ⇒ at least one + require.Equal(t, int64(1), FrameChunkCount(1)) // partial ⇒ one + require.Equal(t, int64(1), FrameChunkCount(vectorindex.MaxChunkSize)) // exact ⇒ one + require.Equal(t, int64(2), FrameChunkCount(vectorindex.MaxChunkSize+1)) // spillover ⇒ two + require.Equal(t, int64(3), FrameChunkCount(2*vectorindex.MaxChunkSize+1)) // ceil +} + +func TestToInsertSqls(t *testing.T) { + cfg := testStorageCfg() + + b := NewBuilder("uid:0", int32(types.T_int64)) + feed(t, b, int64(1), "hello", "world") + feed(t, b, int64(2), "hello", "matrix") + seg, err := b.Finish() + require.NoError(t, err) + seg.Id = SubIndexId("uid", 0) + seg.Recency = 3 + + // nil sqlproc ⇒ createLocalSpillFile falls back to an os temp file. + sqls, cleanup, err := seg.ToInsertSqls(nil, cfg, 12345, int(vectorindex.Tag_ModelChunk)) + require.NoError(t, err) + require.NotNil(t, cleanup) + defer cleanup() + + // first SQL is the metadata row; the rest persist the chunk bytes. + require.GreaterOrEqual(t, len(sqls), 2) + require.Contains(t, sqls[0], "__meta") + require.Contains(t, sqls[0], "uid:0") + require.Contains(t, sqls[1], "__store") + require.Contains(t, sqls[1], "load_file(") +} + +func TestCreateLocalSpillAndTempFile(t *testing.T) { + // nil sqlproc ⇒ both spill helpers fall back to the OS temp dir. + require.Equal(t, "", localSpillDir(nil)) + + fp, err := createLocalSpillFile(nil, "ft2test") + require.NoError(t, err) + name := fp.Name() + require.NoError(t, fp.Close()) + require.NoError(t, os.Remove(name)) + + f, path, err := createLocalTempFile(nil, "ft2test") + require.NoError(t, err) + require.NotEmpty(t, path) // linked file in the OS temp dir + require.NoError(t, f.Close()) + require.NoError(t, os.Remove(path)) +} + +func TestLocalSpillDirNilRootFS(t *testing.T) { + require.Equal(t, "", LocalSpillDir(context.Background(), nil)) +} diff --git a/pkg/fulltext2/stream_phrase_test.go b/pkg/fulltext2/stream_phrase_test.go new file mode 100644 index 0000000000000..dd5942f030b84 --- /dev/null +++ b/pkg/fulltext2/stream_phrase_test.go @@ -0,0 +1,174 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "encoding/binary" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/stretchr/testify/require" +) + +// TestStreamPhraseParity pins that the no-LIMIT streaming phrase path (streamPhrase) +// returns EXACTLY the same pk set and scores as the materializing SearchPhrase (the +// LIMIT path) — same idf²-scaled score, only without the globalN heap. +func TestStreamPhraseParity(t *testing.T) { + bb := NewBuilder("p", int32(types.T_int64)) + feed(t, bb, int64(1), "alpha", "beta") // phrase match + feed(t, bb, int64(2), "alpha", "beta") // phrase match + feed(t, bb, int64(3), "x", "alpha", "beta") // phrase match (offset) + feed(t, bb, int64(4), "beta", "alpha") // NOT adjacent as "alpha beta" + feed(t, bb, int64(5), "alpha", "gamma") // no beta + seg := loadedSeg(t, bb) + idx := NewIndex([]*Segment{seg}, nil) + + slots, err := phraseSlots("alpha beta", ParserDefault) + require.NoError(t, err) + + // materialized reference (LIMIT path, k above corpus so it returns every match). + want := idx.SearchPhrase(slots, BM25, 1<<20, nil) + wantScore := make(map[int64]float32, len(want)) + for _, r := range want { + wantScore[r.Pk.(int64)] = r.Score + } + require.ElementsMatch(t, []int64{1, 2, 3}, keysOf(wantScore)) + + // streamed (no-LIMIT) — decode the box-free int64 ColumnBuffer batches. + got := make(map[int64]float32) + emit := func(o *vectorindex.SearchOutput) error { + for i := 0; i < o.Keys.N; i++ { + pk := int64(binary.LittleEndian.Uint64(o.Keys.Data[i*8:])) + got[pk] = o.Dists[i] + } + PutColumnBuffer(o.Keys) + return nil + } + require.NoError(t, idx.StreamQuery([]byte("alpha beta"), false, ParserDefault, BM25, nil, false, emit)) + + // same pk set, same idf²-scaled scores. + require.Equal(t, len(wantScore), len(got)) + for pk, ws := range wantScore { + gs, ok := got[pk] + require.Truef(t, ok, "streamed result missing pk %d", pk) + require.InEpsilonf(t, ws, gs, 1e-6, "score mismatch for pk %d", pk) + } +} + +func keysOf(m map[int64]float32) []int64 { + out := make([]int64, 0, len(m)) + for k := range m { + out = append(out, k) + } + return out +} + +func streamPhraseIDs(t *testing.T, idx *Index, pattern string) map[int64]float32 { + t.Helper() + got := make(map[int64]float32) + emit := func(o *vectorindex.SearchOutput) error { + for i := 0; i < o.Keys.N; i++ { + got[int64(binary.LittleEndian.Uint64(o.Keys.Data[i*8:]))] = o.Dists[i] + } + PutColumnBuffer(o.Keys) + return nil + } + require.NoError(t, idx.StreamQuery([]byte(pattern), false, ParserDefault, BM25, nil, false, emit)) + return got +} + +// TestFillBlockPositionsReuseMultiBlock stresses the Phase-1 pooled-buffer reuse across +// MULTIPLE blocks (>BlockSize docs) with varying per-doc position counts: the phrase +// term repeats a different number of times per doc, so each reused out[i] slot changes +// length block to block. A stale/aliased backing array would surface as a wrong match +// set. Runs twice so a pooled buffer is re-Got from the pool between queries. +func TestFillBlockPositionsReuseMultiBlock(t *testing.T) { + const nDocs = 3 * BlockSize // spans several position blocks + bb := NewBuilder("mb", int32(types.T_int64)) + wantMatch := map[int64]bool{} + for d := 0; d < nDocs; d++ { + pk := int64(d) + reps := 1 + d%5 // varying position-list length per doc + var words []string + for r := 0; r < reps; r++ { + words = append(words, "alpha", "beta") + } + if d%3 == 0 { // a third have "beta alpha" appended → still contains the phrase "alpha beta" + words = append(words, "beta", "alpha") + } + if d%7 == 0 { // some docs break the phrase entirely (no adjacent alpha->beta) + words = []string{"beta", "alpha", "gamma"} + wantMatch[pk] = false + } else { + wantMatch[pk] = true + } + feed(t, bb, pk, words...) + } + seg := loadedSeg(t, bb) + idx := NewIndex([]*Segment{seg}, nil) + + slots, err := phraseSlots("alpha beta", ParserDefault) + require.NoError(t, err) + + run := func() { + res := idx.SearchPhrase(slots, BM25, 1<<20, nil) + got := map[int64]bool{} + for _, r := range res { + got[r.Pk.(int64)] = true + } + for pk, want := range wantMatch { + require.Equalf(t, want, got[pk], "pk %d match", pk) + } + } + run() // first pass populates the pool + run() // second pass re-Gets a reused buffer — must be identical +} + +// TestStreamPhraseParityMultiSegment: streamPhrase across a base + CDC-tail (with a +// delete making one base copy dead) must match SearchPhrase — same live set, same +// idf²-scaled scores — proving the multi-segment / liveness path streams correctly. +func TestStreamPhraseParityMultiSegment(t *testing.T) { + base := NewBuilder("base", int32(types.T_int64)) + feed(t, base, int64(1), "alpha", "beta") + feed(t, base, int64(2), "alpha", "beta") + feed(t, base, int64(3), "beta", "alpha") // no phrase + baseSeg := loadedSeg(t, base) + baseSeg.Recency = 0 + + tail := NewBuilder("tail", int32(types.T_int64)) + feed(t, tail, int64(4), "alpha", "beta") + tailSeg := loadedSeg(t, tail) + tailSeg.Recency = 10 + + // delete pk 2 (its base phrase-match copy must drop out of both paths). + deletes := foldDeleteFrame(nil, []DeleteRecord{{Pk: int64(2)}}, 20) + idx := NewIndex([]*Segment{baseSeg, tailSeg}, deletes) + + slots, err := phraseSlots("alpha beta", ParserDefault) + require.NoError(t, err) + want := idx.SearchPhrase(slots, BM25, 1<<20, nil) + wantScore := make(map[int64]float32, len(want)) + for _, r := range want { + wantScore[r.Pk.(int64)] = r.Score + } + require.ElementsMatch(t, []int64{1, 4}, keysOf(wantScore)) // 2 deleted, 3 no-phrase + + got := streamPhraseIDs(t, idx, "alpha beta") + require.Equal(t, len(wantScore), len(got)) + for pk, ws := range wantScore { + require.InEpsilonf(t, ws, got[pk], 1e-6, "score mismatch pk %d", pk) + } +} diff --git a/pkg/fulltext2/stream_test.go b/pkg/fulltext2/stream_test.go new file mode 100644 index 0000000000000..8bfa9b0d96ff9 --- /dev/null +++ b/pkg/fulltext2/stream_test.go @@ -0,0 +1,82 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "encoding/binary" + "errors" + "testing" + + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/stretchr/testify/require" +) + +// int64ColumnBuffer decodes an int64-pk ColumnBuffer batch (Fixed, width 8) back to []int64. +func int64ColumnBuffer(k *vectorindex.ColumnBuffer) []int64 { + out := make([]int64, k.N) + for i := 0; i < k.N; i++ { + out[i] = int64(binary.LittleEndian.Uint64(k.Data[i*8:])) + } + return out +} + +// TestStreamQuery pins the no-LIMIT streaming path: StreamQuery must emit EXACTLY the +// same (pk, score) set the materialized SearchQuery(k=all) produces — for both the +// heap-free disjunctive path (streamWAND) and the materialize-then-emit fallback (NL +// phrase) — and it must stop and propagate an emit error. Uses a base + CDC-tail split +// so streaming exercises the per-segment liveness + global stats. +func TestStreamQuery(t *testing.T) { + split := NewIndex([]*Segment{ + buildFtSeg(t, "a", 0, 0, 12), + buildFtSeg(t, "b", 1, 12, 24), + }, nil) + + collect := func(pattern string, boolean bool) map[int64]float64 { + m := map[int64]float64{} + err := split.StreamQuery([]byte(pattern), boolean, ParserDefault, BM25, nil, false, + func(o *vectorindex.SearchOutput) error { + require.LessOrEqual(t, o.Keys.N, streamBatch) + for i, k := range int64ColumnBuffer(o.Keys) { + _, dup := m[k] + require.Falsef(t, dup, "pk %d emitted twice", k) + m[k] = float64(o.Dists[i]) + } + return nil + }) + require.NoError(t, err) + return m + } + sameAsMaterialized := func(pattern string, boolean bool) { + want, err := split.SearchQuery([]byte(pattern), boolean, ParserDefault, BM25, 1000, nil) + require.NoError(t, err) + got := collect(pattern, boolean) + require.Equalf(t, len(want), len(got), "%q: same match count", pattern) + for _, r := range want { + g, ok := got[r.Pk.(int64)] + require.Truef(t, ok, "%q: pk %v streamed", pattern, r.Pk) + require.InDeltaf(t, r.Score, g, 1e-5, "%q: pk %v score", pattern, r.Pk) + } + } + + sameAsMaterialized("alpha beta gamma", true) // disjunctive → streamWAND + sameAsMaterialized("beta delta", true) // disjunctive → streamWAND + sameAsMaterialized("alpha", false) // NL single-word → materialize fallback + + // emit-error path: the callback error propagates and stops the walk. + sentinel := errors.New("consumer aborted") + err := split.StreamQuery([]byte("alpha beta"), true, ParserDefault, BM25, nil, false, + func(o *vectorindex.SearchOutput) error { return sentinel }) + require.ErrorIs(t, err, sentinel) +} diff --git a/pkg/fulltext2/stream_vec_test.go b/pkg/fulltext2/stream_vec_test.go new file mode 100644 index 0000000000000..29e6c8afb9a95 --- /dev/null +++ b/pkg/fulltext2/stream_vec_test.go @@ -0,0 +1,169 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/stretchr/testify/require" +) + +// TestColumnBufferRoundTrip pins the box-free typed streaming codec: a pk encoded into a +// ColumnBuffer (producer, appendAnyPk) must decode back to the identical value in a vector +// of that type (consumer, AppendColumnBuffer), across the fixed-width and varlena paths. +func TestColumnBufferRoundTrip(t *testing.T) { + mp := mpool.MustNewZero() + + build := func(pkType types.T, vals []any) *vectorindex.ColumnBuffer { + w, fixed := fixedPkByteWidth(int32(pkType)) + if !fixed { + w = 0 + } + k := &vectorindex.ColumnBuffer{Type: pkType} + for _, v := range vals { + appendAnyPk(k, w, v) + } + require.Equal(t, len(vals), k.N) + return k + } + toVec := func(pkType types.T, vals []any) *vector.Vector { + vec := vector.NewVec(pkType.ToType()) + require.NoError(t, vectorindex.AppendColumnBuffer(build(pkType, vals), vec, mp)) + require.Equal(t, len(vals), vec.Length()) + return vec + } + + // fixed-width paths + require.Equal(t, []int64{1, -5, 1 << 40}, + vector.MustFixedColWithTypeCheck[int64](toVec(types.T_int64, []any{int64(1), int64(-5), int64(1 << 40)}))) + require.Equal(t, []uint64{0, 1, 1<<64 - 1}, + vector.MustFixedColWithTypeCheck[uint64](toVec(types.T_uint64, []any{uint64(0), uint64(1), uint64(1<<64 - 1)}))) + require.Equal(t, []uint64{7, 255}, + vector.MustFixedColWithTypeCheck[uint64](toVec(types.T_bit, []any{uint64(7), uint64(255)}))) + require.Equal(t, []int32{-3, 100000}, + vector.MustFixedColWithTypeCheck[int32](toVec(types.T_int32, []any{int32(-3), int32(100000)}))) + require.Equal(t, []int8{-1, 2}, + vector.MustFixedColWithTypeCheck[int8](toVec(types.T_int8, []any{int8(-1), int8(2)}))) + require.Equal(t, []types.Datetime{types.Datetime(123456789), types.Datetime(-42)}, + vector.MustFixedColWithTypeCheck[types.Datetime](toVec(types.T_datetime, []any{types.Datetime(123456789), types.Datetime(-42)}))) + require.Equal(t, []types.Decimal128{{B0_63: 5, B64_127: 9}}, + vector.MustFixedColWithTypeCheck[types.Decimal128](toVec(types.T_decimal128, []any{types.Decimal128{B0_63: 5, B64_127: 9}}))) + + // varlena paths + vvar := toVec(types.T_varchar, []any{[]byte("abc"), []byte(""), []byte("a longer varchar pk")}) + require.Equal(t, "abc", vvar.GetStringAt(0)) + require.Equal(t, "", vvar.GetStringAt(1)) + require.Equal(t, "a longer varchar pk", vvar.GetStringAt(2)) + + u1, _ := types.ParseUuid("00000000-0000-0000-0000-000000000001") + u2, _ := types.ParseUuid("12345678-1234-1234-1234-1234567890ab") + require.Equal(t, []types.Uuid{u1, u2}, + vector.MustFixedColWithTypeCheck[types.Uuid](toVec(types.T_uuid, []any{u1, u2}))) + + // remaining AppendColumnBuffer arms: decode without error at the right count, so every + // switch case is exercised (a missing case would panic or mis-count). + more := []struct { + typ types.T + vals []any + }{ + {types.T_uint32, []any{uint32(1), uint32(4000000000)}}, + {types.T_int16, []any{int16(-30000), int16(30000)}}, + {types.T_uint16, []any{uint16(60000)}}, + {types.T_uint8, []any{uint8(200)}}, + {types.T_date, []any{types.Date(19000)}}, + {types.T_time, []any{types.Time(123456)}}, + {types.T_timestamp, []any{types.Timestamp(1700000000)}}, + {types.T_decimal64, []any{types.Decimal64(42)}}, + {types.T_char, []any{[]byte("c")}}, + {types.T_text, []any{[]byte("longer text pk value")}}, + {types.T_blob, []any{[]byte{0, 1, 2, 255}}}, + {types.T_json, []any{[]byte(`{"k":1}`)}}, + } + for _, c := range more { + require.Equalf(t, len(c.vals), toVec(c.typ, c.vals).Length(), "type %v", c.typ) + } +} + +// TestStreamLoadedSegmentAllPkTypes covers the LOADED-segment streaming path (appendPkTo +// reading the docmap, not the build-side re-encode) plus StreamBagOfWords, across a +// fixed-width, a varlena, and a uuid pk (the encodePkLen default branch). It serializes → +// deserializes each segment, streams every doc, and checks the emitted pks via the +// pooled ColumnBuffer → vector path (AppendColumnBuffer + PutColumnBuffer). +func TestStreamLoadedSegmentAllPkTypes(t *testing.T) { + mp := mpool.MustNewZero() + uu, _ := types.ParseUuid("12345678-1234-1234-1234-1234567890ab") + + stream := func(pkType types.T, pks []any) *vector.Vector { + b := NewBuilder("ld", int32(pkType)) + for _, pk := range pks { + feed(t, b, pk, "alpha") + } + seg, err := b.Finish() + require.NoError(t, err) + blob, err := seg.Serialize() + require.NoError(t, err) + loaded, err := Deserialize("ld", bytes.NewReader(blob)) + require.NoError(t, err) + t.Cleanup(func() { _ = loaded.dict.Close() }) + + idx := NewIndex([]*Segment{loaded}, nil) + out := vector.NewVec(pkType.ToType()) + err = idx.StreamBagOfWords([]byte("alpha"), ParserDefault, BM25, nil, false, + func(o *vectorindex.SearchOutput) error { + e := vectorindex.AppendColumnBuffer(o.Keys, out, mp) + PutColumnBuffer(o.Keys) // recycle, as the real consumer does + return e + }) + require.NoError(t, err) + require.Equal(t, len(pks), out.Length()) + return out + } + + // int64 pk → fixed-width loaded path + require.ElementsMatch(t, []int64{10, 20, 30}, + vector.MustFixedColWithTypeCheck[int64](stream(types.T_int64, []any{int64(10), int64(20), int64(30)}))) + + // varchar pk → varlena loaded path + vv := stream(types.T_varchar, []any{[]byte("k1"), []byte("k2")}) + got := []string{vv.GetStringAt(0), vv.GetStringAt(1)} + require.ElementsMatch(t, []string{"k1", "k2"}, got) + + // uuid pk → encodePkLen default branch + varlena loaded path + require.ElementsMatch(t, []types.Uuid{uu}, + vector.MustFixedColWithTypeCheck[types.Uuid](stream(types.T_uuid, []any{uu}))) +} + +// TestStreamBagOfWordsEdges covers the two early-out branches of StreamBagOfWords: an +// empty index (globalN==0) and a pattern that resolves to no tokens. +func TestStreamBagOfWordsEdges(t *testing.T) { + noEmit := func(*vectorindex.SearchOutput) error { + t.Fatal("emit should not be called") + return nil + } + // empty index → globalN == 0 → nil, no emit. + require.NoError(t, NewIndex(nil, nil).StreamBagOfWords([]byte("alpha"), ParserDefault, BM25, nil, false, noEmit)) + + // non-empty index but a pattern that tokenizes to nothing → no resolvable tokens → nil. + b := NewBuilder("e", int32(types.T_int64)) + feed(t, b, int64(1), "alpha") + seg, err := b.Finish() + require.NoError(t, err) + require.NoError(t, NewIndex([]*Segment{seg}, nil).StreamBagOfWords([]byte(" "), ParserDefault, BM25, nil, false, noEmit)) +} diff --git a/pkg/fulltext2/termdict_jieba_test.go b/pkg/fulltext2/termdict_jieba_test.go new file mode 100644 index 0000000000000..6aac31cd3f529 --- /dev/null +++ b/pkg/fulltext2/termdict_jieba_test.go @@ -0,0 +1,131 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Large-scale term-dict test: build the vellum FST from the REAL gojieba +// dictionary (~349k Chinese/mixed words) and exercise exact lookup + `word*` +// prefix enumeration at scale, proving UTF-8 byte order == code-point order (so +// CJK prefix ranges are correct) on production vocabulary rather than a toy set. +package fulltext2 + +import ( + "bufio" + "os" + "path/filepath" + "sort" + "strings" + "testing" + + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" + "github.com/stretchr/testify/require" +) + +// readJiebaWords loads the unique words (first column) of jieba.dict.utf8, or +// skips the test if the dictionary is not available in this environment. +func readJiebaWords(t *testing.T) []string { + t.Helper() + dir := tokenizer.JiebaDictDir() + if dir == "" { + t.Skip("jieba dict dir not found") + } + f, err := os.Open(filepath.Join(dir, "jieba.dict.utf8")) + if err != nil { + t.Skipf("cannot open jieba dict: %v", err) + } + defer f.Close() + + seen := make(map[string]struct{}) + var terms []string + sc := bufio.NewScanner(f) + for sc.Scan() { + fields := strings.Fields(sc.Text()) + if len(fields) == 0 { + continue + } + w := fields[0] + if _, dup := seen[w]; dup { + continue + } + seen[w] = struct{}{} + terms = append(terms, w) + } + require.NoError(t, sc.Err()) + return terms +} + +// TestTermDictFromJiebaDict builds the FST from the whole jieba vocabulary and +// checks exact get() + prefixTerms() over it. +func TestTermDictFromJiebaDict(t *testing.T) { + terms := readJiebaWords(t) + require.Greater(t, len(terms), 100000, "real jieba dict has ~349k words") + + // The FST requires ascending byte order + unique keys (the Segment invariant). + sort.Strings(terms) + values := make([]uint64, len(terms)) + want := make(map[string]uint64, len(terms)) + for i, w := range terms { + values[i] = uint64(i) + want[w] = uint64(i) + } + + data, err := buildTermDictFST(terms, values) + require.NoError(t, err) + d, err := loadTermDict(data) + require.NoError(t, err) + defer d.Close() + + require.Equal(t, len(terms), d.len()) + + // Exact lookups for well-known dictionary words return their assigned value. + for _, w := range []string{"中文", "北京", "清华大学", "苹果", "香蕉", "天安门"} { + exp, in := want[w] + if !in { + continue // tolerate dict variations across environments + } + val, ok, gerr := d.get(w) + require.NoError(t, gerr) + require.True(t, ok, "%q must be present", w) + require.Equal(t, exp, val, "value mismatch for %q", w) + } + + // A word that cannot be in the dictionary is absent. + _, ok, err := d.get("绝对不存在的怪词ZzQq") + require.NoError(t, err) + require.False(t, ok) + + // Prefix enumeration: every 中文* term begins with 中文, is a real dict word, + // and the results are ascending — the CJK `word*` path over real vocabulary. + pref, err := d.prefixTerms("中文") + require.NoError(t, err) + require.NotEmpty(t, pref) + require.Contains(t, pref, "中文") + require.True(t, sort.StringsAreSorted(pref), "prefix results must be ascending") + seen := make(map[string]struct{}, len(terms)) + for _, w := range terms { + seen[w] = struct{}{} + } + for _, w := range pref { + require.True(t, strings.HasPrefix(w, "中文"), "%q lacks prefix 中文", w) + _, in := seen[w] + require.True(t, in, "%q not in source vocabulary", w) + } + + // The prefix scan found the same count as a brute-force filter (no misses). + brute := 0 + for _, w := range terms { + if strings.HasPrefix(w, "中文") { + brute++ + } + } + require.Equal(t, brute, len(pref)) +} diff --git a/pkg/fulltext2/termdict_test.go b/pkg/fulltext2/termdict_test.go new file mode 100644 index 0000000000000..371a1433642df --- /dev/null +++ b/pkg/fulltext2/termdict_test.go @@ -0,0 +1,133 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "sort" + "testing" + + "github.com/blevesearch/vellum" + "github.com/stretchr/testify/require" +) + +// buildDict sorts the given terms (as the segment does), assigns each a distinct +// value, and builds + loads an FST term dict. Returns the dict and the expected +// term→value map. Terms must be unique. +func buildDict(t *testing.T, terms ...string) (*termDict, map[string]uint64) { + t.Helper() + sorted := append([]string(nil), terms...) + sort.Strings(sorted) + vals := make([]uint64, len(sorted)) + exp := make(map[string]uint64, len(sorted)) + for i, term := range sorted { + vals[i] = uint64(i*100 + 7) // distinct, non-trivial + exp[term] = vals[i] + } + data, err := buildTermDictFST(sorted, vals) + require.NoError(t, err) + d, err := loadTermDict(data) + require.NoError(t, err) + t.Cleanup(func() { _ = d.Close() }) + return d, exp +} + +// collectPrefix drains prefixIter into a slice of terms (ascending). +func collectPrefix(t *testing.T, d *termDict, prefix string) []string { + t.Helper() + it, ok, err := d.prefixIter(prefix) + require.NoError(t, err) + if !ok { + return nil + } + defer func() { _ = it.Close() }() + var got []string + for { + term, _ := it.Current() + got = append(got, string(term)) + if err := it.Next(); err == vellum.ErrIteratorDone { + break + } else { + require.NoError(t, err) + } + } + return got +} + +func TestTermDictGet(t *testing.T) { + d, exp := buildDict(t, "apple", "apply", "banana", "band") + require.Equal(t, 4, d.len()) + for term, want := range exp { + got, ok, err := d.get(term) + require.NoError(t, err) + require.True(t, ok, term) + require.Equal(t, want, got, term) + } + // misses + for _, miss := range []string{"", "app", "avocado", "bandit", "z"} { + _, ok, err := d.get(miss) + require.NoError(t, err) + require.False(t, ok, miss) + } +} + +func TestTermDictPrefixASCII(t *testing.T) { + d, _ := buildDict(t, "app", "apple", "apply", "banana", "band", "bat") + + require.Equal(t, []string{"app", "apple", "apply"}, collectPrefix(t, d, "app")) + require.Equal(t, []string{"apple", "apply"}, collectPrefix(t, d, "appl")) + require.Equal(t, []string{"banana", "band"}, collectPrefix(t, d, "ban")) + require.Equal(t, []string{"bat"}, collectPrefix(t, d, "bat")) + require.Equal(t, []string{"banana", "band", "bat"}, collectPrefix(t, d, "b")) + // empty prefix → all terms, ascending + require.Equal(t, []string{"app", "apple", "apply", "banana", "band", "bat"}, collectPrefix(t, d, "")) + // no matches — before, between, after + require.Empty(t, collectPrefix(t, d, "aa")) + require.Empty(t, collectPrefix(t, d, "az")) + require.Empty(t, collectPrefix(t, d, "z")) +} + +// TestTermDictChinese is the load-bearing case: CJK terms need no special +// handling because vellum is byte-oriented and UTF-8 byte order == code-point +// order, so Go's string sort is already vellum's required insertion order, and +// prefix iteration works on whole-character prefixes. +func TestTermDictChinese(t *testing.T) { + // 中(U+4E2D) 国(U+56FD) 文(U+6587) — bigrams share a leading character. + d, exp := buildDict(t, "中", "中国", "中文", "国", "文", "国家") + require.Equal(t, 6, d.len()) + + // exact lookup of a multi-byte term + got, ok, err := d.get("中文") + require.NoError(t, err) + require.True(t, ok) + require.Equal(t, exp["中文"], got) + + // prefix over a shared leading character → the two bigrams + the unigram + require.Equal(t, []string{"中", "中国", "中文"}, collectPrefix(t, d, "中")) + require.Equal(t, []string{"国", "国家"}, collectPrefix(t, d, "国")) + require.Equal(t, []string{"文"}, collectPrefix(t, d, "文")) + // full ascending order matches byte/code-point order + require.Equal(t, []string{"中", "中国", "中文", "国", "国家", "文"}, collectPrefix(t, d, "")) + require.Empty(t, collectPrefix(t, d, "日")) +} + +func TestPrefixSuccessor(t *testing.T) { + require.Equal(t, []byte{'a', 'p', 'q'}, prefixSuccessor([]byte("app"))) + require.Equal(t, []byte{0x01}, prefixSuccessor([]byte{0x00})) + // trailing 0xFF bytes are dropped, the first < 0xFF is incremented + require.Equal(t, []byte{0x02}, prefixSuccessor([]byte{0x01, 0xFF, 0xFF})) + // empty prefix and all-0xFF have no finite successor → nil (open upper bound) + require.Nil(t, prefixSuccessor([]byte{})) + require.Nil(t, prefixSuccessor([]byte{0xFF, 0xFF})) +} diff --git a/pkg/fulltext2/topk_bench_test.go b/pkg/fulltext2/topk_bench_test.go new file mode 100644 index 0000000000000..062610606408f --- /dev/null +++ b/pkg/fulltext2/topk_bench_test.go @@ -0,0 +1,48 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "math/rand" + "testing" +) + +func benchResults(n, buckets int) []Result { + rng := rand.New(rand.NewSource(7)) + rs := make([]Result, n) + for i := range rs { + rs[i] = Result{Pk: int64(rng.Intn(1 << 30)), Score: float32(rng.Intn(buckets))} + } + return rs +} + +// n=45688 k=10 is the observed 'the' whole-corpus stopword shape. The FastMaxHeap +// selection is O(n) with zero per-candidate allocation regardless of the score +// distribution — Distinct (varied BM25 scores) and Tied (all-identical) both stay flat, +// unlike the old bounded-min-heap that allocated a sortKey string per candidate. +func BenchmarkTopK_Distinct(b *testing.B) { + rs := benchResults(45688, 45688) + b.ResetTimer() + for i := 0; i < b.N; i++ { + topKResults(rs, 10) + } +} +func BenchmarkTopK_Tied(b *testing.B) { + rs := benchResults(45688, 1) + b.ResetTimer() + for i := 0; i < b.N; i++ { + topKResults(rs, 10) + } +} diff --git a/pkg/fulltext2/topk_test.go b/pkg/fulltext2/topk_test.go new file mode 100644 index 0000000000000..c14ac8fe4a280 --- /dev/null +++ b/pkg/fulltext2/topk_test.go @@ -0,0 +1,82 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "math" + "math/rand" + "sort" + "testing" + + "github.com/stretchr/testify/require" +) + +// TestTopKResultsValidTopK fuzzes topKResults over many sizes, k values, and score +// distributions (including heavy ties). The contract is deliberately NOT a byte-for-byte +// match of a full sort — equal scores are equally relevant and their order is +// unspecified. It IS: (1) length == min(k, n); (2) output ordered by score descending; +// (3) a *valid* top-k — no returned doc scores below any doc that was left out. That is +// exactly "correct top-k by score, arbitrary tie-break". +func TestTopKResultsValidTopK(t *testing.T) { + rng := rand.New(rand.NewSource(1)) + for _, scoreBuckets := range []int{1, 2, 5, 50, 100000} { + for _, n := range []int{0, 1, 2, 3, 7, 8, 9, 33, 200, 1000} { + for _, k := range []int{0, 1, 2, 8, 10, n - 1, n, n + 5} { + results := make([]Result, n) + for i := range results { + results[i] = Result{ + Pk: int64(rng.Intn(1 << 30)), + Score: float32(rng.Intn(scoreBuckets)), + } + } + got := topKResults(results, k) + + wantLen := k + if wantLen > n { + wantLen = n + } + if k <= 0 { + wantLen = 0 + } + require.Lenf(t, got, wantLen, "n=%d k=%d buckets=%d", n, k, scoreBuckets) + + // (2) ordered by score descending. + for i := 1; i < len(got); i++ { + require.GreaterOrEqualf(t, got[i-1].Score, got[i].Score, + "not score-desc at %d (n=%d k=%d buckets=%d)", i, n, k, scoreBuckets) + } + if len(got) == 0 { + continue + } + // (3) valid top-k: the minimum returned score is >= the k-th largest + // score overall, i.e. nothing better than a returned doc was dropped. + allScores := make([]float64, n) + for i := range results { + allScores[i] = float64(results[i].Score) + } + sort.Sort(sort.Reverse(sort.Float64Slice(allScores))) + kth := allScores[len(got)-1] // k-th largest overall (got has min(k,n) items) + minReturned := math.Inf(1) + for _, r := range got { + if float64(r.Score) < minReturned { + minReturned = float64(r.Score) + } + } + require.GreaterOrEqualf(t, minReturned, kth, + "dropped a higher-scoring doc (n=%d k=%d buckets=%d)", n, k, scoreBuckets) + } + } + } +} diff --git a/pkg/fulltext2/wand_blockmax_test.go b/pkg/fulltext2/wand_blockmax_test.go new file mode 100644 index 0000000000000..b08c650fc56c6 --- /dev/null +++ b/pkg/fulltext2/wand_blockmax_test.go @@ -0,0 +1,169 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "fmt" + "math/rand" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +// blockMaxCorpus builds a MULTI-BLOCK segment (many BlockSize=128 blocks per +// frequent term) with a Zipfian-ish vocabulary and varied doc lengths, so the +// Block-Max skip path actually fires. Deterministic (fixed seed) — no test flake. +func blockMaxCorpus(t *testing.T, nDocs int) []TokenizedDoc { + t.Helper() + rng := rand.New(rand.NewSource(0x5eed)) + // 40 words; earlier words are far more frequent (Zipf), giving long postings + // that span many blocks with skewed per-block max-tf — the shape Block-Max + // prunes best. + vocab := make([]string, 40) + for i := range vocab { + vocab[i] = fmt.Sprintf("w%02d", i) + } + pick := func() string { + // Zipf-like: index ~ floor(40 * u^3) biases hard toward small indices. + u := rng.Float64() + idx := int(float64(len(vocab)) * u * u * u) + if idx >= len(vocab) { + idx = len(vocab) - 1 + } + return vocab[idx] + } + docs := make([]TokenizedDoc, nDocs) + for i := 0; i < nDocs; i++ { + n := 3 + rng.Intn(18) // doc length 3..20 + terms := make([]string, n) + for j := 0; j < n; j++ { + terms[j] = pick() + } + docs[i] = TokenizedDoc{Pk: int64(i), Terms: terms} + } + return docs +} + +// requireSameTopK asserts the Block-Max walk returns the SAME top-k as the full +// scan: identical count, identical score multiset (score-desc, within float tol), +// and — for every doc scoring strictly ABOVE the k-th (boundary) score — an +// identical pk set. The ORDER of exactly-tied docs is NOT asserted: WAND sums a +// doc's term contributions in cursor order and the full scan in clause-map order, +// so tied scores can differ in the last ULP and sort differently, and at the k-th +// boundary a tie is broken arbitrarily (as in bm25). Those are the only permitted +// differences; a dropped/extra above-boundary doc or a wrong score fails here. +func requireSameTopK(t *testing.T, got, want []Result, ctx string) { + t.Helper() + require.Equalf(t, len(want), len(got), "%s: result count", ctx) + if len(want) == 0 { + return + } + // Score multiset: both are score-desc, so compare pairwise within tolerance. + for i := range want { + require.InDeltaf(t, want[i].Score, got[i].Score, 1e-5, "%s: score at rank %d", ctx, i) + } + // Set of docs strictly above the boundary score must match exactly (only the + // boundary tie may differ in membership). + const tol = 1e-5 + boundary := want[len(want)-1].Score + above := func(rs []Result) map[any]struct{} { + m := make(map[any]struct{}) + for _, r := range rs { + if r.Score > boundary+tol { + m[r.Pk] = struct{}{} + } + } + return m + } + require.Equalf(t, above(want), above(got), "%s: above-boundary pk set", ctx) +} + +// TestBlockMaxWANDParity is the correctness gate for the Block-Max WAND walk: over +// a large multi-block corpus it asserts searchWAND (block-max top-k) returns the +// IDENTICAL ranking as searchBooleanFull (the exhaustive scan) for many k values, +// both scorers, both build-side and serialized→loaded segments, and both bare and +// boosted (> / <) disjunctions. A block-skip that ever drops a real top-k doc +// would diverge here. +func TestBlockMaxWANDParity(t *testing.T) { + const nDocs = 3000 + docs := blockMaxCorpus(t, nDocs) + + build, err := BuildSegmentFromTokenized("bm", int32(types.T_int64), docs) + require.NoError(t, err) + require.Greater(t, int(build.N), BlockSize*4, "corpus must span many blocks") + + // The loaded variant exercises deriveTermStats on the Deserialize path. + blob, err := build.Serialize() + require.NoError(t, err) + loaded, err := Deserialize("bm", bytes.NewReader(blob)) + require.NoError(t, err) + + // Sanity: a frequent term's postings really do span several blocks (else the + // skip path is untested). w00 is the most frequent under the Zipf pick. + if pl, ok := build.lookup("w00"); ok { + require.Greater(t, len(pl.blockLastDoc), 3, "frequent term should have >3 blocks") + require.Equal(t, len(pl.blockMaxTf), len(pl.blockLastDoc)) + } else { + t.Fatal("expected w00 present") + } + + // Disjunctive queries: mixes of frequent + rare terms, 2..5 terms, some boosted. + mk := func(weights ...struct { + w string + x float64 + }) BoolQuery { + var q BoolQuery + for _, e := range weights { + q.should = append(q.should, clause{kind: clauseTerm, terms: []string{e.w}, weight: float32(e.x)}) + } + return q + } + type wq = struct { + w string + x float64 + } + queries := []BoolQuery{ + mk(wq{"w00", 1}, wq{"w01", 1}), + mk(wq{"w00", 1}, wq{"w05", 1}, wq{"w20", 1}), + mk(wq{"w02", 1}, wq{"w03", 1}, wq{"w30", 1}, wq{"w39", 1}), + mk(wq{"w00", 1.1}, wq{"w10", 0.9}, wq{"w15", 1}), // boosted / demoted + mk(wq{"w01", 1}, wq{"w02", 1}, wq{"w04", 1}, wq{"w08", 1}, wq{"w16", 1}), + mk(wq{"w38", 1}, wq{"w39", 1}), // both rare + } + ks := []int{1, 5, 20, 100, 500, nDocs} + algos := []ScoreAlgo{BM25, TfIdf} + + for _, seg := range []*Segment{build, loaded} { + tag := "build" + if seg == loaded { + tag = "loaded" + } + for _, algo := range algos { + for qi, q := range queries { + terms, ok := disjunctiveTerms(q) + require.True(t, ok, "query %d must be WAND-eligible", qi) + for _, k := range ks { + want, err := seg.searchBooleanFull(q, algo, k, nil, nil) + require.NoError(t, err) + got := seg.searchWAND(terms, algo, k, nil, nil) + requireSameTopK(t, got, want, + fmt.Sprintf("%s algo=%v q=%d k=%d", tag, algo, qi, k)) + } + } + } + } +} diff --git a/pkg/fulltext2/wand_test.go b/pkg/fulltext2/wand_test.go new file mode 100644 index 0000000000000..e57ffc5c7dd51 --- /dev/null +++ b/pkg/fulltext2/wand_test.go @@ -0,0 +1,142 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "fmt" + "strings" + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/monlp/tokenizer" + "github.com/stretchr/testify/require" +) + +// syntheticCorpus builds a deterministic corpus with varied df/tf so WAND has +// something to prune: doc i includes vocab term j when i % (j+2) == 0, repeated +// (i%3)+1 times. +func syntheticCorpus(t *testing.T) *Segment { + t.Helper() + vocab := []string{"alpha", "beta", "gamma", "delta", "epsilon"} + var docs []Doc + for i := 1; i <= 60; i++ { + var words []string + for j, w := range vocab { + if i%(j+2) == 0 { + for r := 0; r <= i%3; r++ { + words = append(words, w) + } + } + } + if len(words) == 0 { + words = []string{"filler"} + } + docs = append(docs, Doc{int64(i), []byte(strings.Join(words, " "))}) + } + s, err := BuildSegmentFromDocs("syn", int32(types.T_int64), docs, tokenizer.NewSimpleTokenizer()) + require.NoError(t, err) + return s +} + +func requireSameRanking(t *testing.T, label string, wand, full []Result) { + t.Helper() + require.Equal(t, len(full), len(wand), "%s: count", label) + // WAND only avoids work; it must return the same top-k SCORES as the full scan. Both + // results are score-descending, so the score sequence must match rank-by-rank — this + // is the sufficient correctness invariant: a pruned or mis-scored doc changes a score + // here, while a pk difference can only occur among EQUAL scores, whose identity is + // unspecified (neither heap imposes a tiebreak, matching bm25). So we compare scores, + // not pk. (1e-5 also absorbs the float-ULP score noise between the two accumulation + // orders, which can flip a near-tie's doc identity.) + for i := range full { + require.InDelta(t, full[i].Score, wand[i].Score, 1e-5, "%s: rank %d score", label, i) + } +} + +// TestWANDParity: for every disjunctive query, WAND returns the identical ranking +// (same docs, same order, same scores) as the full scan — across k and both +// scorers, on two corpora. This is the WAND correctness contract: it only avoids +// work, it never changes results. +func TestWANDParity(t *testing.T) { + type tc struct { + name string + seg *Segment + query string + } + small := fulltextCorpus(t) + syn := syntheticCorpus(t) + cases := []tc{ + {"small/2", small, "quick fox"}, + {"small/3", small, "quick fox dog"}, + {"small/weighted", small, ">quick fox alpha beta SQL NULL + require.Equal(t, int64(30), vector.GetFixedAtWithTypeCheck[int64](res.Batch.Vecs[2], 2)) + + st.free(tf, proc, false, nil) +} + +// TestFulltext2SearchCallNilOut: when start() bails before running SearchInto (e.g. a NULL +// pattern), u.out stays nil; call() must return end-of-stream, NOT nil-deref u.out.Keys. +func TestFulltext2SearchCallNilOut(t *testing.T) { + proc := testutil.NewProc(t) + st := &fulltext2SearchState{limit: 10} // u.out == nil, not streaming + st.batch = batch.NewWithSize(2) + st.batch.Vecs[0] = vector.NewVec(types.T_int64.ToType()) + st.batch.Vecs[1] = vector.NewVec(types.T_float32.ToType()) + st.pkVecIdx, st.scoreVecIdx = 0, 1 + + tf := &TableFunction{} + res, err := st.call(tf, proc) + require.NoError(t, err) + require.Equal(t, vm.CancelResult, res) + st.free(tf, proc, false, nil) +} + +// TestFulltext2SearchMaterializedAppendError drives the materialized call() drain with a +// capped mpool that is pre-filled to a few KB of headroom, so an append fails PARTWAY +// through the batch. The fix must surface that error (vm.CancelResult + err) instead of +// swallowing it and publishing a batch whose row count exceeds the rows actually appended +// to its vectors (the malformed, mismatched-length batch the pre-fix code produced). +func TestFulltext2SearchMaterializedAppendError(t *testing.T) { + // cap enforcement fires only on the offHeap alloc path, so the vectors below are marked + // offHeap and the pool is given a real (2 MiB) cap. + mp, err := mpool.NewMPool("ft2_append_fail", 2<<20, mpool.NoFixed) + require.NoError(t, err) + proc := testutil.NewProcessWithMPool(t, "", mp) + + st := &fulltext2SearchState{limit: 10} + st.batch = batch.NewWithSize(2) + st.batch.Vecs[0] = vector.NewVec(types.T_int64.ToType()) + st.batch.Vecs[1] = vector.NewVec(types.T_float32.ToType()) + st.pkVecIdx, st.scoreVecIdx = 0, 1 // non-covered [doc_id, score] layout (start() builds this from Attrs) + st.batch.Vecs[0].SetOffHeap(true) // route data allocations through the cap-enforced path + st.batch.Vecs[1].SetOffHeap(true) + + // Far more keys than one call()'s 8192 window, and their append demand dwarfs the + // headroom left below, so the drain loop hits an allocation failure mid-batch. + n := 20000 + keysBuf := &vectorindex.ColumnBuffer{Type: types.T_int64} + dists := make([]float32, n) + var kb [8]byte + for i := 0; i < n; i++ { + binary.LittleEndian.PutUint64(kb[:], uint64(i)) + keysBuf.Data = append(keysBuf.Data, kb[:]...) + keysBuf.N++ + dists[i] = float32(i) + } + st.out = &vectorindex.SearchOutput{Keys: keysBuf, Dists: dists} + + // Consume the pool down to ~4 KB of headroom (offHeap → cap is enforced), so the first + // call()'s appends succeed for a while, then fail — exercising the mid-loop error. + fill, err := mp.Alloc(int(mp.Cap()-mp.CurrNB()-4096), true) + require.NoError(t, err) + defer mp.Free(fill) + + tf := &TableFunction{} + res, err := st.call(tf, proc) + require.Error(t, err, "an append allocation failure must propagate, not be swallowed") + require.Equal(t, vm.CancelResult, res) + + // The batch must never claim more rows than were actually appended to EITHER vector — + // SetRowCount is skipped on the error path, so the count stays 0 and the invariant holds + // (pre-fix, RowCount could be n while a vector held fewer rows). + require.LessOrEqual(t, st.batch.RowCount(), st.batch.Vecs[0].Length()) + require.LessOrEqual(t, st.batch.RowCount(), st.batch.Vecs[1].Length()) + + st.free(tf, proc, false, nil) +} + +// TestFulltext2SearchCallStreaming drives the streaming call() path: batches arriving on +// streamCh are decoded via the box-free ColumnBuffer, and channel close surfaces errCh. +func TestFulltext2SearchCallStreaming(t *testing.T) { + mp := mpool.MustNewZero() + proc := testutil.NewProcessWithMPool(t, "", mp) + + newState := func() *fulltext2SearchState { + st := &fulltext2SearchState{streaming: true} + st.batch = batch.NewWithSize(2) + st.batch.Vecs[0] = vector.NewVec(types.T_int64.ToType()) + st.batch.Vecs[1] = vector.NewVec(types.T_float32.ToType()) + st.pkVecIdx, st.scoreVecIdx = 0, 1 // non-covered [doc_id, score] layout (start() builds this from Attrs) + st.streamCh = make(chan *vectorindex.SearchOutput, 4) + st.errCh = make(chan error, 1) + return st + } + tf := &TableFunction{} + + // happy path: one batch then a clean (nil-error) close. + { + st := newState() + st.streamCh <- &vectorindex.SearchOutput{Keys: ft2Int64ColumnBuffer(7, 8), Dists: []float32{1.5, 2.5}} + close(st.streamCh) + st.errCh <- nil + + res, err := st.call(tf, proc) + require.NoError(t, err) + require.Equal(t, vm.ExecNext, res.Status) + require.Equal(t, 2, res.Batch.RowCount()) + require.Equal(t, int64(8), vector.GetFixedAtWithTypeCheck[int64](res.Batch.Vecs[0], 1)) + require.Equal(t, float32(1.5), vector.GetFixedAtWithTypeCheck[float32](res.Batch.Vecs[1], 0)) + + res, err = st.call(tf, proc) + require.NoError(t, err) + require.Equal(t, vm.CancelResult, res) + require.True(t, st.done) + + // once done, further calls short-circuit. + res, err = st.call(tf, proc) + require.NoError(t, err) + require.Equal(t, vm.CancelResult, res) + } + + // error path: producer reports a search error on close. + { + st := newState() + close(st.streamCh) + st.errCh <- moerr.NewInternalErrorNoCtx("search failed") + res, err := st.call(tf, proc) + require.Error(t, err) + require.Equal(t, vm.CancelResult, res) + } +} + +// TestFulltext2SearchCallStreamingCovered drives the COVERED streaming path: a batch with +// an extra INCLUDE output vector, whose values arrive column-major (one nullable +// ColumnBuffer per include col) and are bulk-decoded box-free via AppendColumnBuffer, +// preserving NULLs. Mirrors what start() builds for `SELECT id, tag ... covered`. +func TestFulltext2SearchCallStreamingCovered(t *testing.T) { + mp := mpool.MustNewZero() + proc := testutil.NewProcessWithMPool(t, "", mp) + + st := &fulltext2SearchState{streaming: true} + st.batch = batch.NewWithSize(3) + st.batch.Attrs = []string{"doc_id", "score", "tag"} + st.batch.Vecs[0] = vector.NewVec(types.T_int64.ToType()) + st.batch.Vecs[1] = vector.NewVec(types.T_float32.ToType()) + st.batch.Vecs[2] = vector.NewVec(types.T_varchar.ToType()) + st.pkVecIdx, st.scoreVecIdx = 0, 1 + st.includeOut = []ft2IncludeOut{{vecIdx: 2, segPos: 0, name: "tag"}} + st.streamCh = make(chan *vectorindex.SearchOutput, 4) + st.errCh = make(chan error, 1) + + // One include col ("tag" varchar): ["x", NULL] — a well-formed [u32 len][content] entry + // plus a [u32 0] placeholder with Nulls[1]=true (the producer's cbAppendNull layout). + tag := &vectorindex.ColumnBuffer{Type: types.T_varchar} + tag.Data = append(tag.Data, 1, 0, 0, 0, 'x') // len=1, "x" + tag.Data = append(tag.Data, 0, 0, 0, 0) // len=0 (NULL placeholder) + tag.N = 2 + tag.Nulls = []bool{false, true} + + st.streamCh <- &vectorindex.SearchOutput{Keys: ft2Int64ColumnBuffer(7, 8), Dists: []float32{1.5, 2.5}, Include: []*vectorindex.ColumnBuffer{tag}} + close(st.streamCh) + st.errCh <- nil + + tf := &TableFunction{} + res, err := st.call(tf, proc) + require.NoError(t, err) + require.Equal(t, vm.ExecNext, res.Status) + require.Equal(t, 2, res.Batch.RowCount()) + require.Equal(t, int64(7), vector.GetFixedAtWithTypeCheck[int64](res.Batch.Vecs[0], 0)) + require.Equal(t, float32(2.5), vector.GetFixedAtWithTypeCheck[float32](res.Batch.Vecs[1], 1)) + require.Equal(t, "x", res.Batch.Vecs[2].GetStringAt(0)) + require.False(t, res.Batch.Vecs[2].IsNull(0)) + require.True(t, res.Batch.Vecs[2].IsNull(1)) // NULL include value surfaces as SQL NULL + + st.free(tf, proc, false, nil) +} + +// TestFulltext2SearchStopStreamDrains verifies stopStream cancels the producer context +// and drains+recycles any buffered batch (no goroutine/buffer leak). +func TestFulltext2SearchStopStreamDrains(t *testing.T) { + st := &fulltext2SearchState{streaming: true} + _, cancel := context.WithCancel(context.Background()) + st.cancel = cancel + st.streamCh = make(chan *vectorindex.SearchOutput, 4) + st.streamCh <- &vectorindex.SearchOutput{Keys: ft2Int64ColumnBuffer(1), Dists: []float32{1}} + close(st.streamCh) + + st.stopStream() + require.Nil(t, st.cancel) + require.Nil(t, st.streamCh) + + // idempotent. + st.stopStream() +} diff --git a/pkg/sql/colexec/table_function/hnsw_create_test.go b/pkg/sql/colexec/table_function/hnsw_create_test.go index 2d3467605c8cb..36e871ba0c9af 100644 --- a/pkg/sql/colexec/table_function/hnsw_create_test.go +++ b/pkg/sql/colexec/table_function/hnsw_create_test.go @@ -17,6 +17,7 @@ package table_function import ( "fmt" "os" + "strings" "testing" "github.com/matrixorigin/matrixone/pkg/common/mpool" @@ -552,3 +553,67 @@ func makeBatchHnswCreateFail(proc *process.Process) []failBatch { } return failBatches } + +// TestHnswCreatePrepareParsesConfig covers the zero-doc enabler: Prepare const-folds the +// IndexTableConfig (arg 0) so end() has the table names even when start() never runs (empty source). +func TestHnswCreatePrepareParsesConfig(t *testing.T) { + hnsw_runSql = mock_hnsw_runSql + ut := newHnswCreateTestCase(t, mpool.MustNewZero(), hnswcreatedefaultAttrs, "{\"op_type\": \"vector_l2_ops\"}") + ut.arg.Args = makeConstInputExprsHnswCreate() + + require.Nil(t, ut.arg.Prepare(ut.proc)) + + st := ut.arg.ctr.state.(*hnswCreateState) + require.Equal(t, "db", st.tblcfg.DbName) + require.Equal(t, "__metadata", st.tblcfg.MetadataTable) + require.Equal(t, "__index", st.tblcfg.IndexTable) +} + +// TestHnswCreateEndZeroDocClears covers the zero-doc rebuild: with no builder created (start()/call() +// never ran, as on an empty source) end() must STILL clear both hidden tables — via DELETE ... WHERE +// TRUE (an in-place delete, not a TRUNCATE that would swap the physical table id). +func TestHnswCreateEndZeroDocClears(t *testing.T) { + var captured []string + old := hnsw_runSql + defer func() { hnsw_runSql = old }() + hnsw_runSql = func(sqlproc *sqlexec.SqlProcess, sql string) (executor.Result, error) { + captured = append(captured, sql) + return executor.Result{Mp: sqlproc.Proc.Mp(), Batches: []*batch.Batch{}}, nil + } + + ut := newHnswCreateTestCase(t, mpool.MustNewZero(), hnswcreatedefaultAttrs, "{\"op_type\": \"vector_l2_ops\"}") + ut.arg.Args = makeConstInputExprsHnswCreate() + require.Nil(t, ut.arg.Prepare(ut.proc)) + + // end() WITHOUT start()/call() → buildf32/buildf64 stay nil (zero-doc). + st := ut.arg.ctr.state.(*hnswCreateState) + require.Nil(t, st.end(ut.arg, ut.proc)) + + var deletes []string + for _, s := range captured { + if strings.Contains(s, "DELETE FROM") { + deletes = append(deletes, s) + } + } + require.Len(t, deletes, 2, "zero-doc rebuild must still clear metadata + storage") + for _, d := range deletes { + require.Contains(t, d, "WHERE TRUE", "clear must be an in-place delete, not a TRUNCATE") + } +} + +// TestHnswCreateEndNoConfigSkips covers the guard: an uninitialized state (no table names — e.g. the +// const-fold parse failed) must skip the clear rather than emit an invalid "DELETE FROM " with an +// empty identifier. +func TestHnswCreateEndNoConfigSkips(t *testing.T) { + var called bool + old := hnsw_runSql + defer func() { hnsw_runSql = old }() + hnsw_runSql = func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + called = true + return executor.Result{}, nil + } + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + st := &hnswCreateState{} // empty tblcfg + require.Nil(t, st.end(&TableFunction{}, proc)) + require.False(t, called, "no SQL must run without a valid table config") +} diff --git a/pkg/sql/colexec/table_function/hnsw_search_test.go b/pkg/sql/colexec/table_function/hnsw_search_test.go index 8eb5b7aa5a4ef..6647ecbe6c62e 100644 --- a/pkg/sql/colexec/table_function/hnsw_search_test.go +++ b/pkg/sql/colexec/table_function/hnsw_search_test.go @@ -27,7 +27,6 @@ import ( plan2 "github.com/matrixorigin/matrixone/pkg/sql/plan" "github.com/matrixorigin/matrixone/pkg/testutil" "github.com/matrixorigin/matrixone/pkg/vectorindex" - "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" veccache "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" "github.com/matrixorigin/matrixone/pkg/vm" @@ -118,10 +117,6 @@ func (m *MockSearch) SearchFloat32(sqlproc *sqlexec.SqlProcess, query any, rt ve return nil } -func (m *MockSearch) UpdateConfig(newalgo cache.VectorIndexSearchIf) error { - return nil -} - func newMockAlgoFn(idxcfg vectorindex.IndexConfig, tblcfg vectorindex.IndexTableConfig) veccache.VectorIndexSearchIf { return &MockSearch{Idxcfg: idxcfg, Tblcfg: tblcfg} } @@ -478,3 +473,7 @@ func TestNewHnswAlgoFn(t *testing.T) { idxcfg.Usearch.Quantization = usearch.I8 assert.Panics(t, func() { newHnswAlgoFn(idxcfg, tblcfg) }, "panic") } + +func (m *MockSearch) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return nil +} diff --git a/pkg/sql/colexec/table_function/ivf_search_test.go b/pkg/sql/colexec/table_function/ivf_search_test.go index be6b811a643f3..08ea2f6158a3d 100644 --- a/pkg/sql/colexec/table_function/ivf_search_test.go +++ b/pkg/sql/colexec/table_function/ivf_search_test.go @@ -163,10 +163,6 @@ func (m *MockIvfSearch[T]) SearchFloat32(sqlproc *sqlexec.SqlProcess, query any, return nil } -func (m *MockIvfSearch[T]) UpdateConfig(newalgo cache.VectorIndexSearchIf) error { - return nil -} - func newMockIvfAlgoFn(idxcfg vectorindex.IndexConfig, tblcfg vectorindex.IndexTableConfig) (cache.VectorIndexSearchIf, error) { return &MockIvfSearch[float32]{Idxcfg: idxcfg, Tblcfg: tblcfg}, nil } @@ -1311,3 +1307,7 @@ func makeBatchIvfSearchFail(proc *process.Process) []failBatch { } return failBatches } + +func (m *MockIvfSearch[T]) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return nil +} diff --git a/pkg/sql/compile/ddl_drop_plugin_index_test.go b/pkg/sql/compile/ddl_drop_plugin_index_test.go index 829222a8a9c0d..35134d1f4ded4 100644 --- a/pkg/sql/compile/ddl_drop_plugin_index_test.go +++ b/pkg/sql/compile/ddl_drop_plugin_index_test.go @@ -39,9 +39,11 @@ func (s *stubIndexSearch) Search(*sqlexec.SqlProcess, any, vectorindex.RuntimeCo func (s *stubIndexSearch) SearchFloat32(*sqlexec.SqlProcess, any, vectorindex.RuntimeConfig, []int64, []float32) error { return nil } -func (s *stubIndexSearch) Load(*sqlexec.SqlProcess) error { return nil } -func (s *stubIndexSearch) UpdateConfig(veccache.VectorIndexSearchIf) error { return nil } -func (s *stubIndexSearch) Destroy() { s.destroyed = true } +func (s *stubIndexSearch) SearchInto(*sqlexec.SqlProcess, any, vectorindex.RuntimeConfig, *vectorindex.SearchOutput) error { + return nil +} +func (s *stubIndexSearch) Load(*sqlexec.SqlProcess) error { return nil } +func (s *stubIndexSearch) Destroy() { s.destroyed = true } // seedCachedIndex puts an entry in the vector-index cache under key, as a // completed search would, and returns the stub so the test can see Destroy(). diff --git a/pkg/sql/parsers/dialect/mysql/keyword_policy_test.go b/pkg/sql/parsers/dialect/mysql/keyword_policy_test.go index b624545893a3a..8fa9536ea3887 100644 --- a/pkg/sql/parsers/dialect/mysql/keyword_policy_test.go +++ b/pkg/sql/parsers/dialect/mysql/keyword_policy_test.go @@ -45,6 +45,7 @@ var legacyKeywordPolicyExceptions = map[string]string{ "escape": "legacy parser behavior before incremental keyword policy", "force_sync": "legacy parser behavior before incremental keyword policy", "full": "legacy parser behavior before incremental keyword policy", + "fulltext2": "fulltext2 index-type keyword (CREATE FULLTEXT2 / REINDEX ... FULLTEXT2); non_reserved_keyword introduces a grammar conflict, so it stays contextual", "ilike": "legacy parser behavior before incremental keyword policy", "intersect": "legacy parser behavior before incremental keyword policy", "minus": "legacy parser behavior before incremental keyword policy", diff --git a/pkg/sql/parsers/dialect/mysql/mysql_sql_test.go b/pkg/sql/parsers/dialect/mysql/mysql_sql_test.go index c645502d09f9a..8502be5be9470 100644 --- a/pkg/sql/parsers/dialect/mysql/mysql_sql_test.go +++ b/pkg/sql/parsers/dialect/mysql/mysql_sql_test.go @@ -2781,6 +2781,12 @@ var ( }, { input: "create index idx using ivfpq on A (a) LISTS 8 kmeans_train_percent 7 max_index_capacity 2000", output: "create index idx using ivfpq on a (a) LISTS 8 KMEANS_TRAIN_PERCENT 7 MAX_INDEX_CAPACITY 2000 ", + }, { + input: "create fulltext2 index idx on A (a) max_index_capacity 500000 max_postings_capacity 8000000", + output: "create fulltext2 index idx on a (a) MAX_INDEX_CAPACITY 500000 MAX_POSTINGS_CAPACITY 8000000 ", + }, { + input: "alter table t1 alter reindex idx1 fulltext2 max_postings_capacity = 4000000", + output: "alter table t1 alter reindex idx1 fulltext2 max_postings_capacity = 4000000", }, { input: "create index idx1 on a (a)", }, { diff --git a/pkg/sql/parsers/tree/fulltext_match_test.go b/pkg/sql/parsers/tree/fulltext_match_test.go new file mode 100644 index 0000000000000..7d39b4a4b38ff --- /dev/null +++ b/pkg/sql/parsers/tree/fulltext_match_test.go @@ -0,0 +1,56 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tree + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/matrixorigin/matrixone/pkg/sql/parsers/dialect" +) + +func TestFullTextMatchFuncExpressionFormat(t *testing.T) { + cols := []*KeyPart{{ColName: NewUnresolvedColName("body")}} + + // classic MATCH deparses as MATCH and carries its mode. The pattern is an Expr + // (a string literal here; #24796 made it an expression so prepared '?' works). + ft, err := NewFullTextMatchFuncExpression(cols, NewNumVal("apple", "apple", false, P_char), FULLTEXT_BOOLEAN) + require.NoError(t, err) + + // The string pattern is emitted as a single-quoted, escaped SQL string literal + // UNCONDITIONALLY so the deparsed MATCH re-parses (view expansion / CTAS) — #24823. + ctx := NewFmtCtx(dialect.MYSQL, WithQuoteString(true)) + ft.Format(ctx) + require.Equal(t, `MATCH (body) AGAINST ('apple' IN BOOLEAN MODE)`, ctx.String()) + + // default mode omits the mode clause. + def, _ := NewFullTextMatchFuncExpression(cols, NewNumVal("apple", "apple", false, P_char), FULLTEXT_DEFAULT) + ctx2 := NewFmtCtx(dialect.MYSQL, WithQuoteString(true)) + def.Format(ctx2) + require.Equal(t, `MATCH (body) AGAINST ('apple')`, ctx2.String()) +} + +func TestFullTextMatchExprValid(t *testing.T) { + cols := []*KeyPart{{ColName: NewUnresolvedColName("body")}} + + // empty column list is rejected + _, err := NewFullTextMatchFuncExpression(nil, NewNumVal("apple", "apple", false, P_char), FULLTEXT_DEFAULT) + require.Error(t, err) + + // empty pattern is rejected + _, err = NewFullTextMatchFuncExpression(cols, NewNumVal("", "", false, P_char), FULLTEXT_DEFAULT) + require.Error(t, err) +} diff --git a/pkg/sql/plan/apply_indices_fulltext2_test.go b/pkg/sql/plan/apply_indices_fulltext2_test.go new file mode 100644 index 0000000000000..de54dc8bf1d0f --- /dev/null +++ b/pkg/sql/plan/apply_indices_fulltext2_test.go @@ -0,0 +1,107 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plan + +import ( + "encoding/json" + "testing" + + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" + "github.com/stretchr/testify/require" +) + +func TestFulltext2ParserFromParams(t *testing.T) { + require.Equal(t, "", fulltext2ParserFromParams("")) + require.Equal(t, "ngram", fulltext2ParserFromParams(`{"parser":"ngram"}`)) + require.Equal(t, "gojieba", fulltext2ParserFromParams(`{"parser":"gojieba","position_free":"true"}`)) + require.Equal(t, "", fulltext2ParserFromParams(`{no json`)) // malformed → default + require.Equal(t, "", fulltext2ParserFromParams(`{"other":"x"}`)) +} + +func TestFulltext2PositionFreeFromParams(t *testing.T) { + require.False(t, fulltext2PositionFreeFromParams("")) + require.True(t, fulltext2PositionFreeFromParams(`{"position_free":"true"}`)) + require.False(t, fulltext2PositionFreeFromParams(`{"position_free":"false"}`)) + require.False(t, fulltext2PositionFreeFromParams(`{bad json`)) + require.False(t, fulltext2PositionFreeFromParams(`{"parser":"ngram"}`)) +} + +func ft2ScanNode(indexName, storeTbl, metaTbl string) *plan.Node { + return &plan.Node{ + ObjRef: &plan.ObjectRef{SchemaName: "db"}, + TableDef: &plan.TableDef{ + Indexes: []*plan.IndexDef{ + {IndexName: indexName, IndexAlgoTableType: catalog.FullText2Index_TblType_Storage, IndexTableName: storeTbl}, + {IndexName: indexName, IndexAlgoTableType: catalog.FullText2Index_TblType_Metadata, IndexTableName: metaTbl}, + }, + }, + } +} + +func TestFindFulltext2IndexTables(t *testing.T) { + b := &QueryBuilder{} + node := ft2ScanNode("ft2idx", "__store", "__meta") + idxdef := &plan.IndexDef{IndexName: "ft2idx"} + + store, meta, ok := b.findFulltext2IndexTables(node, idxdef) + require.True(t, ok) + require.Equal(t, "__store", store) + require.Equal(t, "__meta", meta) + + // nil guards. + _, _, ok = b.findFulltext2IndexTables(nil, idxdef) + require.False(t, ok) + _, _, ok = b.findFulltext2IndexTables(node, nil) + require.False(t, ok) + + // only the storage sibling present → incomplete → ok=false. + partial := &plan.Node{TableDef: &plan.TableDef{Indexes: []*plan.IndexDef{ + {IndexName: "ft2idx", IndexAlgoTableType: catalog.FullText2Index_TblType_Storage, IndexTableName: "__store"}, + }}} + _, _, ok = b.findFulltext2IndexTables(partial, idxdef) + require.False(t, ok) +} + +func TestBuildFulltext2SearchCfg(t *testing.T) { + b := NewQueryBuilder(plan.Query_SELECT, NewMockCompilerContext(true), false, true) + node := ft2ScanNode("ft2idx", "__store", "__meta") + + // positional index, NL mode → cfg JSON with parser. + idxdef := &plan.IndexDef{IndexName: "ft2idx", IndexAlgoParams: `{"parser":"ngram"}`} + cfg, err := b.buildFulltext2SearchCfg(node, idxdef, int64(tree.FULLTEXT_NL)) + require.NoError(t, err) + var m map[string]string + require.NoError(t, json.Unmarshal([]byte(cfg), &m)) + require.Equal(t, "db", m["db"]) + require.Equal(t, "__store", m["index"]) + require.Equal(t, "__meta", m["metadata"]) + require.Equal(t, "ngram", m["parser"]) + + // tables not found → error. + _, err = b.buildFulltext2SearchCfg(node, &plan.IndexDef{IndexName: "missing"}, int64(tree.FULLTEXT_NL)) + require.ErrorContains(t, err, "not found") + + // POSITION_FREE index + non-BM25 mode → rejected. + pf := &plan.IndexDef{IndexName: "ft2idx", IndexAlgoParams: `{"position_free":"true"}`} + _, err = b.buildFulltext2SearchCfg(node, pf, int64(tree.FULLTEXT_NL)) + require.ErrorContains(t, err, "POSITION_FREE") + + // POSITION_FREE index + BM25 mode → allowed. + cfg, err = b.buildFulltext2SearchCfg(node, pf, int64(tree.FULLTEXT_BM25)) + require.NoError(t, err) + require.Contains(t, cfg, "__store") +} diff --git a/pkg/sql/plan/filter_predicate_test.go b/pkg/sql/plan/filter_predicate_test.go index 6b6ebb686cf7e..781cfe869bc24 100644 --- a/pkg/sql/plan/filter_predicate_test.go +++ b/pkg/sql/plan/filter_predicate_test.go @@ -89,7 +89,7 @@ func fnExpr(name string, args ...*plan.Expr) *plan.Expr { // Tests ------------------------------------------------------------------- func TestBuildFilterPredicateJSON_NoFilters(t *testing.T) { - js, ser, res, err := buildFilterPredicateJSON(nil, newFilterTestScanNode(), []string{"price", "cat"}, "") + js, ser, res, err := buildFilterPredicateJSON(nil, newFilterTestScanNode(), []string{"price", "cat"}, "", false) require.NoError(t, err) require.Equal(t, "", js) require.Empty(t, ser) @@ -98,7 +98,7 @@ func TestBuildFilterPredicateJSON_NoFilters(t *testing.T) { func TestBuildFilterPredicateJSON_NoIncludeColumns(t *testing.T) { filters := []*plan.Expr{fnExpr("=", colExpr("price", 1, types.T_float32), f32Lit(5))} - js, ser, res, err := buildFilterPredicateJSON(filters, newFilterTestScanNode(), nil, "") + js, ser, res, err := buildFilterPredicateJSON(filters, newFilterTestScanNode(), nil, "", false) require.NoError(t, err) require.Equal(t, "", js) require.Empty(t, ser) @@ -107,7 +107,7 @@ func TestBuildFilterPredicateJSON_NoIncludeColumns(t *testing.T) { func TestBuildFilterPredicateJSON_NilScanNode(t *testing.T) { filters := []*plan.Expr{fnExpr("=", colExpr("price", 1, types.T_float32), f32Lit(5))} - js, ser, res, err := buildFilterPredicateJSON(filters, nil, []string{"price"}, "") + js, ser, res, err := buildFilterPredicateJSON(filters, nil, []string{"price"}, "", false) require.NoError(t, err) require.Equal(t, "", js) require.Empty(t, ser) @@ -132,7 +132,7 @@ func TestBuildFilterPredicateJSON_AllComparisonOps(t *testing.T) { for _, tc := range cases { t.Run(tc.op, func(t *testing.T) { filters := []*plan.Expr{fnExpr(tc.op, colExpr("price", 1, types.T_float32), i64Lit(5))} - js, ser, res, err := buildFilterPredicateJSON(filters, scan, []string{"price", "cat"}, "") + js, ser, res, err := buildFilterPredicateJSON(filters, scan, []string{"price", "cat"}, "", false) require.NoError(t, err) require.JSONEq(t, tc.wantStr, js, "op=%s", tc.op) require.Len(t, ser, 1) @@ -145,7 +145,7 @@ func TestBuildFilterPredicateJSON_FlippedComparison(t *testing.T) { // 5 < price → price > 5 (op flipped, column on left in the JSON) scan := newFilterTestScanNode() filters := []*plan.Expr{fnExpr("<", i64Lit(5), colExpr("price", 1, types.T_float32))} - js, ser, res, err := buildFilterPredicateJSON(filters, scan, []string{"price"}, "") + js, ser, res, err := buildFilterPredicateJSON(filters, scan, []string{"price"}, "", false) require.NoError(t, err) require.JSONEq(t, `[{"col":0,"op":">","val":5}]`, js) require.Len(t, ser, 1) @@ -159,7 +159,7 @@ func TestBuildFilterPredicateJSON_AndDecomposition(t *testing.T) { right := fnExpr("=", colExpr("cat", 2, types.T_int64), i64Lit(10)) andExpr := fnExpr("and", left, right) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{andExpr}, scan, []string{"price", "cat"}, "") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{andExpr}, scan, []string{"price", "cat"}, "", false) require.NoError(t, err) require.JSONEq(t, `[{"col":0,"op":">=","val":5},{"col":1,"op":"=","val":10}]`, js) @@ -176,7 +176,7 @@ func TestBuildFilterPredicateJSON_AndWithUnserializableArm(t *testing.T) { bad := fnExpr("=", colExpr("other", 3, types.T_int64), i64Lit(7)) andExpr := fnExpr("and", good, bad) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{andExpr}, scan, []string{"price", "cat"}, "") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{andExpr}, scan, []string{"price", "cat"}, "", false) require.NoError(t, err) require.Equal(t, "", js) require.Empty(t, ser) @@ -186,7 +186,7 @@ func TestBuildFilterPredicateJSON_AndWithUnserializableArm(t *testing.T) { func TestBuildFilterPredicateJSON_Between(t *testing.T) { scan := newFilterTestScanNode() bw := fnExpr("between", colExpr("price", 1, types.T_float32), i64Lit(1), i64Lit(10)) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{bw}, scan, []string{"price"}, "") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{bw}, scan, []string{"price"}, "", false) require.NoError(t, err) require.JSONEq(t, `[{"col":0,"op":"between","lo":1,"hi":10}]`, js) require.Len(t, ser, 1) @@ -199,13 +199,65 @@ func TestBuildFilterPredicateJSON_InWithFlatArgs(t *testing.T) { in := fnExpr("in", colExpr("cat", 2, types.T_int64), i64Lit(100), i64Lit(200), i64Lit(300)) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{in}, scan, []string{"price", "cat"}, "") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{in}, scan, []string{"price", "cat"}, "", false) require.NoError(t, err) require.JSONEq(t, `[{"col":1,"op":"in","vals":[100,200,300]}]`, js) require.Len(t, ser, 1) require.Empty(t, res) } +// TestBuildFilterPredicateJSON_StringPeel: fulltext2 (allowStrings=true) peels VARCHAR +// predicates — =, IN, and prefix_eq (a pure-prefix LIKE) — because it stores the actual +// value and byte-compares it (== MO's byte-exact varchar compare). The vector plugins +// (allowStrings=false) hash strings, so the SAME predicates stay residual. +func TestBuildFilterPredicateJSON_StringPeel(t *testing.T) { + scan := &plan.Node{ + BindingTags: []int32{testScanTag}, + TableDef: &plan.TableDef{Name: "docs", Cols: []*plan.ColDef{ + {Name: "id", Typ: plan.Type{Id: int32(types.T_int64)}}, + {Name: "status", Typ: plan.Type{Id: int32(types.T_varchar)}}, + }}, + } + inc := []string{"status"} + + eq := fnExpr("=", colExpr("status", 1, types.T_varchar), sLit("active")) + in := fnExpr("in", colExpr("status", 1, types.T_varchar), sLit("active"), sLit("archived")) + pe := fnExpr("prefix_eq", colExpr("status", 1, types.T_varchar), sLit("act")) + + // allowStrings=true: all three peel into the TVF predicate JSON. + for _, tc := range []struct { + name string + expr *plan.Expr + want string + }{ + {"eq", eq, `[{"col":0,"op":"=","val":"active"}]`}, + {"in", in, `[{"col":0,"op":"in","vals":["active","archived"]}]`}, + {"prefix", pe, `[{"col":0,"op":"prefix","val":"act"}]`}, + } { + t.Run(tc.name+"_peeled", func(t *testing.T) { + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{tc.expr}, scan, inc, "", true) + require.NoError(t, err) + require.JSONEq(t, tc.want, js) + require.Len(t, ser, 1) + require.Empty(t, res) + }) + } + + // allowStrings=false (vector plugins): every string predicate stays residual. + for _, tc := range []struct { + name string + expr *plan.Expr + }{{"eq", eq}, {"in", in}, {"prefix", pe}} { + t.Run(tc.name+"_residual_when_hashed", func(t *testing.T) { + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{tc.expr}, scan, inc, "", false) + require.NoError(t, err) + require.Empty(t, js) + require.Empty(t, ser) + require.Equal(t, []*plan.Expr{tc.expr}, res) + }) + } +} + func TestBuildFilterPredicateJSON_InWithExprList(t *testing.T) { // IN with the (col, Expr_List{...}) shape some planner phases produce. scan := newFilterTestScanNode() @@ -216,7 +268,7 @@ func TestBuildFilterPredicateJSON_InWithExprList(t *testing.T) { }}}, } in := fnExpr("in", colExpr("cat", 2, types.T_int64), listExpr) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{in}, scan, []string{"price", "cat"}, "") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{in}, scan, []string{"price", "cat"}, "", false) require.NoError(t, err) require.JSONEq(t, `[{"col":1,"op":"in","vals":[1,2]}]`, js) require.Len(t, ser, 1) @@ -237,7 +289,7 @@ func TestBuildFilterPredicateJSON_IsNullVariants(t *testing.T) { for _, tc := range cases { t.Run(tc.fnName, func(t *testing.T) { f := fnExpr(tc.fnName, colExpr("price", 1, types.T_float32)) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, []string{"price"}, "") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, []string{"price"}, "", false) require.NoError(t, err) require.JSONEq(t, `[{"col":0,"op":"`+tc.wantOp+`"}]`, js) require.Len(t, ser, 1) @@ -256,7 +308,7 @@ func TestBuildFilterPredicateJSON_MixedIncludeAndResidual(t *testing.T) { js, ser, res, err := buildFilterPredicateJSON( []*plan.Expr{peelable1, residualOne, peelable2}, - scan, []string{"price", "cat"}, "") + scan, []string{"price", "cat"}, "", false) require.NoError(t, err) require.JSONEq(t, `[{"col":0,"op":"<=","val":100},{"col":1,"op":"=","val":5}]`, js) @@ -270,7 +322,7 @@ func TestBuildFilterPredicateJSON_StringLiteralFallsThrough(t *testing.T) { // interpret against a hashed column. scan := newFilterTestScanNode() f := fnExpr("=", colExpr("price", 1, types.T_float32), sLit("xyz")) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, []string{"price"}, "") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, []string{"price"}, "", false) require.NoError(t, err) require.Equal(t, "", js) require.Empty(t, ser) @@ -281,7 +333,7 @@ func TestBuildFilterPredicateJSON_UnsupportedOpFallsThrough(t *testing.T) { // LIKE isn't on the C++ op_from_string list — stays residual. scan := newFilterTestScanNode() f := fnExpr("like", colExpr("price", 1, types.T_float32), sLit("5%")) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, []string{"price"}, "") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, []string{"price"}, "", false) require.NoError(t, err) require.Equal(t, "", js) require.Empty(t, ser) @@ -292,7 +344,7 @@ func TestBuildFilterPredicateJSON_ColumnNotInIncludeList(t *testing.T) { // price is INCLUDE but the predicate references "other" which is not. scan := newFilterTestScanNode() f := fnExpr("=", colExpr("other", 3, types.T_int64), i64Lit(7)) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, []string{"price"}, "") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, []string{"price"}, "", false) require.NoError(t, err) require.Equal(t, "", js) require.Empty(t, ser) @@ -306,7 +358,7 @@ func TestBuildFilterPredicateJSON_PKComparison(t *testing.T) { // emitting col=-1 (sentinel that wraps to kHostIdColIdx on the C++ side). scan := newFilterTestScanNode() f := fnExpr(">=", colExpr("id", 0, types.T_int64), i64Lit(100)) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, nil, "id") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, nil, "id", false) require.NoError(t, err) require.JSONEq(t, `[{"col":-1,"op":">=","val":100}]`, js) require.Len(t, ser, 1) @@ -318,7 +370,7 @@ func TestBuildFilterPredicateJSON_PKIn(t *testing.T) { scan := newFilterTestScanNode() in := fnExpr("in", colExpr("id", 0, types.T_int64), i64Lit(1), i64Lit(2), i64Lit(3)) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{in}, scan, nil, "id") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{in}, scan, nil, "id", false) require.NoError(t, err) require.JSONEq(t, `[{"col":-1,"op":"in","vals":[1,2,3]}]`, js) require.Len(t, ser, 1) @@ -328,7 +380,7 @@ func TestBuildFilterPredicateJSON_PKIn(t *testing.T) { func TestBuildFilterPredicateJSON_PKBetween(t *testing.T) { scan := newFilterTestScanNode() bw := fnExpr("between", colExpr("id", 0, types.T_int64), i64Lit(10), i64Lit(20)) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{bw}, scan, nil, "id") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{bw}, scan, nil, "id", false) require.NoError(t, err) require.JSONEq(t, `[{"col":-1,"op":"between","lo":10,"hi":20}]`, js) require.Len(t, ser, 1) @@ -341,7 +393,7 @@ func TestBuildFilterPredicateJSON_PKAndIncludeMixed(t *testing.T) { pkF := fnExpr("=", colExpr("id", 0, types.T_int64), i64Lit(50)) incF := fnExpr("<", colExpr("price", 1, types.T_float32), i64Lit(10)) js, ser, res, err := buildFilterPredicateJSON( - []*plan.Expr{pkF, incF}, scan, []string{"price", "cat"}, "id") + []*plan.Expr{pkF, incF}, scan, []string{"price", "cat"}, "id", false) require.NoError(t, err) require.JSONEq(t, `[{"col":-1,"op":"=","val":50},{"col":0,"op":"<","val":10}]`, js) @@ -355,7 +407,7 @@ func TestBuildFilterPredicateJSON_PKDisabledWhenNameEmpty(t *testing.T) { scan := newFilterTestScanNode() f := fnExpr("=", colExpr("id", 0, types.T_int64), i64Lit(50)) js, ser, res, err := buildFilterPredicateJSON( - []*plan.Expr{f}, scan, []string{"price"}, "") + []*plan.Expr{f}, scan, []string{"price"}, "", false) require.NoError(t, err) require.Equal(t, "", js) require.Empty(t, ser) @@ -371,7 +423,7 @@ func TestBuildFilterPredicateJSON_PKTakesPrecedenceOverIncludeList(t *testing.T) scan := newFilterTestScanNode() f := fnExpr("=", colExpr("id", 0, types.T_int64), i64Lit(7)) js, ser, res, err := buildFilterPredicateJSON( - []*plan.Expr{f}, scan, []string{"id", "price"}, "id") + []*plan.Expr{f}, scan, []string{"id", "price"}, "id", false) require.NoError(t, err) require.JSONEq(t, `[{"col":-1,"op":"=","val":7}]`, js) require.Len(t, ser, 1) @@ -383,7 +435,7 @@ func TestBuildFilterPredicateJSON_PKIsNotNull(t *testing.T) { // short-circuits (PKs are non-nullable). We still emit the predicate. scan := newFilterTestScanNode() f := fnExpr("is_not_null", colExpr("id", 0, types.T_int64)) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, nil, "id") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, nil, "id", false) require.NoError(t, err) require.JSONEq(t, `[{"col":-1,"op":"is_not_null"}]`, js) require.Len(t, ser, 1) @@ -395,7 +447,7 @@ func TestBuildFilterPredicateJSON_PKVarcharLiteralFallsThrough(t *testing.T) { // residual (no regression vs today). scan := newFilterTestScanNode() f := fnExpr("=", colExpr("id", 0, types.T_int64), sLit("abc")) - js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, nil, "id") + js, ser, res, err := buildFilterPredicateJSON([]*plan.Expr{f}, scan, nil, "id", false) require.NoError(t, err) require.Equal(t, "", js) require.Empty(t, ser) @@ -456,7 +508,7 @@ func TestFilterLiteralToJSONValue_AllNumericTypes(t *testing.T) { } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - v, ok := filterLiteralToJSONValue(tc.lit) + v, ok := filterLiteralToJSONValue(tc.lit, false) require.True(t, ok) require.Equal(t, tc.want, v) }) @@ -464,17 +516,17 @@ func TestFilterLiteralToJSONValue_AllNumericTypes(t *testing.T) { } func TestFilterLiteralToJSONValue_NilOrNull(t *testing.T) { - v, ok := filterLiteralToJSONValue(nil) + v, ok := filterLiteralToJSONValue(nil, false) require.False(t, ok) require.Nil(t, v) - v, ok = filterLiteralToJSONValue(&plan.Literal{Isnull: true}) + v, ok = filterLiteralToJSONValue(&plan.Literal{Isnull: true}, false) require.False(t, ok) require.Nil(t, v) } func TestFilterLiteralToJSONValue_StringFallsThrough(t *testing.T) { - v, ok := filterLiteralToJSONValue(&plan.Literal{Value: &plan.Literal_Sval{Sval: "x"}}) + v, ok := filterLiteralToJSONValue(&plan.Literal{Value: &plan.Literal_Sval{Sval: "x"}}, false) require.False(t, ok) require.Nil(t, v) } diff --git a/pkg/sql/plan/function/function_id_test.go b/pkg/sql/plan/function/function_id_test.go index f4d5f66fad156..c9e34cc4ef75b 100644 --- a/pkg/sql/plan/function/function_id_test.go +++ b/pkg/sql/plan/function/function_id_test.go @@ -730,9 +730,10 @@ var predefinedFunids = map[int]int{ MAX_BY: 559, MAX_BY_NON_NULL: 560, CHECK_CONSTRAINT_ASSERT: 561, + LOAD_TEXT: 562, // FUNCTION_END_NUMBER is not a function, just a flag to record the max number of function. // TODO: every one should put the new function id in front of this one if you want to make a new function. - FUNCTION_END_NUMBER: 562, + FUNCTION_END_NUMBER: 563, } func Test_funids(t *testing.T) { diff --git a/pkg/tools/checkpointtool/fulltext2_dump_test.go b/pkg/tools/checkpointtool/fulltext2_dump_test.go new file mode 100644 index 0000000000000..bca04d357d8ab --- /dev/null +++ b/pkg/tools/checkpointtool/fulltext2_dump_test.go @@ -0,0 +1,88 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package checkpointtool + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/stretchr/testify/require" +) + +// TestRenderCreateIndexStatementFulltext2 pins the checkpoint DDL round trip for a +// fulltext2 index: it must emit FULLTEXT2 (not classic FULLTEXT, which would drop the +// positional / position-free engine on restore) and carry the full persisted option +// set (parser + position_free + capacities). A classic fulltext index still renders +// FULLTEXT. +func TestRenderCreateIndexStatementFulltext2(t *testing.T) { + params, err := catalog.IndexParamsMapToJsonString(map[string]string{ + "parser": "ngram", + catalog.IndexAlgoParamPositionFree: "true", + catalog.IndexAlgoParamMaxIndexCapacity: "1000", + catalog.IndexAlgoParamMaxPostingsCapacity: "8000", + }) + require.NoError(t, err) + + info := &indexDDLInfo{ + name: "ft", + indexType: "FULLTEXT", + algo: catalog.MoIndexFullText2Algo.ToString(), + algoParams: params, + columns: map[string]indexDDLColumn{"body": {name: "body", ordinal: 0}}, + } + + ddl, err := renderCreateIndexStatement("t", info) + require.NoError(t, err) + require.Contains(t, ddl, "FULLTEXT2 ", "engine preserved on restore") + require.NotContains(t, ddl, "FULLTEXT ", "not downgraded to classic fulltext") + require.Contains(t, ddl, "WITH PARSER ngram") + require.Contains(t, ddl, catalog.IndexAlgoParamPositionFree+" = true") + require.Contains(t, ddl, catalog.IndexAlgoParamMaxIndexCapacity+" = 1000") + require.Contains(t, ddl, catalog.IndexAlgoParamMaxPostingsCapacity+" = 8000") + + // classic fulltext keeps the FULLTEXT keyword (regression guard). + info.algo = catalog.MOIndexFullTextAlgo.ToString() + ddl2, err := renderCreateIndexStatement("t", info) + require.NoError(t, err) + require.Contains(t, ddl2, "FULLTEXT ") + require.NotContains(t, ddl2, "FULLTEXT2") +} + +// TestRenderCreateIndexStatementFulltext2Include pins the checkpoint round-trip for INCLUDE +// columns: a covered fulltext2 index must restore its INCLUDE(...) clause (in persisted order), +// not silently drop it — dropping it would change the covering/prefilter contract on restore. +func TestRenderCreateIndexStatementFulltext2Include(t *testing.T) { + params, err := catalog.IndexParamsMapToJsonString(map[string]string{ + "parser": "json", + catalog.IncludedColumns: "status,prio", + }) + require.NoError(t, err) + + info := &indexDDLInfo{ + name: "ft", + indexType: "FULLTEXT", + algo: catalog.MoIndexFullText2Algo.ToString(), + algoParams: params, + columns: map[string]indexDDLColumn{"body": {name: "body", ordinal: 0}}, + } + + ddl, err := renderCreateIndexStatement("t", info) + require.NoError(t, err) + require.Contains(t, ddl, "FULLTEXT2 ") + require.Contains(t, ddl, "WITH PARSER json") + require.Containsf(t, ddl, + "INCLUDE ("+quoteDDLIdent("status")+", "+quoteDDLIdent("prio")+")", + "INCLUDE columns must round-trip in persisted order; ddl=%s", ddl) +} diff --git a/pkg/tools/checkpointtool/table_dump_test.go b/pkg/tools/checkpointtool/table_dump_test.go index 1aba268c47ecc..d08fa0ad27410 100644 --- a/pkg/tools/checkpointtool/table_dump_test.go +++ b/pkg/tools/checkpointtool/table_dump_test.go @@ -2254,6 +2254,36 @@ func TestRenderCreateTableDDLFromSchema_FullTextParserAndAnonymousName(t *testin assert.NotContains(t, ddl, "FULLTEXT `content`") } +// TestRenderCreateTableDDLFromSchema_FullText2InlineConstraint pins that a FULLTEXT2 index +// reconstructed from a mo_tables constraint renders as a FULLTEXT2 clause (with parser + +// the async SECOND cadence), NOT `KEY ... USING fulltext2`. The latter routes to +// BuildSecondaryIndexDefs on restore, which rejects fulltext2 (it needs +// BuildFullTextIndexDefs) — breaking checkpoint restore of a fulltext2 table. +func TestRenderCreateTableDDLFromSchema_FullText2InlineConstraint(t *testing.T) { + ddl := RenderCreateTableDDLFromSchema(&TableSchema{ + TableName: "ft2_test", + Columns: []TableColumn{ + {Name: "id", SQLType: "BIGINT", Position: 1, NotNull: true}, + {Name: "content", SQLType: "TEXT", Position: 2}, + }, + PrimaryKey: []string{"id"}, + UniqueKeys: []TableUniqueKey{ + { + Name: "idx_ft2", + Columns: []string{"content"}, + Algo: catalog.MoIndexFullText2Algo.ToString(), + AlgoParams: `{"parser":"ngram","auto_update":"true","second":"5"}`, + }, + }, + }) + + assert.Contains(t, ddl, "FULLTEXT2 `idx_ft2`(`content`) WITH PARSER ngram", ddl) + assert.Contains(t, ddl, "second = 5", ddl) + // Must NOT downgrade to the generic KEY ... USING fulltext2 form (restore-breaking). + assert.NotContains(t, ddl, "USING fulltext2", ddl) + assert.NotContains(t, ddl, "KEY `idx_ft2`", ddl) +} + func TestRenderCreateTableDDLFromSchema_PreservesIndexOrder(t *testing.T) { ddl := RenderCreateTableDDLFromSchema(&TableSchema{ TableName: "file_working", diff --git a/pkg/vectorindex/brute_force/brute_force_test.go b/pkg/vectorindex/brute_force/brute_force_test.go index 73e2263d92d2e..ef67604a9079c 100644 --- a/pkg/vectorindex/brute_force/brute_force_test.go +++ b/pkg/vectorindex/brute_force/brute_force_test.go @@ -383,7 +383,6 @@ func TestUsearchBruteForceLifecycle(t *testing.T) { bf := idx.(*UsearchBruteForceIndex[float32]) require.NoError(t, bf.Load(nil)) - require.NoError(t, bf.UpdateConfig(nil)) // Destroy with allocator bf.Destroy() @@ -398,7 +397,6 @@ func TestGoBruteForceLifecycle(t *testing.T) { bf := idx.(*GoBruteForceIndex[float32, float32]) require.NoError(t, bf.Load(nil)) - require.NoError(t, bf.UpdateConfig(nil)) bf.Destroy() } @@ -479,3 +477,9 @@ func TestGoBruteForceHeapLogic(t *testing.T) { } } } + +// TestSearchIntoUnsupported covers the SearchInto stubs on the CPU brute-force impls. +func TestSearchIntoUnsupported(t *testing.T) { + require.ErrorContains(t, (&UsearchBruteForceIndex[float32]{}).SearchInto(nil, nil, vectorindex.RuntimeConfig{}, nil), "not supported") + require.ErrorContains(t, (&GoBruteForceIndex[float32, float32]{}).SearchInto(nil, nil, vectorindex.RuntimeConfig{}, nil), "not supported") +} diff --git a/pkg/vectorindex/brute_force/gpu_test.go b/pkg/vectorindex/brute_force/gpu_test.go index 33881b3e7280e..2334e7f73344e 100644 --- a/pkg/vectorindex/brute_force/gpu_test.go +++ b/pkg/vectorindex/brute_force/gpu_test.go @@ -201,3 +201,9 @@ func TestGpuSearchFloat32(t *testing.T) { }) } } + +// TestSearchIntoUnsupportedGpu covers the SearchInto stubs on the GPU brute-force impls. +func TestSearchIntoUnsupportedGpu(t *testing.T) { + require.ErrorContains(t, (&GpuAdhocBruteForceIndex[float32]{}).SearchInto(nil, nil, vectorindex.RuntimeConfig{}, nil), "not supported") + require.ErrorContains(t, (&GpuBruteForceIndex[float32]{}).SearchInto(nil, nil, vectorindex.RuntimeConfig{}, nil), "not supported") +} diff --git a/pkg/vectorindex/cache/cache_test.go b/pkg/vectorindex/cache/cache_test.go index 2d554d151cf24..72cfc99b79bb2 100644 --- a/pkg/vectorindex/cache/cache_test.go +++ b/pkg/vectorindex/cache/cache_test.go @@ -52,10 +52,6 @@ func (m *MockSearch) SearchFloat32(sqlproc *sqlexec.SqlProcess, query any, rt ve return nil } -func (m *MockSearch) UpdateConfig(newalgo VectorIndexSearchIf) error { - return nil -} - type MockAnySearch struct { Idxcfg vectorindex.IndexConfig Tblcfg vectorindex.IndexTableConfig @@ -78,10 +74,6 @@ func (m *MockAnySearch) SearchFloat32(sqlproc *sqlexec.SqlProcess, query any, rt return nil } -func (m *MockAnySearch) UpdateConfig(newalgo VectorIndexSearchIf) error { - return nil -} - // Load Error type MockSearchLoadError struct { Idxcfg vectorindex.IndexConfig @@ -104,10 +96,6 @@ func (m *MockSearchLoadError) SearchFloat32(sqlproc *sqlexec.SqlProcess, query a return nil } -func (m *MockSearchLoadError) UpdateConfig(newalgo VectorIndexSearchIf) error { - return nil -} - // Search Error type MockSearchSearchError struct { Idxcfg vectorindex.IndexConfig @@ -531,3 +519,23 @@ func TestCacheReuseKeepsRuntimeConfigQueryScoped(t *testing.T) { require.Empty(t, cachedAlgo.searchCalls[1].PushdownFilterSQL) require.Equal(t, uint(2), cachedAlgo.searchCalls[1].SearchCursor.Round) } + +func (m *MockSearch) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return nil +} + +func (m *MockAnySearch) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return nil +} + +func (m *MockSearchLoadError) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return nil +} + +func (m *MockSearchSearchError) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return nil +} + +func (m *MockRuntimeSearch) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return nil +} diff --git a/pkg/vectorindex/cachegen/cachegen_test.go b/pkg/vectorindex/cachegen/cachegen_test.go new file mode 100644 index 0000000000000..d63f861758986 --- /dev/null +++ b/pkg/vectorindex/cachegen/cachegen_test.go @@ -0,0 +1,144 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cachegen + +import ( + "context" + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/common/mpool" + "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/container/vector" + "github.com/matrixorigin/matrixone/pkg/testutil" + "github.com/matrixorigin/matrixone/pkg/util/executor" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" + "github.com/stretchr/testify/require" +) + +// TestCdcGenerationSqls pins the two freshness-generation queries: MAX(timestamp) over the +// metadata table (REBUILD/MERGE signal) and MAX(chunk_id) over the tag=1 CdcTail (CDC-append +// signal), scoped to (CdcTailId, tag=1) so a base sub-index cannot mask a fresh append. +func TestCdcGenerationSqls(t *testing.T) { + cfg := vectorindex.IndexTableConfig{DbName: "db", MetadataTable: "__meta", IndexTable: "__store"} + tsSQL, tailSQL := CdcGenerationSqls(cfg) + require.Contains(t, tsSQL, "MAX(timestamp)") + require.Contains(t, tsSQL, "`db`.`__meta`") + require.Contains(t, tailSQL, "MAX(chunk_id)") + require.Contains(t, tailSQL, "`db`.`__store`") + require.Contains(t, tailSQL, "tag = 1") // tag = Tag_CdcEvents + require.Contains(t, tailSQL, vectorindex.CdcTailId) // scoped to the single CDC tail +} + +// TestGenScalarInt64 covers the scalar reader: a populated batch returns its first int64, and +// empty / nil batches fall through to the 0 default. +func TestGenScalarInt64(t *testing.T) { + mp := mpool.MustNewZero() + require.Equal(t, int64(0), genScalarInt64(executor.Result{})) // no batches + require.Equal(t, int64(0), genScalarInt64(executor.Result{Batches: []*batch.Batch{nil}})) // nil batch skipped + + bat := batch.NewWithSize(1) + bat.Vecs[0] = vector.NewVec(types.T_int64.ToType()) + require.NoError(t, vector.AppendFixed[int64](bat.Vecs[0], int64(42), false, mp)) + bat.SetRowCount(1) + require.Equal(t, int64(42), genScalarInt64(executor.Result{Batches: []*batch.Batch{bat}})) + bat.Clean(mp) +} + +// TestLoadCdcGenerationRecover: a unit-test sqlproc has no internal SQL executor, so RunSql +// panics ("missing lock service"); LoadCdcGeneration must recover it into an error (best-effort +// capture — the caller then leaves genValid=false rather than failing the load). +func TestLoadCdcGenerationRecover(t *testing.T) { + proc := sqlexec.NewSqlProcess(testutil.NewProc(t)) + cfg := vectorindex.IndexTableConfig{DbName: "db", MetadataTable: "__meta", IndexTable: "__store"} + _, _, err := LoadCdcGeneration(proc, cfg) + require.Error(t, err) +} + +// TestQueryCdcGenerationError: an unknown CN service has no registered internal SQL executor, so +// RunSqlAutoCommit returns an error, which QueryCdcGeneration propagates (IsStale then treats it +// as stale). +func TestQueryCdcGenerationError(t *testing.T) { + cfg := vectorindex.IndexTableConfig{DbName: "db", MetadataTable: "__meta", IndexTable: "__store"} + _, _, err := QueryCdcGeneration(context.Background(), "no-such-cn-uuid", 0, cfg) + require.Error(t, err) +} + +func int64Result(t *testing.T, mp *mpool.MPool, v int64) executor.Result { + bat := batch.NewWithSize(1) + bat.Vecs[0] = vector.NewVec(types.T_int64.ToType()) + require.NoError(t, vector.AppendFixed[int64](bat.Vecs[0], v, false, mp)) + bat.SetRowCount(1) + return executor.Result{Mp: mp, Batches: []*batch.Batch{bat}} +} + +// TestLoadCdcGenerationHappy stubs runSql to cover the full two-read body (timestamp then tail) +// plus the second-read error branch. +func TestLoadCdcGenerationHappy(t *testing.T) { + mp := mpool.MustNewZero() + cfg := vectorindex.IndexTableConfig{DbName: "db", MetadataTable: "m", IndexTable: "s"} + + old := runSql + defer func() { runSql = old }() + + n := 0 + runSql = func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + n++ + if n == 1 { + return int64Result(t, mp, 100), nil // timestamp + } + return int64Result(t, mp, 5), nil // tail + } + ts, tail, err := LoadCdcGeneration(nil, cfg) + require.NoError(t, err) + require.Equal(t, int64(100), ts) + require.Equal(t, int64(5), tail) + + // tail read errors → propagated. + n = 0 + runSql = func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + n++ + if n == 1 { + return int64Result(t, mp, 100), nil + } + return executor.Result{}, moerr.NewInternalErrorNoCtx("tail read failed") + } + _, _, err = LoadCdcGeneration(nil, cfg) + require.Error(t, err) +} + +// TestQueryCdcGenerationHappy stubs runSqlAutoCommit to cover the full background two-read body. +func TestQueryCdcGenerationHappy(t *testing.T) { + mp := mpool.MustNewZero() + cfg := vectorindex.IndexTableConfig{DbName: "db", MetadataTable: "m", IndexTable: "s"} + + old := runSqlAutoCommit + defer func() { runSqlAutoCommit = old }() + + n := 0 + runSqlAutoCommit = func(_ context.Context, _ string, _ uint32, _, _ string) (executor.Result, error) { + n++ + if n == 1 { + return int64Result(t, mp, 7), nil + } + return int64Result(t, mp, 9), nil + } + ts, tail, err := QueryCdcGeneration(context.Background(), "cn", 0, cfg) + require.NoError(t, err) + require.Equal(t, int64(7), ts) + require.Equal(t, int64(9), tail) +} diff --git a/pkg/vectorindex/cagra/plugin/compile/compile_test.go b/pkg/vectorindex/cagra/plugin/compile/compile_test.go index c5edc31e70e92..9da9f34bf545a 100644 --- a/pkg/vectorindex/cagra/plugin/compile/compile_test.go +++ b/pkg/vectorindex/cagra/plugin/compile/compile_test.go @@ -375,7 +375,7 @@ func TestCagraHandleCreateIndex_AsyncFalseExplicit(t *testing.T) { func TestCagraHandleCreateIndex_BackgroundReentry(t *testing.T) { ctx := newHandleCtx(true) ctx.stubCompileContext.isFrontend = false - err := Hooks{}.HandleReindex(ctx, cagraIndexDefs(), true) + err := Hooks{}.HandleReindex(ctx, cagraIndexDefs(), true, false) require.NoError(t, err) require.True(t, ctx.stubCompileContext.lastCdcTask.called, "background re-entry still drives the CDC task") require.False(t, ctx.stubCompileContext.lastIdxcronUpdate.called, "background re-entry must NOT rewrite mo_index_update") @@ -384,8 +384,8 @@ func TestCagraHandleCreateIndex_BackgroundReentry(t *testing.T) { func TestCagraHandleReindex_DelegatesToCreate(t *testing.T) { // HandleReindex is a thin pass-through to handleCreate; honors // the forceSync arg directly (unlike HandleCreateIndex, which - // now reads catalog.IsIndexAsync). - err := Hooks{}.HandleReindex(newHandleCtx(true), cagraIndexDefs(), false) + // now reads catalog.IndexParamAsync). + err := Hooks{}.HandleReindex(newHandleCtx(true), cagraIndexDefs(), false, false) require.NoError(t, err) } diff --git a/pkg/vectorindex/cagra/search_test.go b/pkg/vectorindex/cagra/search_test.go index 463c7732cf7c0..37f2c20a3b2b6 100644 --- a/pkg/vectorindex/cagra/search_test.go +++ b/pkg/vectorindex/cagra/search_test.go @@ -100,6 +100,11 @@ func TestCagraSearchTypeMismatch(t *testing.T) { s := NewCagraSearch[float32, float32](testIdxcfg(), testTblcfg(), []int{0}) s.Indexes = []*CagraModel[float32, float32]{idx} + // Build the MultiIndex so Search proceeds past the `MultiIndex == nil` early + // return and actually reaches the query-type guard under test (mirrors + // TestIvfpqSearchTypeMismatch). Without this, Search returns an empty result + // with nil error and the type mismatch is never exercised. + s.MultiIndex, _ = s.buildMultiIndex() rt := vectorindex.RuntimeConfig{Limit: 4} @@ -231,3 +236,8 @@ func TestCagraSearchLoad(t *testing.T) { s.Destroy() require.Empty(t, s.Indexes) } + +// TestSearchIntoUnsupported covers the SearchInto stub (cagra has not migrated to SearchOutput). +func TestSearchIntoUnsupported(t *testing.T) { + require.ErrorContains(t, (&CagraSearch[float32, float32]{}).SearchInto(nil, nil, vectorindex.RuntimeConfig{}, nil), "not supported") +} diff --git a/pkg/vectorindex/hnsw/generation_test.go b/pkg/vectorindex/hnsw/generation_test.go new file mode 100644 index 0000000000000..c1c2c7849d169 --- /dev/null +++ b/pkg/vectorindex/hnsw/generation_test.go @@ -0,0 +1,36 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hnsw + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/stretchr/testify/require" +) + +// TestClearIndexSqls pins the in-place clear: two DELETEs (metadata + storage), each carrying +// WHERE TRUE so MO does NOT rewrite them to a TRUNCATE (which would swap the physical table id). +func TestClearIndexSqls(t *testing.T) { + cfg := vectorindex.IndexTableConfig{DbName: "db", MetadataTable: "__meta", IndexTable: "__store"} + sqls := ClearIndexSqls(cfg) + require.Len(t, sqls, 2) + require.Contains(t, sqls[0], "`db`.`__meta`") + require.Contains(t, sqls[1], "`db`.`__store`") + for _, s := range sqls { + require.Contains(t, s, "DELETE FROM") + require.Contains(t, s, "WHERE TRUE", "bare DELETE FROM would truncate and churn the table id") + } +} diff --git a/pkg/vectorindex/hnsw/plugin/compile/compile_smoke_test.go b/pkg/vectorindex/hnsw/plugin/compile/compile_smoke_test.go index 0a68a745ce5b1..0f764ae9c8fe5 100644 --- a/pkg/vectorindex/hnsw/plugin/compile/compile_smoke_test.go +++ b/pkg/vectorindex/hnsw/plugin/compile/compile_smoke_test.go @@ -75,7 +75,7 @@ func TestHnswHandleCreateIndex_LogLine(t *testing.T) { // TestHnswHandleReindex_DelegatesToCreate — HandleReindex routes // through HandleCreateIndex, so the same log line is covered. func TestHnswHandleReindex_DelegatesToCreate(t *testing.T) { - err := Hooks{}.HandleReindex(&stubCtx{isFrontend: true}, map[string]*plan.IndexDef{}, false) + err := Hooks{}.HandleReindex(&stubCtx{isFrontend: true}, map[string]*plan.IndexDef{}, false, false) require.Error(t, err) } diff --git a/pkg/vectorindex/hnsw/plugin/runtime/runtime_test.go b/pkg/vectorindex/hnsw/plugin/runtime/runtime_test.go index 26f2bc5401999..8228038c593e7 100644 --- a/pkg/vectorindex/hnsw/plugin/runtime/runtime_test.go +++ b/pkg/vectorindex/hnsw/plugin/runtime/runtime_test.go @@ -31,6 +31,12 @@ func TestHnswHiddenTableTypes(t *testing.T) { require.Contains(t, got, catalog.Hnsw_TblType_Storage) } +// TestHnswIsVectorIndex pins the vector-KIND capability (the counterpart of the +// fulltext-family plugins' false): indexplugin.IsVectorIndexAlgo delegates to it. +func TestHnswIsVectorIndex(t *testing.T) { + require.True(t, CatalogHooks{}.IsVectorIndex()) +} + func TestHnswShouldTruncateHiddenTable(t *testing.T) { require.True(t, CatalogHooks{}.ShouldTruncateHiddenTable("anything")) } diff --git a/pkg/vectorindex/hnsw/search_test.go b/pkg/vectorindex/hnsw/search_test.go index aba9bd7220735..516ec3ff7c42b 100644 --- a/pkg/vectorindex/hnsw/search_test.go +++ b/pkg/vectorindex/hnsw/search_test.go @@ -24,6 +24,7 @@ import ( "time" fallocate "github.com/detailyang/go-fallocate" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/types" @@ -195,13 +196,6 @@ func TestHnswSearchUnlockSynchronizesWithWaitPredicate(t *testing.T) { require.Zero(t, s.Concurrency.Load()) } -func TestHnswSearchUpdateConfig(t *testing.T) { - idxcfg := vectorindex.IndexConfig{Type: "hnsw", Usearch: usearch.DefaultConfig(3)} - tblcfg := vectorindex.IndexTableConfig{} - s := NewHnswSearch[float32](idxcfg, tblcfg) - require.NoError(t, s.UpdateConfig(nil)) -} - func TestHnsw(t *testing.T) { m := mpool.MustNewZero() proc := testutil.NewProcessWithMPool(t, "", m) @@ -377,3 +371,162 @@ func makeIndexBatch2Files(proc *process.Process, id int) *batch.Batch { bat.SetRowCount(1) return bat } + +// TestHnswSearchIsStaleUncheckableEvicts: the index model loaded fine but generation capture +// failed (genValid=false — a transient error on the tiny generation SELECT after the model itself +// loaded). IsStale must report stale, NOT a no-op: an uncheckable entry whose TTL keeps sliding +// on every search would otherwise serve pre-CDC/rebuild data forever. Reporting stale forces a +// bounded evict+reload that retries capture and self-heals once it succeeds. +func TestHnswSearchIsStaleUncheckableEvicts(t *testing.T) { + // genValid=false: model present, generation never captured. + s := &HnswSearch[float32]{genValid: false, cnUUID: "some-cn"} + stale, err := s.IsStale() + require.NoError(t, err) + require.True(t, stale, "an entry that can't self-check freshness must be evicted, not pinned") + + // no service to re-query with (cnUUID empty) is equally uncheckable → stale. + s2 := &HnswSearch[float32]{genValid: true, cnUUID: ""} + stale, err = s2.IsStale() + require.NoError(t, err) + require.True(t, stale) +} + +// TestHnswSyncNextTimestampMonotonic covers the enforced-monotonic generation: even when the +// existing max timestamp EXCEEDS the writer's wall-clock (cross-CN clock skew, or a local clock +// stepping backward), nextTimestamp still allocates strictly above it — so MAX(metadata.timestamp) +// always advances on a CDC save and HnswSearch.IsStale cannot miss the update. +func TestHnswSyncNextTimestampMonotonic(t *testing.T) { + // existing max is 1h in the FUTURE relative to wall-clock. + future := time.Now().UnixMicro() + int64(time.Hour/time.Microsecond) + s := &HnswSync[float32]{indexes: []*HnswModel[float32]{{Timestamp: future - 1}, {Timestamp: future}}} + require.Equal(t, future+1, s.nextTimestamp(), "must advance past the existing max, not use wall-clock") + + // no existing rows → falls back to wall-clock now (bounded). + empty := &HnswSync[float32]{} + ts := empty.nextTimestamp() + require.Positive(t, ts) + require.LessOrEqual(t, ts, time.Now().UnixMicro()+1) +} + +// TestHnswSearchIsStaleQueryError covers the IsStale query path: with a captured generation but +// an unresolvable CN service, queryHnswGeneration errors, and IsStale treats a query error as +// stale (so a dropped/rebuilt index's dead cache entry is reclaimed) while surfacing the error. +func TestHnswSearchIsStaleQueryError(t *testing.T) { + s := &HnswSearch[float32]{ + genValid: true, + cnUUID: "no-such-cn-uuid", + Tblcfg: vectorindex.IndexTableConfig{DbName: "db", MetadataTable: "m", IndexTable: "s"}, + } + stale, err := s.IsStale() + require.Error(t, err) + require.True(t, stale) +} + +// TestHnswGenerationSql pins the freshness-generation query: the per-model checksum column over the +// metadata table (the multiset of MD5 model-file checksums is the content fingerprint). +func TestHnswGenerationSql(t *testing.T) { + sql := hnswGenerationSql(vectorindex.IndexTableConfig{DbName: "db", MetadataTable: "__meta"}) + require.Contains(t, sql, "checksum") + require.Contains(t, sql, "`db`.`__meta`") +} + +func genChecksumResult(t *testing.T, mp *mpool.MPool, sums ...string) executor.Result { + bat := batch.NewWithSize(1) + bat.Vecs[0] = vector.NewVec(types.T_varchar.ToType()) + for _, s := range sums { + require.NoError(t, vector.AppendBytes(bat.Vecs[0], []byte(s), false, mp)) + } + bat.SetRowCount(len(sums)) + return executor.Result{Mp: mp, Batches: []*batch.Batch{bat}} +} + +// TestGenChecksumsFingerprint pins the multiset digest: it is order-independent, sensitive to any +// checksum change, and the empty result has a well-defined (constant) fingerprint with count 0. +func TestGenChecksumsFingerprint(t *testing.T) { + mp := mpool.MustNewZero() + fpAB, n := genChecksums(genChecksumResult(t, mp, "A", "B")) + require.Equal(t, int64(2), n) + fpBA, _ := genChecksums(genChecksumResult(t, mp, "B", "A")) + require.Equal(t, fpAB, fpBA, "fingerprint is a multiset digest (order-independent)") + fpAC, _ := genChecksums(genChecksumResult(t, mp, "A", "C")) + require.NotEqual(t, fpAB, fpAC, "a changed checksum changes the fingerprint") + + fpEmpty, c := genChecksums(executor.Result{}) + require.Zero(t, c) + fpNil, cNil := genChecksums(executor.Result{Batches: []*batch.Batch{nil}}) + require.Zero(t, cNil) + require.Equal(t, fpEmpty, fpNil) // both empty → same constant fingerprint + require.NotEqual(t, fpAB, fpEmpty) +} + +// TestHnswSearchIsStaleContentChange is the regression for the empty-state ABA the checksum +// generation closes: a rebuild with DIFFERENT model content but the SAME model count — which a +// timestamp-or-count generation could re-mint after an intermediate (0,0) empty state — produces a +// different checksum fingerprint, so IsStale still reports stale. Identical content is not stale. +func TestHnswSearchIsStaleContentChange(t *testing.T) { + mp := mpool.MustNewZero() + old := runSqlAutoCommit + defer func() { runSqlAutoCommit = old }() + cfg := vectorindex.IndexTableConfig{DbName: "db", MetadataTable: "m", IndexTable: "s"} + + // current on-disk content: two models {A, C2}. The second model's content changed C1→C2. + runSqlAutoCommit = func(_ context.Context, _ string, _ uint32, _, _ string) (executor.Result, error) { + return genChecksumResult(t, mp, "A", "C2"), nil + } + + // loaded from {A, C1}, count 2 — same count as current, different content. + loadedFp, loadedN := genChecksums(genChecksumResult(t, mp, "A", "C1")) + s := &HnswSearch[float32]{genValid: true, cnUUID: "cn", Tblcfg: cfg, loadedFp: loadedFp, loadedCount: loadedN} + stale, err := s.IsStale() + require.NoError(t, err) + require.True(t, stale, "different content → different checksum fingerprint, even at the same count") + + // control: loaded at the current content {A, C2} → not stale (identical bytes are not stale). + curFp, curN := genChecksums(genChecksumResult(t, mp, "A", "C2")) + s2 := &HnswSearch[float32]{genValid: true, cnUUID: "cn", Tblcfg: cfg, loadedFp: curFp, loadedCount: curN} + stale, err = s2.IsStale() + require.NoError(t, err) + require.False(t, stale) +} + +// TestLoadHnswGenerationHappy stubs the live-txn reader to cover the checksum-fingerprint load path +// (order-independent digest + count) plus the read-error branch. +func TestLoadHnswGenerationHappy(t *testing.T) { + mp := mpool.MustNewZero() + cfg := vectorindex.IndexTableConfig{DbName: "db", MetadataTable: "m", IndexTable: "s"} + old := runSql + defer func() { runSql = old }() + + runSql = func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return genChecksumResult(t, mp, "cksA", "cksB", "cksC"), nil + } + fp, count, err := loadHnswGeneration(nil, cfg) + require.NoError(t, err) + require.Equal(t, int64(3), count) + want, _ := genChecksums(genChecksumResult(t, mp, "cksC", "cksA", "cksB")) // same multiset, different order + require.Equal(t, want, fp) + + // read error → propagated. + runSql = func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{}, moerr.NewInternalErrorNoCtx("read failed") + } + _, _, err = loadHnswGeneration(nil, cfg) + require.Error(t, err) +} + +// TestQueryHnswGenerationReadError covers the background reader's read-error branch. +func TestQueryHnswGenerationReadError(t *testing.T) { + cfg := vectorindex.IndexTableConfig{DbName: "db", MetadataTable: "m", IndexTable: "s"} + oldAuto := runSqlAutoCommit + defer func() { runSqlAutoCommit = oldAuto }() + runSqlAutoCommit = func(_ context.Context, _ string, _ uint32, _, _ string) (executor.Result, error) { + return executor.Result{}, moerr.NewInternalErrorNoCtx("read failed") + } + _, _, err := queryHnswGeneration(context.Background(), "cn", 0, cfg) + require.Error(t, err) +} + +// TestSearchIntoUnsupported covers the SearchInto stub. +func TestSearchIntoUnsupported(t *testing.T) { + require.ErrorContains(t, (&HnswSearch[float32]{}).SearchInto(nil, nil, vectorindex.RuntimeConfig{}, nil), "not supported") +} diff --git a/pkg/vectorindex/idxcron/executor_test.go b/pkg/vectorindex/idxcron/executor_test.go index a1845b77326ba..2e44f11308cd9 100644 --- a/pkg/vectorindex/idxcron/executor_test.go +++ b/pkg/vectorindex/idxcron/executor_test.go @@ -280,6 +280,7 @@ func (m mockCatalogHooks) ParamsFromTree(_ *tree.Index) (map[string]string, erro func (m mockCatalogHooks) DefaultOptions() map[string]string { return nil } func (m mockCatalogHooks) SupportedOpTypes() map[string]string { return nil } func (m mockCatalogHooks) SupportedVectorTypes() []types.T { return nil } +func (m mockCatalogHooks) IsVectorIndex() bool { return true } func (m mockCatalogHooks) SupportedPrimaryKeyTypes() []types.T { return nil } func (m mockCatalogHooks) SupportedIncludeColumnTypes() []types.T { return nil } func (m mockCatalogHooks) ValidQuantization(_, _ string) error { return nil } diff --git a/pkg/vectorindex/ivfflat/plugin/compile/compile_smoke_test.go b/pkg/vectorindex/ivfflat/plugin/compile/compile_smoke_test.go index 4792800a1b48d..522aeafe63b3b 100644 --- a/pkg/vectorindex/ivfflat/plugin/compile/compile_smoke_test.go +++ b/pkg/vectorindex/ivfflat/plugin/compile/compile_smoke_test.go @@ -83,7 +83,7 @@ func TestIvfflatHandleCreateIndex_LogLine(t *testing.T) { // TestIvfflatHandleReindex_LogLine — same shape via HandleReindex. func TestIvfflatHandleReindex_LogLine(t *testing.T) { - err := Hooks{}.HandleReindex(&stubCtx{}, map[string]*plan.IndexDef{}, false) + err := Hooks{}.HandleReindex(&stubCtx{}, map[string]*plan.IndexDef{}, false, false) require.Error(t, err) } diff --git a/pkg/vectorindex/ivfflat/plugin/compile/narrow_quantization_compile_test.go b/pkg/vectorindex/ivfflat/plugin/compile/narrow_quantization_compile_test.go index cb019b3fe6db9..137e6ac025dcc 100644 --- a/pkg/vectorindex/ivfflat/plugin/compile/narrow_quantization_compile_test.go +++ b/pkg/vectorindex/ivfflat/plugin/compile/narrow_quantization_compile_test.go @@ -446,9 +446,8 @@ func (f *fakeCachedIndex) Search(_ *sqlexec.SqlProcess, _ any, _ vectorindex.Run func (f *fakeCachedIndex) SearchFloat32(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ []int64, _ []float32) error { return nil } -func (f *fakeCachedIndex) Load(_ *sqlexec.SqlProcess) error { return nil } -func (f *fakeCachedIndex) UpdateConfig(_ cache.VectorIndexSearchIf) error { return nil } -func (f *fakeCachedIndex) Destroy() { f.destroyed.Store(true) } +func (f *fakeCachedIndex) Load(_ *sqlexec.SqlProcess) error { return nil } +func (f *fakeCachedIndex) Destroy() { f.destroyed.Store(true) } // seedCache parks a fake index under the given cache key and returns it. func seedCache(key string) *fakeCachedIndex { @@ -620,3 +619,7 @@ func TestValidateReindexParams_EmptyOpTypeIsUnchanged(t *testing.T) { }) require.Error(t, err) } + +func (f *fakeCachedIndex) SearchInto(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ *vectorindex.SearchOutput) error { + return nil +} diff --git a/pkg/vectorindex/ivfflat/search_test.go b/pkg/vectorindex/ivfflat/search_test.go index 05a3724f2f29d..99f798b9dfd90 100644 --- a/pkg/vectorindex/ivfflat/search_test.go +++ b/pkg/vectorindex/ivfflat/search_test.go @@ -180,11 +180,6 @@ func TestIvfflatSearchFloat32_BadQueryType(t *testing.T) { require.Error(t, err) } -func TestIvfflatUpdateConfig(t *testing.T) { - s := &IvfflatSearch[float32]{} - require.NoError(t, s.UpdateConfig(nil)) -} - func TestIvfSearchRace(t *testing.T) { runSql = mock_runSql @@ -631,3 +626,9 @@ func TestBuildActiveCentroidIDsUsesNonOverlappingBucketSlices(t *testing.T) { require.Equal(t, uint(1), cursor.CurrentBucketCount) require.True(t, cursor.Exhausted) } + +// TestSearchIntoUnsupported covers the SearchInto stub (ivfflat has not migrated to the +// box-free SearchOutput path yet; it must report "not supported", not silently no-op). +func TestSearchIntoUnsupported(t *testing.T) { + require.ErrorContains(t, (&IvfflatSearch[float32]{}).SearchInto(nil, nil, vectorindex.RuntimeConfig{}, nil), "not supported") +} diff --git a/pkg/vectorindex/ivfpq/plugin/compile/compile_test.go b/pkg/vectorindex/ivfpq/plugin/compile/compile_test.go index e1e4c3b74b69a..58e405d40ce22 100644 --- a/pkg/vectorindex/ivfpq/plugin/compile/compile_test.go +++ b/pkg/vectorindex/ivfpq/plugin/compile/compile_test.go @@ -420,7 +420,7 @@ func TestIvfpqHandleCreateIndex_AsyncFalseExplicit(t *testing.T) { func TestIvfpqHandleCreateIndex_BackgroundReentry(t *testing.T) { ctx := newHandleCtx(true) ctx.stubCompileContext.isFrontend = false - err := Hooks{}.HandleReindex(ctx, ivfpqIndexDefs(), true) + err := Hooks{}.HandleReindex(ctx, ivfpqIndexDefs(), true, false) require.NoError(t, err) require.True(t, ctx.stubCompileContext.lastCdcTask.called, "background re-entry still drives the CDC task") require.False(t, ctx.stubCompileContext.lastIdxcronUpdate.called, "background re-entry must NOT rewrite mo_index_update") @@ -429,7 +429,7 @@ func TestIvfpqHandleCreateIndex_BackgroundReentry(t *testing.T) { func TestIvfpqHandleReindex_DelegatesToCreate(t *testing.T) { // HandleReindex is a thin pass-through to handleCreate; honors // the forceSync arg directly (unlike HandleCreateIndex, which - // now reads catalog.IsIndexAsync). - err := Hooks{}.HandleReindex(newHandleCtx(true), ivfpqIndexDefs(), false) + // now reads catalog.IndexParamAsync). + err := Hooks{}.HandleReindex(newHandleCtx(true), ivfpqIndexDefs(), false, false) require.NoError(t, err) } diff --git a/pkg/vectorindex/ivfpq/search_test.go b/pkg/vectorindex/ivfpq/search_test.go index 222d636fb07d8..c93e4fccd7ee8 100644 --- a/pkg/vectorindex/ivfpq/search_test.go +++ b/pkg/vectorindex/ivfpq/search_test.go @@ -225,3 +225,8 @@ func TestIvfpqSearchLoad(t *testing.T) { s.Destroy() require.Empty(t, s.Indexes) } + +// TestSearchIntoUnsupported covers the SearchInto stub (ivfpq has not migrated to SearchOutput). +func TestSearchIntoUnsupported(t *testing.T) { + require.ErrorContains(t, (&IvfpqSearch[float32, float32]{}).SearchInto(nil, nil, vectorindex.RuntimeConfig{}, nil), "not supported") +} diff --git a/pkg/vectorindex/sqlexec/idxcron_sessionvar_guard_test.go b/pkg/vectorindex/sqlexec/idxcron_sessionvar_guard_test.go new file mode 100644 index 0000000000000..ae9c1149488dd --- /dev/null +++ b/pkg/vectorindex/sqlexec/idxcron_sessionvar_guard_test.go @@ -0,0 +1,110 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sqlexec + +import ( + "context" + "testing" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + moruntime "github.com/matrixorigin/matrixone/pkg/common/runtime" + "github.com/matrixorigin/matrixone/pkg/util/executor" + "github.com/stretchr/testify/require" +) + +// reindexRequiredSessionVars is the canonical set of session/system variables the background +// idxcron reindex build SQL path resolves. It is the CONTRACT this guard enforces: every var +// here MUST be answerable by Metadata.ResolveVariableWithSessionDefaults — either a captured +// algo knob or a deliberate background default in sessionSystemVarDefaults. When the +// reindex/build/INSERT path grows a NEW session-var dependency, add it here; the guard then +// FAILS until it is also enumerated in sessionSystemVarDefaults — forcing a conscious background +// default instead of a silent reindex abort (the #25438 sql_mode failure: the strict resolver +// errored "key sql_mode not found" and aborted every reindex). +var reindexRequiredSessionVars = []string{ + "sql_mode", // #25438 zero-temporal-date write-policy check (process/sql_mode.go) + "lock_wait_timeout", // lockop / process_codec +} + +// mockResolveExecutor is a stand-in InternalSQLExecutor whose Exec RESOLVES a fixed set of +// session vars through the Options resolver — exactly as the real internal executor plumbs +// Options.ResolveVariableFunc into plan/exec (RunSql -> WithResolveVariableFunc -> here). It lets +// this unit test drive that plumbing end-to-end with the idxcron Metadata resolver, without an +// engine. The first resolve error is surfaced (a reindex would likewise abort). +type mockResolveExecutor struct{ vars []string } + +func (m *mockResolveExecutor) Exec(_ context.Context, _ string, opts executor.Options) (executor.Result, error) { + resolve := opts.ResolveVariableFunc() + if resolve == nil { + return executor.Result{}, moerr.NewInternalErrorNoCtx("no resolveVariableFunc plumbed into Options") + } + for _, v := range m.vars { + if _, err := resolve(v, true, false); err != nil { + return executor.Result{}, err + } + } + return executor.Result{}, nil +} + +func (m *mockResolveExecutor) ExecTxn(context.Context, func(executor.TxnExecutor) error, executor.Options) error { + return nil +} + +// TestIdxcronReindexSessionVarGuard drives RunSql through a mock InternalSQLExecutor that resolves +// the reindex's required session vars via the installed idxcron Metadata resolver +// (ResolveVariableWithSessionDefaults). It guards the session-var contract: a new required var +// that is not enumerated in sessionSystemVarDefaults breaks this test. +// +// IF THIS TEST FAILS with `un-enumerated session variable ""`: the background idxcron +// reindex path now resolves a session variable that has no background default. FIX IT in +// pkg/vectorindex/sqlexec/metadata.go — add "" to the sessionSystemVarDefaults map with the +// correct BACKGROUND value (e.g. sql_mode "" not the strict default), then add it to +// reindexRequiredSessionVars above. Do NOT just capture the user's live value: a background +// rebuild must use a safe default (see the sessionSystemVarDefaults doc comment). This is the +// #25438 failure mode surfaced at test time instead of aborting reindex in production. +func TestIdxcronReindexSessionVarGuard(t *testing.T) { + const sid = "ut-idxcron-sessionvar-guard" + moruntime.SetupServiceBasedRuntime(sid, moruntime.DefaultRuntime()) + + // An algo-only capture blob — exactly what an idxcron task's Metadata carries (no session + // vars); session vars must come from the background defaults. + md, err := NewMetadataFromJson(`{"cfg":{"kmeans_train_percent":{"t":"F","v":10}}}`) + require.NoError(t, err) + + run := func(vars []string) error { + moruntime.ServiceRuntime(sid).SetGlobalVariables(moruntime.InternalSQLExecutor, &mockResolveExecutor{vars: vars}) + sp := NewSqlProcessWithContext(NewSqlContext(context.Background(), sid, nil, 0, md.ResolveVariableWithSessionDefaults)) + _, e := RunSql(sp, "select 1") + return e + } + + // GUARD: every currently-required reindex session var resolves without error through the + // full RunSql -> Options.ResolveVariableFunc -> Metadata resolver plumbing. If the reindex + // path grows a new dependency added to reindexRequiredSessionVars but NOT to + // sessionSystemVarDefaults, ResolveVariableWithSessionDefaults fail-fasts and this fails. + require.NoErrorf(t, run(reindexRequiredSessionVars), + "a required reindex session var has no background default: add it to the "+ + "sessionSystemVarDefaults map in pkg/vectorindex/sqlexec/metadata.go with its correct "+ + "BACKGROUND value (see the fix note on TestIdxcronReindexSessionVarGuard)") + + // NEGATIVE: an un-enumerated session var fail-fasts through the same plumbing — proving the + // guard actually catches a newly-plumbed, unwhitelisted dependency (not a silent nil). + err = run([]string{"some_new_unenumerated_var"}) + require.Error(t, err) + require.Contains(t, err.Error(), "some_new_unenumerated_var") + + // A CAPTURED algo knob still resolves strictly (it is not treated as an un-enumerated + // session var), so real build-config is honored on the reindex path. + require.NoError(t, run([]string{"kmeans_train_percent"})) +} diff --git a/pkg/vectorindex/sqlexec/metadata_test.go b/pkg/vectorindex/sqlexec/metadata_test.go index 68205ef3ae652..76af5483d2cf6 100644 --- a/pkg/vectorindex/sqlexec/metadata_test.go +++ b/pkg/vectorindex/sqlexec/metadata_test.go @@ -55,6 +55,63 @@ func TestResolveVariableFunc(t *testing.T) { require.Equal(t, v5, any("action string")) } +// TestResolveVariableWithSessionDefaults pins the idxcron reindex-hook fix: a +// captured Metadata holds only algo build knobs and NOT sql_mode, so the plain +// ResolveVariableFunc errors on it — which aborts every reindex once the rebuild +// INSERT resolves sql_mode (RejectZeroTemporalWritePolicy, #25438). The +// session-defaulting resolver must instead return the permissive background +// default ("") for sql_mode, while keeping algo vars answered exactly and an +// uncaptured algo var still ERRORING (so a real build-config bug surfaces). +func TestResolveVariableWithSessionDefaults(t *testing.T) { + // A realistic captured blob: algo knobs only, deliberately no sql_mode. + jstr := `{"cfg":{"kmeans_train_percent":{"t":"F", "v":10}, + "kmeans_max_iteration":{"t":"I", "v":4}}}` + + m, err := NewMetadataFromJson(jstr) + require.Nil(t, err) + + // Baseline: the plain resolver errors on the uncaptured sql_mode — this is + // exactly the "key sql_mode not found" that broke idxcron reindex. + _, err = m.ResolveVariableFunc("sql_mode", true, false) + require.NotNil(t, err) + + // Fixed resolver: sql_mode falls back to the permissive background default "". + v, err := m.ResolveVariableWithSessionDefaults("sql_mode", true, false) + require.Nil(t, err) + require.Equal(t, any(""), v) + + // Case-insensitive on the whitelisted var name. + v, err = m.ResolveVariableWithSessionDefaults("SQL_MODE", true, false) + require.Nil(t, err) + require.Equal(t, any(""), v) + + // lock_wait_timeout is enumerated with a nil default: its callers fall back to + // their own context-aware default on nil, so we must NOT invent a value. + v, err = m.ResolveVariableWithSessionDefaults("lock_wait_timeout", true, false) + require.Nil(t, err) + require.Nil(t, v) + + // FAIL FAST on an un-enumerated session var: rather than silently defaulting to + // nil, a newly-plumbed dependency surfaces loudly, named, so it gets a + // deliberate default. (This is design B.) + _, err = m.ResolveVariableWithSessionDefaults("some_new_session_var", true, false) + require.NotNil(t, err) + require.Contains(t, err.Error(), "some_new_session_var") + + // Captured algo vars are answered exactly as the strict resolver does. + v, err = m.ResolveVariableWithSessionDefaults("kmeans_train_percent", false, false) + require.Nil(t, err) + require.Equal(t, any(float64(10)), v) + + // Strictness preserved for CAPTURED vars: a captured-but-malformed algo knob + // still errors, so a genuine build blob bug surfaces loudly. (An uncaptured + // name is treated as a session var above; only names present in cfg are strict.) + bad, err := NewMetadataFromJson(`{"cfg":{"kmeans_train_percent":{"v":10}}}`) // missing type + require.Nil(t, err) + _, err = bad.ResolveVariableWithSessionDefaults("kmeans_train_percent", false, false) + require.NotNil(t, err) +} + func TestMetadataWriter(t *testing.T) { writer := NewMetadataWriter() diff --git a/pkg/vectorindex/sqlexec/sqlexec_test.go b/pkg/vectorindex/sqlexec/sqlexec_test.go index 685c2244e2127..fb89252711f68 100644 --- a/pkg/vectorindex/sqlexec/sqlexec_test.go +++ b/pkg/vectorindex/sqlexec/sqlexec_test.go @@ -131,3 +131,21 @@ func TestFinishTxnWithCleanupContextCommitsWithFreshContext(t *testing.T) { require.NoError(t, err) require.Equal(t, uint32(42), commitTenant) } + +// TestSqlProcessServiceAndAccount covers the GetService/GetAccountID accessors added for the +// cache freshness check (captured at load to re-query in the background). +func TestSqlProcessServiceAndAccount(t *testing.T) { + // Proc-backed: just exercise the branch (values depend on the test proc). + proc := testutil.NewProcessWithMPool(t, "", mpool.MustNewZero()) + sp := NewSqlProcess(proc) + _ = sp.GetService() + _, _ = sp.GetAccountID() + + // SqlCtx-backed: deterministic service + account. + sc := NewSqlContext(context.Background(), "cn-uuid", nil, 42, nil) + sp2 := NewSqlProcessWithContext(sc) + require.Equal(t, "cn-uuid", sp2.GetService()) + acc, err := sp2.GetAccountID() + require.NoError(t, err) + require.Equal(t, uint32(42), acc) +} diff --git a/test/distributed/cases/fulltext/fulltext2.result b/test/distributed/cases/fulltext/fulltext2.result index 43bfc44127ec3..01357719bddba 100644 --- a/test/distributed/cases/fulltext/fulltext2.result +++ b/test/distributed/cases/fulltext/fulltext2.result @@ -17,24 +17,33 @@ insert into fulltext01 VALUES('Johnson', 'David','M','Engineering',66); insert into fulltext01 VALUES('Sharma', 'Bradley','M','Production',27); insert into fulltext01 VALUES('Rapier', 'Abigail','F', 'Human Resources',38); select * from fulltext01; -lastname firstname gender departmentname age -Gilbert Kevin M Tool Design 33 -Tamburello Andrea F Marketing 45 -Johnson David M Engineering 66 -Sharma Bradley M Production 27 -Rapier Abigail F Human Resources 38 +➤ lastname[1,10,0] ¦ firstname[1,10,0] ¦ gender[1,1,0] ¦ departmentname[1,20,0] ¦ age[4,32,0] 𝄀 +Gilbert ¦ Kevin ¦ M ¦ Tool Design ¦ 33 𝄀 +Tamburello ¦ Andrea ¦ F ¦ Marketing ¦ 45 𝄀 +Johnson ¦ David ¦ M ¦ Engineering ¦ 66 𝄀 +Sharma ¦ Bradley ¦ M ¦ Production ¦ 27 𝄀 +Rapier ¦ Abigail ¦ F ¦ Human Resources ¦ 38 create fulltext index ftidx on fulltext01 (LastName, FirstName); alter table fulltext01 add column newcolumn decimal after LastName; show create table fulltext01; -Table Create Table -fulltext01 CREATE TABLE `fulltext01` (\n `LastName` char(10) NOT NULL,\n `newcolumn` decimal(38,0) DEFAULT NULL,\n `FirstName` char(10) DEFAULT NULL,\n `Gender` char(1) DEFAULT NULL,\n `DepartmentName` char(20) DEFAULT NULL,\n `Age` int DEFAULT NULL,\n PRIMARY KEY (`LastName`),\n FULLTEXT `ftidx`(`LastName`,`FirstName`)\n) +➤ Table[12,-1,0] ¦ Create Table[12,-1,0] 𝄀 +fulltext01 ¦ CREATE TABLE `fulltext01` ( + `LastName` char(10) NOT NULL, + `newcolumn` decimal(38,0) DEFAULT NULL, + `FirstName` char(10) DEFAULT NULL, + `Gender` char(1) DEFAULT NULL, + `DepartmentName` char(20) DEFAULT NULL, + `Age` int DEFAULT NULL, + PRIMARY KEY (`LastName`), + FULLTEXT `ftidx`(`LastName`,`FirstName`) +) select * from fulltext01; -lastname newcolumn firstname gender departmentname age -Gilbert null Kevin M Tool Design 33 -Tamburello null Andrea F Marketing 45 -Johnson null David M Engineering 66 -Sharma null Bradley M Production 27 -Rapier null Abigail F Human Resources 38 +➤ lastname[1,10,0] ¦ newcolumn[3,38,0] ¦ firstname[1,10,0] ¦ gender[1,1,0] ¦ departmentname[1,20,0] ¦ age[4,32,0] 𝄀 +Gilbert ¦ null ¦ Kevin ¦ M ¦ Tool Design ¦ 33 𝄀 +Tamburello ¦ null ¦ Andrea ¦ F ¦ Marketing ¦ 45 𝄀 +Johnson ¦ null ¦ David ¦ M ¦ Engineering ¦ 66 𝄀 +Sharma ¦ null ¦ Bradley ¦ M ¦ Production ¦ 27 𝄀 +Rapier ¦ null ¦ Abigail ¦ F ¦ Human Resources ¦ 38 truncate fulltext01; drop table fulltext01; drop table if exists employees; @@ -54,22 +63,32 @@ insert into employees(employeeNumber,lastName,firstName,extension,email,officeCo (1056,'Patterson','Mary','x4611','mpatterso@classicmodelcars.com','1',1002,'VP Sales'), (1076,'Firrelli','Jeff','x9273','jfirrelli@classicmodelcars.com','1',1002,'VP Marketing'); select * from employees; -employeenumber lastname firstname extension email officecode reportsto jobtitle -1002 Murphy Diane x5800 dmurphy@classicmodelcars.com 1 null President -1056 Patterson Mary x4611 mpatterso@classicmodelcars.com 1 1002 VP Sales -1076 Firrelli Jeff x9273 jfirrelli@classicmodelcars.com 1 1002 VP Marketing +➤ employeenumber[4,32,0] ¦ lastname[12,-1,0] ¦ firstname[12,-1,0] ¦ extension[12,-1,0] ¦ email[12,-1,0] ¦ officecode[12,-1,0] ¦ reportsto[4,32,0] ¦ jobtitle[12,-1,0] 𝄀 +1002 ¦ Murphy ¦ Diane ¦ x5800 ¦ dmurphy@classicmodelcars.com ¦ 1 ¦ null ¦ President 𝄀 +1056 ¦ Patterson ¦ Mary ¦ x4611 ¦ mpatterso@classicmodelcars.com ¦ 1 ¦ 1002 ¦ VP Sales 𝄀 +1076 ¦ Firrelli ¦ Jeff ¦ x9273 ¦ jfirrelli@classicmodelcars.com ¦ 1 ¦ 1002 ¦ VP Marketing create fulltext index f01 on employees (LastName, FirstName); alter table employees drop column LastName; show create table employees; -Table Create Table -employees CREATE TABLE `employees` (\n `employeeNumber` int NOT NULL,\n `firstName` varchar(50) NOT NULL,\n `extension` varchar(10) NOT NULL,\n `email` varchar(100) NOT NULL,\n `officeCode` varchar(10) NOT NULL,\n `reportsTo` int DEFAULT NULL,\n `jobTitle` varchar(50) NOT NULL,\n PRIMARY KEY (`employeeNumber`),\n FULLTEXT `f01`(`firstName`)\n) +➤ Table[12,-1,0] ¦ Create Table[12,-1,0] 𝄀 +employees ¦ CREATE TABLE `employees` ( + `employeeNumber` int NOT NULL, + `firstName` varchar(50) NOT NULL, + `extension` varchar(10) NOT NULL, + `email` varchar(100) NOT NULL, + `officeCode` varchar(10) NOT NULL, + `reportsTo` int DEFAULT NULL, + `jobTitle` varchar(50) NOT NULL, + PRIMARY KEY (`employeeNumber`), + FULLTEXT `f01`(`firstName`) +) select * from employees; -employeenumber firstname extension email officecode reportsto jobtitle -1002 Diane x5800 dmurphy@classicmodelcars.com 1 null President -1056 Mary x4611 mpatterso@classicmodelcars.com 1 1002 VP Sales -1076 Jeff x9273 jfirrelli@classicmodelcars.com 1 1002 VP Marketing +➤ employeenumber[4,32,0] ¦ firstname[12,-1,0] ¦ extension[12,-1,0] ¦ email[12,-1,0] ¦ officecode[12,-1,0] ¦ reportsto[4,32,0] ¦ jobtitle[12,-1,0] 𝄀 +1002 ¦ Diane ¦ x5800 ¦ dmurphy@classicmodelcars.com ¦ 1 ¦ null ¦ President 𝄀 +1056 ¦ Mary ¦ x4611 ¦ mpatterso@classicmodelcars.com ¦ 1 ¦ 1002 ¦ VP Sales 𝄀 +1076 ¦ Jeff ¦ x9273 ¦ jfirrelli@classicmodelcars.com ¦ 1 ¦ 1002 ¦ VP Marketing select count(*) from employees; -count(*) +➤ count(*)[-5,64,0] 𝄀 3 truncate employees; drop table employees; @@ -82,36 +101,41 @@ insert into t1 values(4, 'ab_def'); create fulltext index f02 on t1 (col2); alter table t1 modify column col2 text; show create table t1; -Table Create Table -t1 CREATE TABLE `t1` (\n `col1` int NOT NULL,\n `col2` text DEFAULT NULL,\n PRIMARY KEY (`col1`),\n FULLTEXT `f02`(`col2`)\n) +➤ Table[12,-1,0] ¦ Create Table[12,-1,0] 𝄀 +t1 ¦ CREATE TABLE `t1` ( + `col1` int NOT NULL, + `col2` text DEFAULT NULL, + PRIMARY KEY (`col1`), + FULLTEXT `f02`(`col2`) +) select * from t1; -col1 col2 -1 abcdef -2 _bcdef -3 a_cdef -4 ab_def +➤ col1[4,32,0] ¦ col2[12,0,0] 𝄀 +1 ¦ abcdef 𝄀 +2 ¦ _bcdef 𝄀 +3 ¦ a_cdef 𝄀 +4 ¦ ab_def drop table t1; drop table if exists t1; create table t1 (a json, b int primary key); insert into t1 values ('{"t1":"a"}',1),('{"t1":"b"}',2),('{"t1":"c"}',3),('{"t1":"d"}',4); select * from t1; -a b -{"t1": "a"} 1 -{"t1": "b"} 2 -{"t1": "c"} 3 -{"t1": "d"} 4 +➤ a[-1,2147483647,0] ¦ b[4,32,0] 𝄀 +{"t1": "a"} ¦ 1 𝄀 +{"t1": "b"} ¦ 2 𝄀 +{"t1": "c"} ¦ 3 𝄀 +{"t1": "d"} ¦ 4 create fulltext index f03 on t1 (a); insert into t1 values ('{"t1":"c"}',5); select count(a) from t1; -count(a) +➤ count(a)[-5,64,0] 𝄀 5 select * from t1; -a b -{"t1": "a"} 1 -{"t1": "b"} 2 -{"t1": "c"} 3 -{"t1": "d"} 4 -{"t1": "c"} 5 +➤ a[-1,2147483647,0] ¦ b[4,32,0] 𝄀 +{"t1": "a"} ¦ 1 𝄀 +{"t1": "b"} ¦ 2 𝄀 +{"t1": "c"} ¦ 3 𝄀 +{"t1": "d"} ¦ 4 𝄀 +{"t1": "c"} ¦ 5 drop table t1; drop table if exists table01; create table table01 (col1 int primary key, col2 datalink); @@ -124,9 +148,9 @@ create table ab01(col1 int not null primary key , col2 char, col3 varchar(10)); insert into ab01 values (1,2,'da'); insert into ab01 values (2,3,'e4r34f'); select * from ab01; -col1 col2 col3 -1 2 da -2 3 e4r34f +➤ col1[4,32,0] ¦ col2[1,1,0] ¦ col3[12,-1,0] 𝄀 +1 ¦ 2 ¦ da 𝄀 +2 ¦ 3 ¦ e4r34f create fulltext index f01 on ab01 (col2); create fulltext index f02 on ab01 (col2); not supported: Fulltext index are not allowed to use the same column @@ -136,23 +160,29 @@ create table char01 (col1 varchar(200) primary key , col2 char(10)); insert into char01 values ('23789178942u1uj3ridjfh2d28u49u4ueji32jf2f32ef32894rjk32nv432f432f', '367283r343'); insert into char01 values ('32jhbfchjecmwd%^&^(*&)UJHFRE%^T&YUHIJKNM', null); select * from char01; -col1 col2 -23789178942u1uj3ridjfh2d28u49u4ueji32jf2f32ef32894rjk32nv432f432f 367283r343 -32jhbfchjecmwd%^&^(*&)UJHFRE%^T&YUHIJKNM null +➤ col1[12,-1,0] ¦ col2[1,10,0] 𝄀 +23789178942u1uj3ridjfh2d28u49u4ueji32jf2f32ef32894rjk32nv432f432f ¦ 367283r343 𝄀 +32jhbfchjecmwd%^&^(*&)UJHFRE%^T&YUHIJKNM ¦ null alter table char01 add fulltext index f01(col1); alter table char01 add fulltext index f02(col2); show create table char01; -Table Create Table -char01 CREATE TABLE `char01` (\n `col1` varchar(200) NOT NULL,\n `col2` char(10) DEFAULT NULL,\n PRIMARY KEY (`col1`),\n FULLTEXT `f01`(`col1`),\n FULLTEXT `f02`(`col2`)\n) +➤ Table[12,-1,0] ¦ Create Table[12,-1,0] 𝄀 +char01 ¦ CREATE TABLE `char01` ( + `col1` varchar(200) NOT NULL, + `col2` char(10) DEFAULT NULL, + PRIMARY KEY (`col1`), + FULLTEXT `f01`(`col1`), + FULLTEXT `f02`(`col2`) +) drop table char01; drop table if exists ab02; create table ab02 (a bigint unsigned not null, primary key(a)); insert into ab02 values (18446744073709551615), (0xFFFFFFFFFFFFFFFE), (18446744073709551613), (18446744073709551612); select * from ab02; -a -18446744073709551615 -18446744073709551614 -18446744073709551613 +➤ a[-5,64,0] 𝄀 +18446744073709551615 𝄀 +18446744073709551614 𝄀 +18446744073709551613 𝄀 18446744073709551612 create fulltext index f03 on ab02 (a); not supported: fulltext index only support char, varchar, text, datalink and json @@ -162,10 +192,10 @@ create table t1(a binary(2) primary key); insert into t1 values(null); constraint violation: Column 'a' cannot be null select * from t1; -a +➤ a[12,6,0] insert into t1 values("时"); select * from t1; -a +➤ a[12,6,0] 𝄀 时 insert into t1 values(rpad("1", 500, "1")); internal error: Can't cast '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111...' from VARCHAR type to BINARY type. Src length 500 is larger than Dest length 2 @@ -196,15 +226,21 @@ insert into articles (title, content) values ('MO全文索引示例', '这是一个关于MO全文索引的例子。它展示了如何使用ngram解析器进行全文搜索。'), ('ngram解析器', 'ngram解析器允许MO对中文等语言进行分词,以优化全文搜索。'); show create table articles; -Table Create Table -articles CREATE TABLE `articles` (\n `id` int NOT NULL AUTO_INCREMENT,\n `title` varchar(255) DEFAULT NULL,\n `content` text DEFAULT NULL,\n PRIMARY KEY (`id`),\n FULLTEXT (`title`,`content`) WITH PARSER ngram\n) +➤ Table[12,-1,0] ¦ Create Table[12,-1,0] 𝄀 +articles ¦ CREATE TABLE `articles` ( + `id` int NOT NULL AUTO_INCREMENT, + `title` varchar(255) DEFAULT NULL, + `content` text DEFAULT NULL, + PRIMARY KEY (`id`), + FULLTEXT (`title`,`content`) WITH PARSER ngram +) select * from articles where match(title, content) against('全文索引' IN NATURAL LANGUAGE MODE); -id title content -1 MO全文索引示例 这是一个关于MO全文索引的例子。它展示了如何使用ngram解析器进行全文搜索。 +➤ id[4,32,0] ¦ title[12,-1,0] ¦ content[12,0,0] 𝄀 +1 ¦ MO全文索引示例 ¦ 这是一个关于MO全文索引的例子。它展示了如何使用ngram解析器进行全文搜索。 select * from articles; -id title content -1 MO全文索引示例 这是一个关于MO全文索引的例子。它展示了如何使用ngram解析器进行全文搜索。 -2 ngram解析器 ngram解析器允许MO对中文等语言进行分词,以优化全文搜索。 +➤ id[4,32,0] ¦ title[12,-1,0] ¦ content[12,0,0] 𝄀 +1 ¦ MO全文索引示例 ¦ 这是一个关于MO全文索引的例子。它展示了如何使用ngram解析器进行全文搜索。 𝄀 +2 ¦ ngram解析器 ¦ ngram解析器允许MO对中文等语言进行分词,以优化全文搜索。 drop table articles; drop table if exists products; create table products ( @@ -217,21 +253,27 @@ insert into products (name, details) values ('ノートパソコン', '{"brand": "Dell", "specs": "i7, 16GB RAM", "price": 1200}'), ('스마트폰', '{"brand": "Apple", "model": "iPhone 12", "price": 800}'); show create table products; -Table Create Table -products CREATE TABLE `products` (\n `id` int NOT NULL AUTO_INCREMENT,\n `name` varchar(255) DEFAULT NULL,\n `details` json DEFAULT NULL,\n PRIMARY KEY (`id`),\n FULLTEXT (`details`) WITH PARSER json\n) +➤ Table[12,-1,0] ¦ Create Table[12,-1,0] 𝄀 +products ¦ CREATE TABLE `products` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `details` json DEFAULT NULL, + PRIMARY KEY (`id`), + FULLTEXT (`details`) WITH PARSER json +) insert into products (name, details) values('手机', '{"brand": "Apple", "model": "iPhone 12", "price": 800}'); select * from products; -id name details -1 ノートパソコン {"brand": "Dell", "price": 1200, "specs": "i7, 16GB RAM"} -2 스마트폰 {"brand": "Apple", "model": "iPhone 12", "price": 800} -3 手机 {"brand": "Apple", "model": "iPhone 12", "price": 800} +➤ id[4,32,0] ¦ name[12,-1,0] ¦ details[-1,2147483647,0] 𝄀 +1 ¦ ノートパソコン ¦ {"brand": "Dell", "price": 1200, "specs": "i7, 16GB RAM"} 𝄀 +2 ¦ 스마트폰 ¦ {"brand": "Apple", "model": "iPhone 12", "price": 800} 𝄀 +3 ¦ 手机 ¦ {"brand": "Apple", "model": "iPhone 12", "price": 800} select * from products where match(details) against('Dell' IN NATURAL LANGUAGE MODE); -id name details -1 ノートパソコン {"brand": "Dell", "price": 1200, "specs": "i7, 16GB RAM"} +➤ id[4,32,0] ¦ name[12,-1,0] ¦ details[-1,2147483647,0] 𝄀 +1 ¦ ノートパソコン ¦ {"brand": "Dell", "price": 1200, "specs": "i7, 16GB RAM"} select id, name from products where match(details) against('Apple' IN NATURAL LANGUAGE MODE); -id name -2 스마트폰 -3 手机 +➤ id[4,32,0] ¦ name[12,-1,0] 𝄀 +2 ¦ 스마트폰 𝄀 +3 ¦ 手机 drop table products; drop table if exists prepare_fulltext; create table prepare_fulltext (a char primary key , b varchar(20)); @@ -239,13 +281,18 @@ insert into prepare_fulltext values (1, 11), (2, 22), (3, 33); prepare stmt1 from 'create fulltext index f06 on prepare_fulltext (a)'; execute stmt1; show create table prepare_fulltext; -Table Create Table -prepare_fulltext CREATE TABLE `prepare_fulltext` (\n `a` char(1) NOT NULL,\n `b` varchar(20) DEFAULT NULL,\n PRIMARY KEY (`a`),\n FULLTEXT `f06`(`a`)\n) +➤ Table[12,-1,0] ¦ Create Table[12,-1,0] 𝄀 +prepare_fulltext ¦ CREATE TABLE `prepare_fulltext` ( + `a` char(1) NOT NULL, + `b` varchar(20) DEFAULT NULL, + PRIMARY KEY (`a`), + FULLTEXT `f06`(`a`) +) select * from prepare_fulltext; -a b -1 11 -2 22 -3 33 +➤ a[1,1,0] ¦ b[12,-1,0] 𝄀 +1 ¦ 11 𝄀 +2 ¦ 22 𝄀 +3 ¦ 33 drop table prepare_fulltext; drop table if exists pro; create table pro ( @@ -258,12 +305,19 @@ execute stmt4; prepare stmt3 from 'alter table pro add fulltext index pro2(name)'; execute stmt3; show create table pro; -Table Create Table -pro CREATE TABLE `pro` (\n `id` int NOT NULL AUTO_INCREMENT,\n `name` varchar(255) DEFAULT NULL,\n `details` json DEFAULT NULL,\n PRIMARY KEY (`id`),\n FULLTEXT `pro1`(`details`) WITH PARSER json,\n FULLTEXT `pro2`(`name`)\n) +➤ Table[12,-1,0] ¦ Create Table[12,-1,0] 𝄀 +pro ¦ CREATE TABLE `pro` ( + `id` int NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `details` json DEFAULT NULL, + PRIMARY KEY (`id`), + FULLTEXT `pro1`(`details`) WITH PARSER json, + FULLTEXT `pro2`(`name`) +) insert into pro (name, details) values('手机', '{"brand": "Apple", "model": "iPhone 12", "price": 800}'); select * from pro; -id name details -1 手机 {"brand": "Apple", "model": "iPhone 12", "price": 800} +➤ id[4,32,0] ¦ name[12,-1,0] ¦ details[-1,2147483647,0] 𝄀 +1 ¦ 手机 ¦ {"brand": "Apple", "model": "iPhone 12", "price": 800} drop table pro; drop table if exists test_table; create table test_table( @@ -277,63 +331,72 @@ PRIMARY KEY (`col1`), fulltext(col5) ); show create table test_table; -Table Create Table -test_table CREATE TABLE `test_table` (\n `col1` int NOT NULL AUTO_INCREMENT,\n `col2` float DEFAULT NULL,\n `col3` bool DEFAULT NULL,\n `col4` date DEFAULT NULL,\n `col5` varchar(255) DEFAULT NULL,\n `col6` text DEFAULT NULL,\n PRIMARY KEY (`col1`),\n FULLTEXT (`col5`)\n) +➤ Table[12,-1,0] ¦ Create Table[12,-1,0] 𝄀 +test_table ¦ CREATE TABLE `test_table` ( + `col1` int NOT NULL AUTO_INCREMENT, + `col2` float DEFAULT NULL, + `col3` bool DEFAULT NULL, + `col4` date DEFAULT NULL, + `col5` varchar(255) DEFAULT NULL, + `col6` text DEFAULT NULL, + PRIMARY KEY (`col1`), + FULLTEXT (`col5`) +) set @old_sql_mode = @@sql_mode; set sql_mode = concat_ws(',', @@sql_mode, 'NO_AUTO_VALUE_ON_ZERO'); load data infile '$resources/load_data/test_1.csv' into table test_table fields terminated by ',' parallel 'true'; select * from test_table; -col1 col2 col3 col4 col5 col6 -0 0.1 1 1982-03-24 varchar_0 text_0 -1 1.1 0 1982-07-31 varchar_1 text_1 -2 2.1 1 2002-04-13 varchar_2 text_2 -3 3.1 0 2019-09-29 varchar_3 text_3 -4 4.1 1 1996-08-20 varchar_4 text_4 -5 5.1 0 2008-10-13 varchar_5 text_5 -6 6.1 1 2002-03-05 varchar_6 text_6 -7 7.1 0 1993-11-07 varchar_7 text_7 -8 8.1 1 1994-01-08 varchar_8 text_8 -9 9.1 0 1999-09-17 varchar_9 text_9 -10 10.1 1 2003-09-28 varchar_10 text_10 -11 11.1 0 1984-01-22 varchar_11 text_11 -12 12.1 1 1994-02-10 varchar_12 text_12 -13 13.1 0 1994-12-02 varchar_13 text_13 -14 14.1 1 2013-01-19 varchar_14 text_14 -15 15.1 0 2002-09-29 varchar_15 text_15 -16 16.1 1 2001-12-07 varchar_16 text_16 -17 17.1 0 1989-03-11 varchar_17 text_17 -18 18.1 1 2019-11-04 varchar_18 text_18 -19 19.1 0 2002-08-15 varchar_19 text_19 -20 20.1 1 1995-12-20 varchar_20 text_20 -21 21.1 0 2018-08-03 varchar_21 text_21 -22 22.1 1 1998-03-10 varchar_22 text_22 -23 23.1 0 2022-04-08 varchar_23 text_23 -24 24.1 1 1989-04-27 varchar_24 text_24 -25 25.1 0 2009-03-14 varchar_25 text_25 -26 26.1 1 2019-03-27 varchar_26 text_26 -27 27.1 0 1983-10-28 varchar_27 text_27 -28 28.1 1 1993-07-19 varchar_28 text_28 -29 29.1 0 1986-09-11 varchar_29 text_29 -30 30.1 1 1992-08-19 varchar_30 text_30 -31 31.1 0 1981-08-07 varchar_31 text_31 -32 32.1 1 1984-03-25 varchar_32 text_32 -33 33.1 0 2001-09-05 varchar_33 text_33 -34 34.1 1 2022-03-17 varchar_34 text_34 -35 35.1 0 2008-06-24 varchar_35 text_35 -36 36.1 1 1987-11-23 varchar_36 text_36 -37 37.1 0 2008-11-10 varchar_37 text_37 -38 38.1 1 1986-01-29 varchar_38 text_38 -39 39.1 0 1985-02-27 varchar_39 text_39 -40 40.1 1 2016-11-28 varchar_40 text_40 -41 41.1 0 1991-02-11 varchar_41 text_41 -42 42.1 1 2019-01-08 varchar_42 text_42 -43 43.1 0 2009-05-01 varchar_43 text_43 -44 44.1 1 1986-11-15 varchar_44 text_44 -45 45.1 0 2000-07-22 varchar_45 text_45 -46 46.1 1 2002-09-07 varchar_46 text_46 -47 47.1 0 1987-09-23 varchar_47 text_47 -48 48.1 1 1985-10-25 varchar_48 text_48 -49 49.1 0 1988-10-06 varchar_49 text_49 +➤ col1[4,32,0] ¦ col2[7,24,0] ¦ col3[-7,1,0] ¦ col4[91,64,0] ¦ col5[12,-1,0] ¦ col6[12,0,0] 𝄀 +0 ¦ 0.1 ¦ 1 ¦ 1982-03-24 ¦ varchar_0 ¦ text_0 𝄀 +1 ¦ 1.1 ¦ 0 ¦ 1982-07-31 ¦ varchar_1 ¦ text_1 𝄀 +2 ¦ 2.1 ¦ 1 ¦ 2002-04-13 ¦ varchar_2 ¦ text_2 𝄀 +3 ¦ 3.1 ¦ 0 ¦ 2019-09-29 ¦ varchar_3 ¦ text_3 𝄀 +4 ¦ 4.1 ¦ 1 ¦ 1996-08-20 ¦ varchar_4 ¦ text_4 𝄀 +5 ¦ 5.1 ¦ 0 ¦ 2008-10-13 ¦ varchar_5 ¦ text_5 𝄀 +6 ¦ 6.1 ¦ 1 ¦ 2002-03-05 ¦ varchar_6 ¦ text_6 𝄀 +7 ¦ 7.1 ¦ 0 ¦ 1993-11-07 ¦ varchar_7 ¦ text_7 𝄀 +8 ¦ 8.1 ¦ 1 ¦ 1994-01-08 ¦ varchar_8 ¦ text_8 𝄀 +9 ¦ 9.1 ¦ 0 ¦ 1999-09-17 ¦ varchar_9 ¦ text_9 𝄀 +10 ¦ 10.1 ¦ 1 ¦ 2003-09-28 ¦ varchar_10 ¦ text_10 𝄀 +11 ¦ 11.1 ¦ 0 ¦ 1984-01-22 ¦ varchar_11 ¦ text_11 𝄀 +12 ¦ 12.1 ¦ 1 ¦ 1994-02-10 ¦ varchar_12 ¦ text_12 𝄀 +13 ¦ 13.1 ¦ 0 ¦ 1994-12-02 ¦ varchar_13 ¦ text_13 𝄀 +14 ¦ 14.1 ¦ 1 ¦ 2013-01-19 ¦ varchar_14 ¦ text_14 𝄀 +15 ¦ 15.1 ¦ 0 ¦ 2002-09-29 ¦ varchar_15 ¦ text_15 𝄀 +16 ¦ 16.1 ¦ 1 ¦ 2001-12-07 ¦ varchar_16 ¦ text_16 𝄀 +17 ¦ 17.1 ¦ 0 ¦ 1989-03-11 ¦ varchar_17 ¦ text_17 𝄀 +18 ¦ 18.1 ¦ 1 ¦ 2019-11-04 ¦ varchar_18 ¦ text_18 𝄀 +19 ¦ 19.1 ¦ 0 ¦ 2002-08-15 ¦ varchar_19 ¦ text_19 𝄀 +20 ¦ 20.1 ¦ 1 ¦ 1995-12-20 ¦ varchar_20 ¦ text_20 𝄀 +21 ¦ 21.1 ¦ 0 ¦ 2018-08-03 ¦ varchar_21 ¦ text_21 𝄀 +22 ¦ 22.1 ¦ 1 ¦ 1998-03-10 ¦ varchar_22 ¦ text_22 𝄀 +23 ¦ 23.1 ¦ 0 ¦ 2022-04-08 ¦ varchar_23 ¦ text_23 𝄀 +24 ¦ 24.1 ¦ 1 ¦ 1989-04-27 ¦ varchar_24 ¦ text_24 𝄀 +25 ¦ 25.1 ¦ 0 ¦ 2009-03-14 ¦ varchar_25 ¦ text_25 𝄀 +26 ¦ 26.1 ¦ 1 ¦ 2019-03-27 ¦ varchar_26 ¦ text_26 𝄀 +27 ¦ 27.1 ¦ 0 ¦ 1983-10-28 ¦ varchar_27 ¦ text_27 𝄀 +28 ¦ 28.1 ¦ 1 ¦ 1993-07-19 ¦ varchar_28 ¦ text_28 𝄀 +29 ¦ 29.1 ¦ 0 ¦ 1986-09-11 ¦ varchar_29 ¦ text_29 𝄀 +30 ¦ 30.1 ¦ 1 ¦ 1992-08-19 ¦ varchar_30 ¦ text_30 𝄀 +31 ¦ 31.1 ¦ 0 ¦ 1981-08-07 ¦ varchar_31 ¦ text_31 𝄀 +32 ¦ 32.1 ¦ 1 ¦ 1984-03-25 ¦ varchar_32 ¦ text_32 𝄀 +33 ¦ 33.1 ¦ 0 ¦ 2001-09-05 ¦ varchar_33 ¦ text_33 𝄀 +34 ¦ 34.1 ¦ 1 ¦ 2022-03-17 ¦ varchar_34 ¦ text_34 𝄀 +35 ¦ 35.1 ¦ 0 ¦ 2008-06-24 ¦ varchar_35 ¦ text_35 𝄀 +36 ¦ 36.1 ¦ 1 ¦ 1987-11-23 ¦ varchar_36 ¦ text_36 𝄀 +37 ¦ 37.1 ¦ 0 ¦ 2008-11-10 ¦ varchar_37 ¦ text_37 𝄀 +38 ¦ 38.1 ¦ 1 ¦ 1986-01-29 ¦ varchar_38 ¦ text_38 𝄀 +39 ¦ 39.1 ¦ 0 ¦ 1985-02-27 ¦ varchar_39 ¦ text_39 𝄀 +40 ¦ 40.1 ¦ 1 ¦ 2016-11-28 ¦ varchar_40 ¦ text_40 𝄀 +41 ¦ 41.1 ¦ 0 ¦ 1991-02-11 ¦ varchar_41 ¦ text_41 𝄀 +42 ¦ 42.1 ¦ 1 ¦ 2019-01-08 ¦ varchar_42 ¦ text_42 𝄀 +43 ¦ 43.1 ¦ 0 ¦ 2009-05-01 ¦ varchar_43 ¦ text_43 𝄀 +44 ¦ 44.1 ¦ 1 ¦ 1986-11-15 ¦ varchar_44 ¦ text_44 𝄀 +45 ¦ 45.1 ¦ 0 ¦ 2000-07-22 ¦ varchar_45 ¦ text_45 𝄀 +46 ¦ 46.1 ¦ 1 ¦ 2002-09-07 ¦ varchar_46 ¦ text_46 𝄀 +47 ¦ 47.1 ¦ 0 ¦ 1987-09-23 ¦ varchar_47 ¦ text_47 𝄀 +48 ¦ 48.1 ¦ 1 ¦ 1985-10-25 ¦ varchar_48 ¦ text_48 𝄀 +49 ¦ 49.1 ¦ 0 ¦ 1988-10-06 ¦ varchar_49 ¦ text_49 set sql_mode = @old_sql_mode; drop table test_table; drop table if exists jsonline_t2; @@ -345,13 +408,13 @@ col4 varchar(225) primary key ); create fulltext index f05 on jsonline_t2(col3); load data infile{'filepath'='$resources/load_data/char_varchar_2.jl','format'='jsonline','jsondata'='object'}into table jsonline_t2; -select * from jsonline_t2; -col1 col2 col3 col4 -"a" "b" "c" "d" -'d' '123456' 'b' 'a' -a 中文 #¥%……&# 234cccc重要 -"'a'" "'b'" "'c'" abc - 123f fff yeah 红tu bb" +select * from jsonline_t2 order by col4; +➤ col1[1,225,0] ¦ col2[12,-1,0] ¦ col3[12,0,0] ¦ col4[12,-1,0] 𝄀 +"a" ¦ "b" ¦ "c" ¦ "d" 𝄀 +'d' ¦ '123456' ¦ 'b' ¦ 'a' 𝄀 +a ¦ 中文 ¦ #¥%……&# ¦ 234cccc重要 𝄀 +"'a'" ¦ "'b'" ¦ "'c'" ¦ abc 𝄀 + 123f fff ¦ yeah ¦ 红tu ¦ bb" drop table jsonline_t2; drop table if exists t1; create table t1( @@ -371,14 +434,29 @@ col12 bool create fulltext index f06 on t1(col9); load data infile {'filepath'='$resources/load_data/jsonline_object01.jl','format'='jsonline','jsondata'='object'} into table t1; select * from t1; -col1 col2 col3 col4 col5 col6 col7 col8 col9 col10 col11 col12 -1 1 var 2020-09-07 2020-09-07 00:00:00 2020-09-07 00:00:00 18 121.11 ["1", 2, null, false, true, {"q": 1}] 1qaz null null -1 2 var 2020-09-07 2020-09-07 00:00:00 2020-09-07 00:00:00 18 121.11 {"b": ["a", "b", {"q": 4}], "c": 1} 1aza null null -1 3 var 2020-09-07 2020-09-07 00:00:00 2020-09-07 00:00:00 18 121.11 ["1", 2, null, false, true, {"q": 1}] 1az null null -1 4 var 2020-09-07 2020-09-07 00:00:00 2020-09-07 00:00:00 18 121.11 {"b": ["a", "b", {"q": 4}], "c": 1} 1qaz null null +➤ col1[-7,1,0] ¦ col2[4,32,0] ¦ col3[12,-1,0] ¦ col4[91,64,0] ¦ col5[93,64,0] ¦ col6[93,64,0] ¦ col7[3,38,0] ¦ col8[7,24,0] ¦ col9[-1,2147483647,0] ¦ col10[12,0,0] ¦ col11[-1,2147483647,0] ¦ col12[-7,1,0] 𝄀 +1 ¦ 1 ¦ var ¦ 2020-09-07 ¦ 2020-09-07 00:00:00 ¦ 2020-09-07 00:00:00 ¦ 18 ¦ 121.11 ¦ ["1", 2, null, false, true, {"q": 1}] ¦ 1qaz ¦ null ¦ null 𝄀 +1 ¦ 2 ¦ var ¦ 2020-09-07 ¦ 2020-09-07 00:00:00 ¦ 2020-09-07 00:00:00 ¦ 18 ¦ 121.11 ¦ {"b": ["a", "b", {"q": 4}], "c": 1} ¦ 1aza ¦ null ¦ null 𝄀 +1 ¦ 3 ¦ var ¦ 2020-09-07 ¦ 2020-09-07 00:00:00 ¦ 2020-09-07 00:00:00 ¦ 18 ¦ 121.11 ¦ ["1", 2, null, false, true, {"q": 1}] ¦ 1az ¦ null ¦ null 𝄀 +1 ¦ 4 ¦ var ¦ 2020-09-07 ¦ 2020-09-07 00:00:00 ¦ 2020-09-07 00:00:00 ¦ 18 ¦ 121.11 ¦ {"b": ["a", "b", {"q": 4}], "c": 1} ¦ 1qaz ¦ null ¦ null show create table t1; -Table Create Table -t1 CREATE TABLE `t1` (\n `col1` bool DEFAULT NULL,\n `col2` int NOT NULL,\n `col3` varchar(100) DEFAULT NULL,\n `col4` date DEFAULT NULL,\n `col5` datetime DEFAULT NULL,\n `col6` timestamp NULL DEFAULT NULL,\n `col7` decimal(38,0) DEFAULT NULL,\n `col8` float DEFAULT NULL,\n `col9` json DEFAULT NULL,\n `col10` text DEFAULT NULL,\n `col11` json DEFAULT NULL,\n `col12` bool DEFAULT NULL,\n PRIMARY KEY (`col2`),\n FULLTEXT `f06`(`col9`)\n) +➤ Table[12,-1,0] ¦ Create Table[12,-1,0] 𝄀 +t1 ¦ CREATE TABLE `t1` ( + `col1` bool DEFAULT NULL, + `col2` int NOT NULL, + `col3` varchar(100) DEFAULT NULL, + `col4` date DEFAULT NULL, + `col5` datetime DEFAULT NULL, + `col6` timestamp NULL DEFAULT NULL, + `col7` decimal(38,0) DEFAULT NULL, + `col8` float DEFAULT NULL, + `col9` json DEFAULT NULL, + `col10` text DEFAULT NULL, + `col11` json DEFAULT NULL, + `col12` bool DEFAULT NULL, + PRIMARY KEY (`col2`), + FULLTEXT `f06`(`col9`) +) drop table t1; drop table if exists articles; create table articles ( @@ -397,27 +475,27 @@ insert into articles (title, body) VALUES select * from articles where match (title, body) against ('database' in natural language mode); -id title body -1 MO Tutorial DBMS stands for DataBase ... -5 MO vs. YourSQL In the following database comparison ... +➤ id[4,32,0] ¦ title[12,-1,0] ¦ body[12,0,0] 𝄀 +1 ¦ MO Tutorial ¦ DBMS stands for DataBase ... 𝄀 +5 ¦ MO vs. YourSQL ¦ In the following database comparison ... select id, match (title, body) against ('Tutorial' in natural language mode) as score from articles; -id score -1 0.2276447 -3 0.2276447 +➤ id[4,32,0] ¦ score[7,5,0] 𝄀 +1 ¦ 0.2276447 𝄀 +3 ¦ 0.2276447 select id, body, match (title, body) against ('MO tutorial DBMS stands for' in natural language mode) as score from articles; -id body score -1 DBMS stands for DataBase ... 3.0275967 +➤ id[4,32,0] ¦ body[12,0,0] ¦ score[7,5,0] 𝄀 +1 ¦ DBMS stands for DataBase ... ¦ 3.0275967 select id, body, match (title, body) against ('MO tutorial DBMS stands for' in natural language mode) as score from articles where match (title, body) against ('MO tutorial DBMS stands for' in natural language mode); -id body score -1 DBMS stands for DataBase ... 3.0275967 +➤ id[4,32,0] ¦ body[12,0,0] ¦ score[7,5,0] 𝄀 +1 ¦ DBMS stands for DataBase ... ¦ 3.0275967 drop table articles; drop table if exists article; create table article ( @@ -436,22 +514,22 @@ insert into article (title, body) VALUES select * from article where match (body) against ('database' in natural language mode); -id title body -1 MO Tutorial DBMS stands for DataBase ... -5 MO vs. YourSQL In the following database comparison ... +➤ id[4,32,0] ¦ title[12,-1,0] ¦ body[12,0,0] 𝄀 +1 ¦ MO Tutorial ¦ DBMS stands for DataBase ... 𝄀 +5 ¦ MO vs. YourSQL ¦ In the following database comparison ... select id, match (body) against ('DataBase' in natural language mode) as score from article; -id score -1 0.2276447 -5 0.2276447 +➤ id[4,32,0] ¦ score[7,5,0] 𝄀 +1 ¦ 0.2276447 𝄀 +5 ¦ 0.2276447 select id, body, match (body) against ('DBMS stands for Database' in natural language mode) as score from article where match (body) against ('DBMS stands for Database' in natural language mode); -id body score -1 DBMS stands for DataBase ... 2.4220774 +➤ id[4,32,0] ¦ body[12,0,0] ¦ score[7,5,0] 𝄀 +1 ¦ DBMS stands for DataBase ... ¦ 2.4220774 drop table article; drop table if exists example1; create table example1 ( @@ -468,59 +546,59 @@ insert into example1 (content) values select * from example1 where match (content) against ('+MO +database' in boolean mode); -id content -1 MO is a database management system. +➤ id[4,32,0] ¦ content[12,0,0] 𝄀 +1 ¦ MO is a database management system. select * from example1 where match(content) against ('+database' in boolean mode); -id content -1 MO is a database management system. -2 A database management system is a software that manages databases. +➤ id[4,32,0] ¦ content[12,0,0] 𝄀 +1 ¦ MO is a database management system. 𝄀 +2 ¦ A database management system is a software that manages databases. select * from example1 where match(content) against ('-database' in boolean mode); -id content +➤ id[4,32,0] ¦ content[12,0,0] select * from example1 where match (content) against ('+web development -MO' in boolean mode); -id content -4 PHP is a popular server-side scripting language for web development. +➤ id[4,32,0] ¦ content[12,0,0] 𝄀 +4 ¦ PHP is a popular server-side scripting language for web development. select * from example1 where match (content) against ('+MO' in boolean mode); -id content -1 MO is a database management system. -3 MO is a popular choice for development. +➤ id[4,32,0] ¦ content[12,0,0] 𝄀 +1 ¦ MO is a database management system. 𝄀 +3 ¦ MO is a popular choice for development. select * from example1 where match (content) against ('+MO ~popular' in boolean mode); -id content -1 MO is a database management system. -3 MO is a popular choice for development. +➤ id[4,32,0] ¦ content[12,0,0] 𝄀 +1 ¦ MO is a database management system. 𝄀 +3 ¦ MO is a popular choice for development. select * from example1 where match (content) against ('MO*' in boolean mode); -id content -1 MO is a database management system. -3 MO is a popular choice for development. +➤ id[4,32,0] ¦ content[12,0,0] 𝄀 +1 ¦ MO is a database management system. 𝄀 +3 ¦ MO is a popular choice for development. select * from example1 where match (content) against ('+MO +(database)' in boolean mode); -id content -1 MO is a database management system. -3 MO is a popular choice for development. +➤ id[4,32,0] ¦ content[12,0,0] 𝄀 +1 ¦ MO is a database management system. 𝄀 +3 ¦ MO is a popular choice for development. select * from example1 where match (content) against ('+MO popular' in boolean mode); -id content -3 MO is a popular choice for development. -1 MO is a database management system. +➤ id[4,32,0] ¦ content[12,0,0] 𝄀 +3 ¦ MO is a popular choice for development. 𝄀 +1 ¦ MO is a database management system. select * from example1 where match (content) against ('popular' in boolean mode); -id content -3 MO is a popular choice for development. -4 PHP is a popular server-side scripting language for web development. +➤ id[4,32,0] ¦ content[12,0,0] 𝄀 +3 ¦ MO is a popular choice for development. 𝄀 +4 ¦ PHP is a popular server-side scripting language for web development. drop table example1; drop table if exists example_json; create table example_json ( @@ -533,19 +611,19 @@ insert into example_json (data) values ('{"title": "Introduction to MO", "content": "MO is an open-source relational database management system."}'), ('{"title": "MO development", "content": "MO history"}'); select * from example_json where match(data) against ('MO development' in boolean mode); -id data -3 {"content": "MO history", "title": "MO development"} -1 {"content": "Full-text search is a technique for searching text-based content.", "title": "MO Full-Text Search"} -2 {"content": "MO is an open-source relational database management system.", "title": "Introduction to MO"} +➤ id[4,32,0] ¦ data[-1,2147483647,0] 𝄀 +3 ¦ {"content": "MO history", "title": "MO development"} 𝄀 +1 ¦ {"content": "Full-text search is a technique for searching text-based content.", "title": "MO Full-Text Search"} 𝄀 +2 ¦ {"content": "MO is an open-source relational database management system.", "title": "Introduction to MO"} select * from example_json where match(data) against (' '); -id data +➤ id[4,32,0] ¦ data[-1,2147483647,0] select * from example_json where match(data) against ('"MO development"' in boolean mode); -id data -3 {"content": "MO history", "title": "MO development"} +➤ id[4,32,0] ¦ data[-1,2147483647,0] 𝄀 +3 ¦ {"content": "MO history", "title": "MO development"} select * from example_json where match(data) against ('+MO -open -source' in boolean mode); -id data -1 {"content": "Full-text search is a technique for searching text-based content.", "title": "MO Full-Text Search"} -3 {"content": "MO history", "title": "MO development"} +➤ id[4,32,0] ¦ data[-1,2147483647,0] 𝄀 +1 ¦ {"content": "Full-text search is a technique for searching text-based content.", "title": "MO Full-Text Search"} 𝄀 +3 ¦ {"content": "MO history", "title": "MO development"} drop table example_json; drop table if exists articles; drop table if exists authors; @@ -567,21 +645,21 @@ insert into articles (title, content, author_id) values ('MO性能优化', '本文将探讨如何优化MO数据库的性能,包括索引优化和查询优化。', 3), ('全文索引与JOIN操作', '全文索引可以与JOIN操作结合使用,以实现跨表的全文搜索。', 1); select * from articles; -id title content author_id -1 MO全文索引入门 MO全文索引是一种强大的工具,可以帮助你快速检索数据库中的文本数据。 1 -2 深入理解全文索引 全文索引不仅可以提高搜索效率,还可以通过JOIN操作与其他表结合使用。 2 -3 MO性能优化 本文将探讨如何优化MO数据库的性能,包括索引优化和查询优化。 3 -4 全文索引与JOIN操作 全文索引可以与JOIN操作结合使用,以实现跨表的全文搜索。 1 +➤ id[4,32,0] ¦ title[12,-1,0] ¦ content[12,0,0] ¦ author_id[4,32,0] 𝄀 +1 ¦ MO全文索引入门 ¦ MO全文索引是一种强大的工具,可以帮助你快速检索数据库中的文本数据。 ¦ 1 𝄀 +2 ¦ 深入理解全文索引 ¦ 全文索引不仅可以提高搜索效率,还可以通过JOIN操作与其他表结合使用。 ¦ 2 𝄀 +3 ¦ MO性能优化 ¦ 本文将探讨如何优化MO数据库的性能,包括索引优化和查询优化。 ¦ 3 𝄀 +4 ¦ 全文索引与JOIN操作 ¦ 全文索引可以与JOIN操作结合使用,以实现跨表的全文搜索。 ¦ 1 select * from authors; -id name -1 John Doe -2 Jane Smith -3 Alice Johnson +➤ id[4,32,0] ¦ name[12,-1,0] 𝄀 +1 ¦ John Doe 𝄀 +2 ¦ Jane Smith 𝄀 +3 ¦ Alice Johnson select a.title, a.content, au.name from articles a join authors au on a.author_id = au.id where match(a.content) against ('MO' IN NATURAL LANGUAGE MODE); - +[unknown result because it is related to issue#20687] drop table articles; drop table authors; drop table if exists posts; @@ -610,13 +688,13 @@ from posts left join comments on posts.post_id = comments.post_id where match(posts.content) against ('全文索引' IN NATURAL LANGUAGE MODE) group by posts.post_id; - +[unknown result because it is related to issue#20687] select title, content from articles where match(content) against ('全文索引' IN NATURAL LANGUAGE MODE) union select comment_text as title, comment_text as content from comments where match(comment_text) AGAINST ('全文索引' IN NATURAL LANGUAGE MODE); - +[unknown result because it is related to issue#20687] drop table comments; drop table posts; drop table if exists fulltext_test01; @@ -630,17 +708,30 @@ FULLTEXT f01(`col3`) WITH PARSER ngram ); load data infile '$resources/external_table_file/zhwiki.txt' into table fulltext_test01 fields terminated by ':' ESCAPED BY '\t' lines terminated by '\n'; show create table fulltext_test01; -Table Create Table -fulltext_test01 CREATE TABLE `fulltext_test01` (\n `col1` bigint DEFAULT NULL,\n `col2` int NOT NULL,\n `col3` varchar(200) DEFAULT NULL,\n `col4` varchar(200) DEFAULT NULL,\n PRIMARY KEY (`col2`),\n FULLTEXT `f01`(`col3`) WITH PARSER ngram\n) +➤ Table[12,-1,0] ¦ Create Table[12,-1,0] 𝄀 +fulltext_test01 ¦ CREATE TABLE `fulltext_test01` ( + `col1` bigint DEFAULT NULL, + `col2` int NOT NULL, + `col3` varchar(200) DEFAULT NULL, + `col4` varchar(200) DEFAULT NULL, + PRIMARY KEY (`col2`), + FULLTEXT `f01`(`col3`) WITH PARSER ngram +) alter table fulltext_test01 drop index f01; show create table fulltext_test01; -Table Create Table -fulltext_test01 CREATE TABLE `fulltext_test01` (\n `col1` bigint DEFAULT NULL,\n `col2` int NOT NULL,\n `col3` varchar(200) DEFAULT NULL,\n `col4` varchar(200) DEFAULT NULL,\n PRIMARY KEY (`col2`)\n) +➤ Table[12,-1,0] ¦ Create Table[12,-1,0] 𝄀 +fulltext_test01 ¦ CREATE TABLE `fulltext_test01` ( + `col1` bigint DEFAULT NULL, + `col2` int NOT NULL, + `col3` varchar(200) DEFAULT NULL, + `col4` varchar(200) DEFAULT NULL, + PRIMARY KEY (`col2`) +) select * from fulltext_test01; -col1 col2 col3 col4 -608 1 Wikipedia 上载纪录/存档/2002年 -608 2 Wikipedia 删除纪录/档案馆/2004年3月 -608 26 Wikipedia 繁简分歧词表 -608 31 Wikipedia 宣告/2005年 +➤ col1[-5,64,0] ¦ col2[4,32,0] ¦ col3[12,-1,0] ¦ col4[12,-1,0] 𝄀 +608 ¦ 1 ¦ Wikipedia ¦ 上载纪录/存档/2002年 𝄀 +608 ¦ 2 ¦ Wikipedia ¦ 删除纪录/档案馆/2004年3月 𝄀 +608 ¦ 26 ¦ Wikipedia ¦ 繁简分歧词表 𝄀 +608 ¦ 31 ¦ Wikipedia ¦ 宣告/2005年 drop table fulltext_test01; drop database test_fulltext; diff --git a/test/distributed/cases/fulltext/fulltext2.sql b/test/distributed/cases/fulltext/fulltext2.sql index e194e3480ce61..67a649fbb423e 100644 --- a/test/distributed/cases/fulltext/fulltext2.sql +++ b/test/distributed/cases/fulltext/fulltext2.sql @@ -254,7 +254,7 @@ col4 varchar(225) primary key ); create fulltext index f05 on jsonline_t2(col3); load data infile{'filepath'='$resources/load_data/char_varchar_2.jl','format'='jsonline','jsondata'='object'}into table jsonline_t2; -select * from jsonline_t2; +select * from jsonline_t2 order by col4; drop table jsonline_t2; diff --git a/test/distributed/cases/fulltext2/fulltext2.result b/test/distributed/cases/fulltext2/fulltext2.result new file mode 100644 index 0000000000000..f0854f8487bc4 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2.result @@ -0,0 +1,90 @@ +drop database if exists test_fulltext2; +create database test_fulltext2; +use test_fulltext2; +set experimental_fulltext2_index = 1; +select @@ft2_relevancy_algorithm; +@@ft2_relevancy_algorithm +BM25 +drop table if exists docs; +create table docs(id bigint primary key, body text); +insert into docs values +(0,'the quick brown fox jumps'), +(1,'a quick brown dog'), +(2,'the lazy fox sleeps'), +(3,'brown bear and lazy cat'), +(4,'quick quick quick fox fox'); +create fulltext2 index ft on docs(body); +show create table docs; +Table Create Table +docs CREATE TABLE `docs` (\n `id` bigint NOT NULL,\n `body` text DEFAULT NULL,\n PRIMARY KEY (`id`),\n FULLTEXT2 `ft`(`body`) WITH PARSER ngram\n) +select id from docs where match(body) against('quick brown fox') order by id; +id +0 +select id from docs where match(body) against('brown fox') order by id; +id +0 +select id from docs where match(body) against('lazy') order by id; +id +2 +3 +select id from docs where match(body) against('unicorn') order by id; +id +select id from docs where match(body) against('+quick +fox' in boolean mode) order by id; +id +0 +4 +select id from docs where match(body) against('+quick -fox' in boolean mode) order by id; +id +1 +select id from docs where match(body) against('quic*' in boolean mode) order by id; +id +0 +1 +4 +select id from docs where match(body) against('"quick brown"' in boolean mode) order by id; +id +0 +1 +select id from docs where match(body) against('+brown +(fox dog)' in boolean mode) order by id; +id +0 +1 +select id from docs where match(body) against('lazy fox' in boolean mode) order by id; +id +0 +2 +3 +4 +select id from docs where match(body) against('+lazy ~fox' in boolean mode); +id +3 +2 +select count(*) from docs where match(body) against('+quick +fox' in boolean mode); +count(*) +2 +drop table if exists mc; +create table mc(id bigint primary key, body text, title text); +insert into mc values (0,'quick brown fox','red car'),(1,'lazy dog','quick red'); +create fulltext2 index ft on mc(body,title); +select id from mc where match(body,title) against('+quick +red' in boolean mode) order by id; +id +0 +1 +select id from mc where match(body,title) against('red') order by id; +id +0 +1 +drop table if exists cp; +create table cp(id1 varchar, id2 bigint, body text, primary key(id1,id2)); +insert into cp values ('a',1,'quick brown fox'),('b',2,'lazy fox sleeps'); +create fulltext2 index ft on cp(body); +select id1,id2 from cp where match(body) against('+fox -brown' in boolean mode) order by id1; +id1 id2 +b 2 +set ft2_relevancy_algorithm='TF-IDF'; +select id from docs where match(body) against('+lazy ~fox' in boolean mode); +id +3 +2 +set ft2_relevancy_algorithm='BM25'; +drop database test_fulltext2; diff --git a/test/distributed/cases/fulltext2/fulltext2.sql b/test/distributed/cases/fulltext2/fulltext2.sql new file mode 100644 index 0000000000000..6680a3d4aa49d --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2.sql @@ -0,0 +1,71 @@ +-- fulltext2 (WAND positional engine) SYNCHRONOUS functional test: INSERT rows, +-- then CREATE FULLTEXT2 INDEX (builds the tag=0 base from source). No post-create +-- DML here — async CDC maintenance is covered by +-- pessimistic_transaction/fulltext2. NL mode is EXACT PHRASE (fulltext2's +-- distinguishing semantics); boolean supports the full +/-/~//()/*/"..." surface. + +drop database if exists test_fulltext2; +create database test_fulltext2; +use test_fulltext2; + +-- CREATE FULLTEXT2 INDEX is gated behind experimental_fulltext2_index (default off). +set experimental_fulltext2_index = 1; + +-- fulltext2 has its own relevance var (default BM25, distinct from classic +-- ft_relevancy_algorithm = TF-IDF). +select @@ft2_relevancy_algorithm; + +-- ================= English: NL exact-phrase + boolean ================= +drop table if exists docs; +create table docs(id bigint primary key, body text); +insert into docs values + (0,'the quick brown fox jumps'), + (1,'a quick brown dog'), + (2,'the lazy fox sleeps'), + (3,'brown bear and lazy cat'), + (4,'quick quick quick fox fox'); +create fulltext2 index ft on docs(body); +show create table docs; + +-- NL EXACT-PHRASE: contiguous only. +select id from docs where match(body) against('quick brown fox') order by id; +select id from docs where match(body) against('brown fox') order by id; +select id from docs where match(body) against('lazy') order by id; +select id from docs where match(body) against('unicorn') order by id; + +-- boolean mode. +select id from docs where match(body) against('+quick +fox' in boolean mode) order by id; +select id from docs where match(body) against('+quick -fox' in boolean mode) order by id; +select id from docs where match(body) against('quic*' in boolean mode) order by id; +select id from docs where match(body) against('"quick brown"' in boolean mode) order by id; +select id from docs where match(body) against('+brown +(fox dog)' in boolean mode) order by id; +select id from docs where match(body) against('lazy fox' in boolean mode) order by id; + +-- ~ downweights: doc 3 (lazy, no fox) outranks doc 2 (lazy + fox). The planner +-- auto-sorts by score desc, so no ORDER BY is needed (result order IS score desc). +select id from docs where match(body) against('+lazy ~fox' in boolean mode); + +-- match in aggregate. +select count(*) from docs where match(body) against('+quick +fox' in boolean mode); + +-- ================= multi-column ================= +drop table if exists mc; +create table mc(id bigint primary key, body text, title text); +insert into mc values (0,'quick brown fox','red car'),(1,'lazy dog','quick red'); +create fulltext2 index ft on mc(body,title); +select id from mc where match(body,title) against('+quick +red' in boolean mode) order by id; +select id from mc where match(body,title) against('red') order by id; + +-- ================= composite primary key ================= +drop table if exists cp; +create table cp(id1 varchar, id2 bigint, body text, primary key(id1,id2)); +insert into cp values ('a',1,'quick brown fox'),('b',2,'lazy fox sleeps'); +create fulltext2 index ft on cp(body); +select id1,id2 from cp where match(body) against('+fox -brown' in boolean mode) order by id1; + +-- ================= TF-IDF vs BM25 (membership stable) ================= +set ft2_relevancy_algorithm='TF-IDF'; +select id from docs where match(body) against('+lazy ~fox' in boolean mode); +set ft2_relevancy_algorithm='BM25'; + +drop database test_fulltext2; diff --git a/test/distributed/cases/fulltext2/fulltext2_bitpk.result b/test/distributed/cases/fulltext2/fulltext2_bitpk.result new file mode 100644 index 0000000000000..80f7c765ddc71 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_bitpk.result @@ -0,0 +1,46 @@ +drop database if exists test_fulltext2_bitpk; +create database test_fulltext2_bitpk; +use test_fulltext2_bitpk; +set experimental_fulltext2_index = 1; +drop table if exists docs; +create table docs(id bit(16) primary key, body text); +insert into docs values +(10,'the quick brown fox jumps'), +(20,'a quick brown dog'), +(30,'the lazy fox sleeps'), +(40,'brown bear and lazy cat'), +(50,'quick quick quick fox fox'); +create fulltext2 index ft on docs(body); +show create table docs; +➤ Table[12,4,0] ¦ Create Table[12,140,0] 𝄀 +docs ¦ CREATE TABLE `docs` ( + `id` bit(16) NOT NULL, + `body` text DEFAULT NULL, + PRIMARY KEY (`id`), + FULLTEXT2 `ft`(`body`) WITH PARSER ngram +) +select cast(id as unsigned) as id from docs where match(body) against('quick brown fox') order by id; +➤ id[-5,64,0] 𝄀 +10 +select cast(id as unsigned) as id from docs where match(body) against('brown fox') order by id; +➤ id[-5,64,0] 𝄀 +10 +select cast(id as unsigned) as id from docs where match(body) against('lazy') order by id; +➤ id[-5,64,0] 𝄀 +30 𝄀 +40 +select cast(id as unsigned) as id from docs where match(body) against('unicorn') order by id; +➤ id[-5,64,0] +select cast(id as unsigned) as id from docs where match(body) against('+quick +fox' in boolean mode) order by id; +➤ id[-5,64,0] 𝄀 +10 𝄀 +50 +select cast(id as unsigned) as id from docs where match(body) against('+quick -fox' in boolean mode) order by id; +➤ id[-5,64,0] 𝄀 +20 +select cast(id as unsigned) as id from docs where match(body) against('quic*' in boolean mode) order by id; +➤ id[-5,64,0] 𝄀 +10 𝄀 +20 𝄀 +50 +drop database test_fulltext2_bitpk; diff --git a/test/distributed/cases/fulltext2/fulltext2_bitpk.sql b/test/distributed/cases/fulltext2/fulltext2_bitpk.sql new file mode 100644 index 0000000000000..f4371b73c1250 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_bitpk.sql @@ -0,0 +1,36 @@ +-- fulltext2 with a BIT primary key. build_ddl accepts a BIT column as a primary +-- key, so the segment pk codec (encodePk/decodePk/decodeDocmap) must support it. +-- A synchronous CREATE FULLTEXT2 builds the tag=0 base from source: each segment's +-- docmap is serialized via encodePk(T_bit) and reloaded via decodePk(T_bit) at +-- query time, so a plain CREATE + MATCH round-trips the BIT pk end-to-end. Before +-- the codec fix this returned "unsupported pk type" and stalled index maintenance. + +drop database if exists test_fulltext2_bitpk; +create database test_fulltext2_bitpk; +use test_fulltext2_bitpk; + +set experimental_fulltext2_index = 1; + +drop table if exists docs; +create table docs(id bit(16) primary key, body text); +insert into docs values + (10,'the quick brown fox jumps'), + (20,'a quick brown dog'), + (30,'the lazy fox sleeps'), + (40,'brown bear and lazy cat'), + (50,'quick quick quick fox fox'); +create fulltext2 index ft on docs(body); +show create table docs; + +-- NL exact-phrase; cast the BIT pk to unsigned for stable rendering. +select cast(id as unsigned) as id from docs where match(body) against('quick brown fox') order by id; +select cast(id as unsigned) as id from docs where match(body) against('brown fox') order by id; +select cast(id as unsigned) as id from docs where match(body) against('lazy') order by id; +select cast(id as unsigned) as id from docs where match(body) against('unicorn') order by id; + +-- boolean mode over a BIT pk. +select cast(id as unsigned) as id from docs where match(body) against('+quick +fox' in boolean mode) order by id; +select cast(id as unsigned) as id from docs where match(body) against('+quick -fox' in boolean mode) order by id; +select cast(id as unsigned) as id from docs where match(body) against('quic*' in boolean mode) order by id; + +drop database test_fulltext2_bitpk; diff --git a/test/distributed/cases/fulltext2/fulltext2_conflict.result b/test/distributed/cases/fulltext2/fulltext2_conflict.result new file mode 100644 index 0000000000000..d772c029cde8a --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_conflict.result @@ -0,0 +1,19 @@ +set experimental_fulltext2_index = 1; +drop database if exists ft2_conflict; +create database ft2_conflict; +use ft2_conflict; +create table t (id bigint primary key, body text, title text); +create fulltext index ft1 on t(body); +create fulltext2 index ft2 on t(body); +invalid input: cannot create both a FULLTEXT and a FULLTEXT2 index on the same column(s); drop the existing one first +create fulltext2 index ft3 on t(title); +create table t2 (id bigint primary key, body text); +create fulltext2 index f2 on t2(body); +create fulltext index f1 on t2(body); +invalid input: cannot create both a FULLTEXT and a FULLTEXT2 index on the same column(s); drop the existing one first +create table t3 (id bigint primary key, body text, fulltext(body), fulltext2(body)); +invalid input: cannot create both a FULLTEXT and a FULLTEXT2 index on the same column(s); drop the existing one first +create table t4 (id bigint primary key, body text); +alter table t4 add fulltext ftx(body), add fulltext2 fty(body); +invalid input: cannot create both a FULLTEXT and a FULLTEXT2 index on the same column(s); drop the existing one first +drop database ft2_conflict; diff --git a/test/distributed/cases/fulltext2/fulltext2_conflict.sql b/test/distributed/cases/fulltext2/fulltext2_conflict.sql new file mode 100644 index 0000000000000..6bf35ccd78855 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_conflict.sql @@ -0,0 +1,32 @@ +-- fulltext2 DDL guard: a classic FULLTEXT and a FULLTEXT2 index must NOT coexist on +-- the same column set — they would both resolve the same MATCH(...) verb, making the +-- answering engine (classic SQL fulltext vs WAND positional) order-dependent. Creating +-- the second one (either order, CREATE INDEX or inline in CREATE TABLE) is rejected; +-- the two engines on DIFFERENT columns are fine. +set experimental_fulltext2_index = 1; +drop database if exists ft2_conflict; +create database ft2_conflict; +use ft2_conflict; + +create table t (id bigint primary key, body text, title text); + +-- classic first, then fulltext2 on the SAME column -> rejected +create fulltext index ft1 on t(body); +create fulltext2 index ft2 on t(body); + +-- fulltext2 on a DIFFERENT column is allowed +create fulltext2 index ft3 on t(title); + +-- reverse order: fulltext2 first, then classic on the same column -> rejected +create table t2 (id bigint primary key, body text); +create fulltext2 index f2 on t2(body); +create fulltext index f1 on t2(body); + +-- CREATE TABLE with BOTH engines inline on the same column -> rejected +create table t3 (id bigint primary key, body text, fulltext(body), fulltext2(body)); + +-- ALTER with BOTH engines on the same column in ONE statement (multi-action) -> rejected +create table t4 (id bigint primary key, body text); +alter table t4 add fulltext ftx(body), add fulltext2 fty(body); + +drop database ft2_conflict; diff --git a/test/distributed/cases/fulltext2/fulltext2_covered.result b/test/distributed/cases/fulltext2/fulltext2_covered.result new file mode 100644 index 0000000000000..d9c8fba0e16fb --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_covered.result @@ -0,0 +1,151 @@ +set experimental_fulltext2_index = 1; +drop database if exists fulltext2_covered; +create database fulltext2_covered; +use fulltext2_covered; +create table docs ( +id bigint primary key, +body text not null, +status varchar(20), -- INCLUDE +prio int, -- INCLUDE +category varchar(20) not null -- NOT included +); +insert into docs values +(1, 'quick brown fox', 'active', 10, 'tech'), +(2, 'lazy fox sleeps', 'archived', 20, 'food'), +(3, 'red fox runs fast', 'active', 30, 'tech'), +(4, 'fox fox everywhere', 'active', 40, 'tech'), +(5, 'quiet sleeping cat', 'active', 50, 'home'), +(6, 'a clever silver fox', null, 60, 'tech'), +(7, 'the lazy brown dog', 'archived', null, 'home'); +create fulltext2 index ftidx on docs (body) include (status, prio); +explain select id, status, prio from docs where match(body) against('fox'); +➤ TP QUERY PLAN[12,0,0] 𝄀 +Project 𝄀 + -> Sort 𝄀 + Sort Key: mo_fulltext_alias_0.__mo_ft_score DESC 𝄀 + -> Table Function on fulltext2_search +select id, status, prio from docs where match(body) against('silver'); +➤ id[-5,64,0] ¦ status[12,20,0] ¦ prio[4,32,0] 𝄀 +6 ¦ null ¦ 60 +select id, prio from docs where match(body) against('fox') and prio > 5 order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 +4 ¦ 40 𝄀 +1 ¦ 10 𝄀 +2 ¦ 20 𝄀 +3 ¦ 30 𝄀 +6 ¦ 60 +select id, status from docs where match(body) against('fox') and prio > 5 order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ status[12,20,0] 𝄀 +4 ¦ active 𝄀 +1 ¦ active 𝄀 +2 ¦ archived 𝄀 +3 ¦ active 𝄀 +6 ¦ null +select id, status, prio from docs where match(body) against('fox') order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ status[12,20,0] ¦ prio[4,32,0] 𝄀 +4 ¦ active ¦ 40 𝄀 +1 ¦ active ¦ 10 𝄀 +2 ¦ archived ¦ 20 𝄀 +3 ¦ active ¦ 30 𝄀 +6 ¦ null ¦ 60 +select id, prio, status from docs where match(body) against('fox') order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ prio[4,32,0] ¦ status[12,20,0] 𝄀 +4 ¦ 40 ¦ active 𝄀 +1 ¦ 10 ¦ active 𝄀 +2 ¦ 20 ¦ archived 𝄀 +3 ¦ 30 ¦ active 𝄀 +6 ¦ 60 ¦ null +select id from docs where match(body) against('fox') order by match(body) against('fox') desc, id; +➤ id[-5,64,0] 𝄀 +4 𝄀 +1 𝄀 +2 𝄀 +3 𝄀 +6 +select id, prio from docs where match(body) against('fox') and prio > 5 order by match(body) against('fox') desc, id limit 3; +➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 +4 ¦ 40 𝄀 +1 ¦ 10 𝄀 +2 ¦ 20 +select id, prio from docs where match(body) against('fox') and prio = 30 order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 +3 ¦ 30 +select id, prio from docs where match(body) against('fox') and prio between 15 and 45 order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 +4 ¦ 40 𝄀 +2 ¦ 20 𝄀 +3 ¦ 30 +select id, prio from docs where match(body) against('fox') and prio in (10, 40, 60) order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 +4 ¦ 40 𝄀 +1 ¦ 10 𝄀 +6 ¦ 60 +select id, status from docs where match(body) against('fox') and id in (1, 4, 6) order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ status[12,20,0] 𝄀 +4 ¦ active 𝄀 +1 ¦ active 𝄀 +6 ¦ null +select id, status, prio from docs where match(body) against('fox') and (status is null or prio is null) order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ status[12,20,0] ¦ prio[4,32,0] 𝄀 +6 ¦ null ¦ 60 +select count(*) from docs where match(body) against('fox') and prio > 25; +➤ count(*)[-5,64,0] 𝄀 +3 +select id, status from docs where match(body) against('fox') and status = 'active' order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ status[12,20,0] 𝄀 +4 ¦ active 𝄀 +1 ¦ active 𝄀 +3 ¦ active +select id, status from docs where match(body) against('fox') and status in ('active', 'archived') order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ status[12,20,0] 𝄀 +4 ¦ active 𝄀 +1 ¦ active 𝄀 +2 ¦ archived 𝄀 +3 ¦ active +select id, status from docs where match(body) against('fox') and status like 'ar%' order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ status[12,20,0] 𝄀 +2 ¦ archived +select id, status, prio from docs where match(body) against('fox') and status = 'active' and prio > 15 order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ status[12,20,0] ¦ prio[4,32,0] 𝄀 +4 ¦ active ¦ 40 𝄀 +3 ¦ active ¦ 30 +explain select id from docs where match(body) against('fox') order by status; +➤ TP QUERY PLAN[12,0,0] 𝄀 +Project 𝄀 + -> Sort 𝄀 + Sort Key: mo_fulltext_alias_0.status INTERNAL 𝄀 + -> Sort 𝄀 + Sort Key: mo_fulltext_alias_0.__mo_ft_score DESC 𝄀 + -> Table Function on fulltext2_search +explain select id, status from docs where match(body) against('fox') order by category; +➤ TP QUERY PLAN[12,0,0] 𝄀 +Project 𝄀 + -> Sort 𝄀 + Sort Key: docs.category INTERNAL 𝄀 + -> Sort 𝄀 + Sort Key: mo_fulltext_alias_0.__mo_ft_score DESC 𝄀 + -> Join 𝄀 + Join Type: INNER 𝄀 + Join Cond: (docs.id = mo_fulltext_alias_0.__mo_ft_doc_id) 𝄀 + Runtime Filter Build: #[-1,0] 𝄀 + -> Table Scan on fulltext2_covered.docs 𝄀 + Runtime Filter Probe: docs.id 𝄀 + -> Table Function on fulltext2_search +select id, status, category from docs where match(body) against('fox') order by category, id; +➤ id[-5,64,0] ¦ status[12,20,0] ¦ category[12,20,0] 𝄀 +2 ¦ archived ¦ food 𝄀 +1 ¦ active ¦ tech 𝄀 +3 ¦ active ¦ tech 𝄀 +4 ¦ active ¦ tech 𝄀 +6 ¦ null ¦ tech +create table cs (id bigint primary key, body text not null, tag varchar(10)); +insert into cs values (1, 'red fox', 'Active'), (2, 'blue fox', 'active'), (3, 'grey fox', 'ACTIVE'); +create fulltext2 index csidx on cs (body) include (tag); +select id, tag from cs where match(body) against('fox') and tag = 'active' order by match(body) against('fox') desc, id; -- only id 2 +➤ id[-5,64,0] ¦ tag[12,10,0] 𝄀 +2 ¦ active +select id, tag from cs where match(body) against('fox') and tag like 'Act%' order by match(body) against('fox') desc, id; -- only id 1 +➤ id[-5,64,0] ¦ tag[12,10,0] 𝄀 +1 ¦ Active +drop table cs; +drop database fulltext2_covered; diff --git a/test/distributed/cases/fulltext2/fulltext2_covered.sql b/test/distributed/cases/fulltext2/fulltext2_covered.sql new file mode 100644 index 0000000000000..7fcf99bf90025 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_covered.sql @@ -0,0 +1,108 @@ +-- fulltext2 INCLUDE covered fast path (Phase 6): a fully-covered query (projection = +-- pk/score/include cols, all filters peeled into the TVF, single MATCH) drops the +-- base-table JOIN and reads pk/score/include values straight from fulltext2_search +-- (plan: Project -> Sort(score DESC) -> Table Function, no Join / no base Table Scan). +-- These cases exercise the covered-path specifics that the two P6 bugs lived in: +-- * column pruning: projecting a SUBSET of the include cols (the unprojected one is +-- pruned from the TVF output — the runtime must map the rest BY NAME, not position); +-- * reversed projection order vs the index INCLUDE order; +-- * streaming (no-LIMIT) vs non-streaming (LIMIT) include emission. +-- Ordering: fulltext is score-ranked, so the covered path is shown working WITHOUT an +-- explicit ORDER BY (the planner adds the score-DESC sort), and multi-row results are made +-- deterministic with `ORDER BY match(...) DESC, id` (score-primary; id is only a tiebreaker, +-- NOT an ORDER BY id that would hide score-path behavior). Every result MUST equal the plain +-- base-table semantics. +set experimental_fulltext2_index = 1; +drop database if exists fulltext2_covered; +create database fulltext2_covered; +use fulltext2_covered; + +create table docs ( + id bigint primary key, + body text not null, + status varchar(20), -- INCLUDE + prio int, -- INCLUDE + category varchar(20) not null -- NOT included +); +insert into docs values +(1, 'quick brown fox', 'active', 10, 'tech'), +(2, 'lazy fox sleeps', 'archived', 20, 'food'), +(3, 'red fox runs fast', 'active', 30, 'tech'), +(4, 'fox fox everywhere', 'active', 40, 'tech'), +(5, 'quiet sleeping cat', 'active', 50, 'home'), +(6, 'a clever silver fox', null, 60, 'tech'), +(7, 'the lazy brown dog', 'archived', null, 'home'); + +create fulltext2 index ftidx on docs (body) include (status, prio); + +-- The covered fast path fires with NO explicit ORDER BY: the planner adds the score-DESC +-- sort and the rewrite reads pk/score/include straight from the TVF -> no Join, no base +-- Table Scan (this is the plan shape the covered path is all about). +explain select id, status, prio from docs where match(body) against('fox'); + +-- ... and it EXECUTES correctly without an explicit ORDER BY. Single-doc match ('silver' is +-- only in doc 6) so the result is order-independent and deterministic. +select id, status, prio from docs where match(body) against('silver'); + +-- covered, project ONE include col (prio) -> the other (status) is pruned from the TVF +-- output; streaming (no LIMIT). This is the column-pruning regression case. +select id, prio from docs where match(body) against('fox') and prio > 5 order by match(body) against('fox') desc, id; + +-- covered, project the OTHER include col (status) -> prio pruned. +select id, status from docs where match(body) against('fox') and prio > 5 order by match(body) against('fox') desc, id; + +-- covered, all include cols. +select id, status, prio from docs where match(body) against('fox') order by match(body) against('fox') desc, id; + +-- covered, projection order REVERSED vs INCLUDE(status, prio) order -> name mapping. +select id, prio, status from docs where match(body) against('fox') order by match(body) against('fox') desc, id; + +-- covered, pk-only projection. +select id from docs where match(body) against('fox') order by match(body) against('fox') desc, id; + +-- covered with LIMIT (non-streaming include path). +select id, prio from docs where match(body) against('fox') and prio > 5 order by match(body) against('fox') desc, id limit 3; + +-- covered numeric prefilter variety (all peel into the TVF), projecting include cols. +select id, prio from docs where match(body) against('fox') and prio = 30 order by match(body) against('fox') desc, id; +select id, prio from docs where match(body) against('fox') and prio between 15 and 45 order by match(body) against('fox') desc, id; +select id, prio from docs where match(body) against('fox') and prio in (10, 40, 60) order by match(body) against('fox') desc, id; + +-- covered pk prefilter (pk sentinel) + include projection. +select id, status from docs where match(body) against('fox') and id in (1, 4, 6) order by match(body) against('fox') desc, id; + +-- covered NULL include values surface as SQL NULL (doc 6 status, doc 7 prio). +select id, status, prio from docs where match(body) against('fox') and (status is null or prio is null) order by match(body) against('fox') desc, id; + +-- covered aggregate over the fast path (no ORDER BY needed — order-independent). +select count(*) from docs where match(body) against('fox') and prio > 25; + +-- covered VARCHAR include prefilter (peeled into the TVF -> 0-join): =, IN, LIKE prefix. +select id, status from docs where match(body) against('fox') and status = 'active' order by match(body) against('fox') desc, id; +select id, status from docs where match(body) against('fox') and status in ('active', 'archived') order by match(body) against('fox') desc, id; +select id, status from docs where match(body) against('fox') and status like 'ar%' order by match(body) against('fox') desc, id; +-- covered mixed: varchar = + numeric range (both peel). +select id, status, prio from docs where match(body) against('fox') and status = 'active' and prio > 15 order by match(body) against('fox') desc, id; + +-- ORDER BY sort-key NOT in the SELECT list: the binder projects it as a hidden INTERNAL +-- column, so coverage still applies. An ORDER BY on an INCLUDE col that is not selected stays +-- COVERED (0-join): status is not projected here yet the plan still reads it from the TVF. +explain select id from docs where match(body) against('fox') order by status; + +-- FALLBACK: an ORDER BY on a NON-covered base column (category is not an include col, and is +-- not even in the SELECT list) is NOT covered — it declines the fast path and uses the 2-JOIN +-- path (base Table Scan + Join on doc_id), which still returns correct rows. The EXPLAIN shows +-- the Join; the result matches. +explain select id, status from docs where match(body) against('fox') order by category; +select id, status, category from docs where match(body) against('fox') order by category, id; + +-- byte-exact: MO compares varchar byte-exact regardless of declared collation, so a covered +-- '=' / LIKE matches only the exact bytes (case-sensitive), NOT case-folded. +create table cs (id bigint primary key, body text not null, tag varchar(10)); +insert into cs values (1, 'red fox', 'Active'), (2, 'blue fox', 'active'), (3, 'grey fox', 'ACTIVE'); +create fulltext2 index csidx on cs (body) include (tag); +select id, tag from cs where match(body) against('fox') and tag = 'active' order by match(body) against('fox') desc, id; -- only id 2 +select id, tag from cs where match(body) against('fox') and tag like 'Act%' order by match(body) against('fox') desc, id; -- only id 1 +drop table cs; + +drop database fulltext2_covered; diff --git a/test/distributed/cases/fulltext2/fulltext2_include.result b/test/distributed/cases/fulltext2/fulltext2_include.result new file mode 100644 index 0000000000000..bcfd2dcf68430 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_include.result @@ -0,0 +1,129 @@ +set experimental_fulltext2_index = 1; +drop database if exists fulltext2_include; +create database fulltext2_include; +use fulltext2_include; +create table g (id bigint primary key, body text not null, title varchar(100), status varchar(20), prio int, amt decimal(10,2), created date); +create fulltext2 index gi1 on g (body) include (id); -- pk +invalid input: INCLUDE column 'id' must not be the primary key; predicates on the pk are pushed down automatically via the __mo_pk_host_id virtual column, so listing it here only duplicates storage +create fulltext2 index gi2 on g (body) include (body); -- indexed text column +invalid input: INCLUDE column 'body' cannot be an indexed text column +create fulltext2 index gi3 on g (body, title) include (title); -- indexed column (multi-column index) +invalid input: INCLUDE column 'title' cannot be an indexed text column +create fulltext2 index gi4 on g (body) include (amt); -- unsupported type: decimal +not supported: INCLUDE column 'amt' has unsupported type DECIMAL64 (supported: TINYINT, SMALLINT, INT, BIGINT, TINYINT UNSIGNED, SMALLINT UNSIGNED, INT UNSIGNED, BIGINT UNSIGNED, VARCHAR, CHAR) +create fulltext2 index gi5 on g (body) include (created); -- unsupported type: date +not supported: INCLUDE column 'created' has unsupported type DATE (supported: TINYINT, SMALLINT, INT, BIGINT, TINYINT UNSIGNED, SMALLINT UNSIGNED, INT UNSIGNED, BIGINT UNSIGNED, VARCHAR, CHAR) +create fulltext2 index gi6 on g (body) include (status, status); -- duplicate +invalid input: duplicate INCLUDE column 'status' +drop table g; +create table docs ( +id bigint primary key, +body text not null, +status varchar(20), -- INCLUDE (varchar, nullable) +prio int, -- INCLUDE (numeric, nullable) +category varchar(20) not null -- NOT included -> residual post-filter +); +insert into docs values +(1, 'machine learning basics', 'active', 10, 'tech'), +(2, 'deep learning networks', 'active', 20, 'tech'), +(3, 'learning to cook french food', 'archived', 30, 'food'), +(4, 'machine learning for finance', 'active', 40, 'finance'), +(5, 'reinforcement learning games', null, 50, 'tech'), +(6, 'learning sports analytics', 'active', null, 'sports'), +(7, 'transfer learning models', 'pending', 15, 'tech'), +(8, 'classical cooking learning', 'archived', 25, 'food'); +create fulltext2 index ftidx on docs (body) include (status, prio); +show create table docs; +➤ Table[12,4,0] ¦ Create Table[12,264,0] 𝄀 +docs ¦ CREATE TABLE `docs` ( + `id` bigint NOT NULL, + `body` text NOT NULL, + `status` varchar(20) DEFAULT NULL, + `prio` int DEFAULT NULL, + `category` varchar(20) NOT NULL, + PRIMARY KEY (`id`), + FULLTEXT2 `ftidx`(`body`) INCLUDE (`status`, `prio`) WITH PARSER ngram +) +select id, status, prio from docs where match(body) against('learning') order by id; +➤ id[-5,64,0] ¦ status[12,20,0] ¦ prio[4,32,0] 𝄀 +1 ¦ active ¦ 10 𝄀 +2 ¦ active ¦ 20 𝄀 +3 ¦ archived ¦ 30 𝄀 +4 ¦ active ¦ 40 𝄀 +5 ¦ null ¦ 50 𝄀 +6 ¦ active ¦ null 𝄀 +7 ¦ pending ¦ 15 𝄀 +8 ¦ archived ¦ 25 +select id, prio from docs where match(body) against('learning') and prio = 20 order by id; +➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 +2 ¦ 20 +select id, prio from docs where match(body) against('learning') and prio > 25 order by id; +➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 +3 ¦ 30 𝄀 +4 ¦ 40 𝄀 +5 ¦ 50 +select id, prio from docs where match(body) against('learning') and prio between 15 and 30 order by id; +➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 +2 ¦ 20 𝄀 +3 ¦ 30 𝄀 +7 ¦ 15 𝄀 +8 ¦ 25 +select id, prio from docs where match(body) against('learning') and prio in (10, 40) order by id; +➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 +1 ¦ 10 𝄀 +4 ¦ 40 +select id, status from docs where match(body) against('learning') and status = 'active' order by id; +➤ id[-5,64,0] ¦ status[12,20,0] 𝄀 +1 ¦ active 𝄀 +2 ¦ active 𝄀 +4 ¦ active 𝄀 +6 ¦ active +select id, status from docs where match(body) against('learning') and status like 'arch%' order by id; +➤ id[-5,64,0] ¦ status[12,20,0] 𝄀 +3 ¦ archived 𝄀 +8 ¦ archived +select id from docs where match(body) against('learning') and id in (1, 3, 5) order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +3 𝄀 +5 +select id from docs where match(body) against('learning') and id > 6 order by id; +➤ id[-5,64,0] 𝄀 +7 𝄀 +8 +select id, status from docs where match(body) against('learning') and status is null order by id; +➤ id[-5,64,0] ¦ status[12,20,0] 𝄀 +5 ¦ null +select id, prio from docs where match(body) against('learning') and prio is null order by id; +➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 +6 ¦ null +select id, status from docs where match(body) against('learning') and status is not null order by id; +➤ id[-5,64,0] ¦ status[12,20,0] 𝄀 +1 ¦ active 𝄀 +2 ¦ active 𝄀 +3 ¦ archived 𝄀 +4 ¦ active 𝄀 +6 ¦ active 𝄀 +7 ¦ pending 𝄀 +8 ¦ archived +select id, category from docs where match(body) against('learning') and category = 'tech' order by id; +➤ id[-5,64,0] ¦ category[12,20,0] 𝄀 +1 ¦ tech 𝄀 +2 ¦ tech 𝄀 +5 ¦ tech 𝄀 +7 ¦ tech +select id, status, prio, category from docs where match(body) against('learning') and status = 'active' and prio > 15 order by id; +➤ id[-5,64,0] ¦ status[12,20,0] ¦ prio[4,32,0] ¦ category[12,20,0] 𝄀 +2 ¦ active ¦ 20 ¦ tech 𝄀 +4 ¦ active ¦ 40 ¦ finance +select id from docs where match(body) against('learning') and status = 'active' and category = 'tech' order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +2 +select count(*) from docs where match(body) against('learning') and status = 'active'; +➤ count(*)[-5,64,0] 𝄀 +4 +select count(*) from docs where match(body) against('learning') and prio between 15 and 30; +➤ count(*)[-5,64,0] 𝄀 +4 +drop database fulltext2_include; diff --git a/test/distributed/cases/fulltext2/fulltext2_include.sql b/test/distributed/cases/fulltext2/fulltext2_include.sql new file mode 100644 index 0000000000000..3854329e61a5b --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_include.sql @@ -0,0 +1,85 @@ +-- fulltext2 INCLUDE columns: the actual typed value of chosen scalar columns is +-- stored inside the index (docmap), so a WHERE predicate on an INCLUDE column is +-- evaluated inside fulltext2_search (in-index prefilter) and a SELECT of one is +-- served from the index (covering projection) -- no base-table JOIN. This case +-- pins correctness against the plain base-table semantics: every MATCH+INCLUDE +-- query MUST return exactly what the same predicate over the base rows gives. +-- Covers: covering projection, numeric prefilter (= > BETWEEN IN), varchar +-- prefilter (= LIKE prefix), primary-key prefilter, NULL 3-valued logic, a +-- NON-included residual filter, and DDL round-trip (SHOW CREATE). +set experimental_fulltext2_index = 1; +drop database if exists fulltext2_include; +create database fulltext2_include; +use fulltext2_include; + +-- ============================================================================ +-- INCLUDE validation guards (all rejected at CREATE): the primary key, an +-- indexed text column (single or one of several), an unsupported column type, +-- and a duplicate column. +-- ============================================================================ +create table g (id bigint primary key, body text not null, title varchar(100), status varchar(20), prio int, amt decimal(10,2), created date); +create fulltext2 index gi1 on g (body) include (id); -- pk +create fulltext2 index gi2 on g (body) include (body); -- indexed text column +create fulltext2 index gi3 on g (body, title) include (title); -- indexed column (multi-column index) +create fulltext2 index gi4 on g (body) include (amt); -- unsupported type: decimal +create fulltext2 index gi5 on g (body) include (created); -- unsupported type: date +create fulltext2 index gi6 on g (body) include (status, status); -- duplicate +drop table g; + +create table docs ( + id bigint primary key, + body text not null, + status varchar(20), -- INCLUDE (varchar, nullable) + prio int, -- INCLUDE (numeric, nullable) + category varchar(20) not null -- NOT included -> residual post-filter +); +insert into docs values +(1, 'machine learning basics', 'active', 10, 'tech'), +(2, 'deep learning networks', 'active', 20, 'tech'), +(3, 'learning to cook french food', 'archived', 30, 'food'), +(4, 'machine learning for finance', 'active', 40, 'finance'), +(5, 'reinforcement learning games', null, 50, 'tech'), +(6, 'learning sports analytics', 'active', null, 'sports'), +(7, 'transfer learning models', 'pending', 15, 'tech'), +(8, 'classical cooking learning', 'archived', 25, 'food'); + +create fulltext2 index ftidx on docs (body) include (status, prio); + +-- DDL round-trip: the INCLUDE clause is reconstructed by SHOW CREATE (a +-- rebuild from clause-less DDL would silently drop the covering columns). +show create table docs; + +-- covering projection: status/prio served from the index (incl. NULLs), no JOIN. +select id, status, prio from docs where match(body) against('learning') order by id; + +-- numeric include prefilter (peeled into the TVF): = > BETWEEN IN. +select id, prio from docs where match(body) against('learning') and prio = 20 order by id; +select id, prio from docs where match(body) against('learning') and prio > 25 order by id; +select id, prio from docs where match(body) against('learning') and prio between 15 and 30 order by id; +select id, prio from docs where match(body) against('learning') and prio in (10, 40) order by id; + +-- varchar include prefilter: equality + LIKE prefix (real value stored, so both work). +select id, status from docs where match(body) against('learning') and status = 'active' order by id; +select id, status from docs where match(body) against('learning') and status like 'arch%' order by id; + +-- primary-key prefilter (pk sentinel col=-1): IN and range. +select id from docs where match(body) against('learning') and id in (1, 3, 5) order by id; +select id from docs where match(body) against('learning') and id > 6 order by id; + +-- NULL include values: 3-valued predicate logic + projection emits SQL NULL. +select id, status from docs where match(body) against('learning') and status is null order by id; +select id, prio from docs where match(body) against('learning') and prio is null order by id; +select id, status from docs where match(body) against('learning') and status is not null order by id; + +-- NON-included column (category) -> residual post-filter, still correct. +select id, category from docs where match(body) against('learning') and category = 'tech' order by id; + +-- mixed: varchar include + numeric include + non-included residual together. +select id, status, prio, category from docs where match(body) against('learning') and status = 'active' and prio > 15 order by id; +select id from docs where match(body) against('learning') and status = 'active' and category = 'tech' order by id; + +-- aggregate over an in-index prefilter. +select count(*) from docs where match(body) against('learning') and status = 'active'; +select count(*) from docs where match(body) against('learning') and prio between 15 and 30; + +drop database fulltext2_include; diff --git a/test/distributed/cases/fulltext2/fulltext2_jsonvalue.result b/test/distributed/cases/fulltext2/fulltext2_jsonvalue.result new file mode 100644 index 0000000000000..fc6e6b6dd1acc --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_jsonvalue.result @@ -0,0 +1,62 @@ +drop database if exists fulltext2_jsonvalue; +create database fulltext2_jsonvalue; +use fulltext2_jsonvalue; +set experimental_fulltext2_index = 1; +drop table if exists js; +create table js(id bigint primary key, doc json); +insert into js values +(0,'{"a":"redbluebrownyelloworange","b":"winter"}'), +(1,'{"a":"red","b":"winter"}'), +(2,'{"a":"中文學習教材"}'), +(3,'{"a":"happybirthday.happy-birthday_happybirthday"}'); +create fulltext2 index ft on js(doc) with parser json_value; +select id from js where match(doc) against('redbluebrownyelloworange' in boolean mode) order by id; +id +0 +select id from js where match(doc) against('中文學習教材' in boolean mode) order by id; +id +2 +select id from js where match(doc) against('happybirthday.happy-birthday_happybirthday' in boolean mode) order by id; +id +3 +select id from js where match(doc) against('red' in boolean mode) order by id; +id +1 +select id from js where match(doc) against('winter' in boolean mode) order by id; +id +0 +1 +select id from js where match(doc) against('+red +winter' in boolean mode) order by id; +id +1 +select id from js where match(doc) against('bluebrown' in boolean mode) order by id; +id +select id from js where match(doc) against('中文學習' in boolean mode) order by id; +id +drop table if exists js2; +create table js2(id bigint primary key, j1 json, j2 json); +insert into js2 values +(0,'{"x":"red"}','{"y":"winter"}'), +(1,'{"x":"blue"}','{"y":"summer"}'); +create fulltext2 index ft on js2(j1, j2) with parser json_value; +select id from js2 where match(j1, j2) against('+red +winter' in boolean mode) order by id; +id +0 +select id from js2 where match(j1, j2) against('summer' in boolean mode) order by id; +id +1 +drop table if exists jt; +create table jt(id bigint primary key, doc text); +insert into jt values +(0,'{"a":"red..blue--brown@@yellow::orange"}'), +(1,'{"a":"兒童中文"}'); +create fulltext2 index ft on jt(doc) with parser json_value; +select id from jt where match(doc) against('red..blue--brown@@yellow::orange' in boolean mode) order by id; +id +0 +select id from jt where match(doc) against('兒童中文' in boolean mode) order by id; +id +1 +select id from jt where match(doc) against('red' in boolean mode) order by id; +id +drop database fulltext2_jsonvalue; diff --git a/test/distributed/cases/fulltext2/fulltext2_jsonvalue.sql b/test/distributed/cases/fulltext2/fulltext2_jsonvalue.sql new file mode 100644 index 0000000000000..ad2e2511b0d70 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_jsonvalue.sql @@ -0,0 +1,59 @@ +-- fulltext2 json_value parser: each json leaf value is ONE whole atomic token, +-- matched EXACTLY (no ngram, no bag-of-words) — parity with classic fulltext's +-- json_value (a whole value is indexed; a query matches `word = value`). A value's +-- substring never matches; punctuation and CJK values are single tokens. + +drop database if exists fulltext2_jsonvalue; +create database fulltext2_jsonvalue; +use fulltext2_jsonvalue; + +set experimental_fulltext2_index = 1; + +-- ============ JSON-typed column ============ +drop table if exists js; +create table js(id bigint primary key, doc json); +insert into js values + (0,'{"a":"redbluebrownyelloworange","b":"winter"}'), + (1,'{"a":"red","b":"winter"}'), + (2,'{"a":"中文學習教材"}'), + (3,'{"a":"happybirthday.happy-birthday_happybirthday"}'); +create fulltext2 index ft on js(doc) with parser json_value; + +-- whole-value exact match (punctuation / CJK kept as one token) +select id from js where match(doc) against('redbluebrownyelloworange' in boolean mode) order by id; +select id from js where match(doc) against('中文學習教材' in boolean mode) order by id; +select id from js where match(doc) against('happybirthday.happy-birthday_happybirthday' in boolean mode) order by id; + +-- "red" is a WHOLE value only in row 1 — NOT a substring hit on row 0 +select id from js where match(doc) against('red' in boolean mode) order by id; + +-- distinct values OR / multi-operand MUST +select id from js where match(doc) against('winter' in boolean mode) order by id; +select id from js where match(doc) against('+red +winter' in boolean mode) order by id; + +-- no bag-of-words: a substring of a value never matches +select id from js where match(doc) against('bluebrown' in boolean mode) order by id; +select id from js where match(doc) against('中文學習' in boolean mode) order by id; + +-- ============ multi-column json_value ============ +drop table if exists js2; +create table js2(id bigint primary key, j1 json, j2 json); +insert into js2 values + (0,'{"x":"red"}','{"y":"winter"}'), + (1,'{"x":"blue"}','{"y":"summer"}'); +create fulltext2 index ft on js2(j1, j2) with parser json_value; +select id from js2 where match(j1, j2) against('+red +winter' in boolean mode) order by id; +select id from js2 where match(j1, j2) against('summer' in boolean mode) order by id; + +-- ============ text/varchar column holding json ============ +drop table if exists jt; +create table jt(id bigint primary key, doc text); +insert into jt values + (0,'{"a":"red..blue--brown@@yellow::orange"}'), + (1,'{"a":"兒童中文"}'); +create fulltext2 index ft on jt(doc) with parser json_value; +select id from jt where match(doc) against('red..blue--brown@@yellow::orange' in boolean mode) order by id; +select id from jt where match(doc) against('兒童中文' in boolean mode) order by id; +select id from jt where match(doc) against('red' in boolean mode) order by id; + +drop database fulltext2_jsonvalue; diff --git a/test/distributed/cases/fulltext2/fulltext2_membership.result b/test/distributed/cases/fulltext2/fulltext2_membership.result new file mode 100644 index 0000000000000..8d338d8b9ece2 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_membership.result @@ -0,0 +1,119 @@ +set experimental_fulltext2_index = 1; +drop database if exists fulltext2_pushdown; +create database fulltext2_pushdown; +use fulltext2_pushdown; +create table ft_cbitmap (id bigint primary key, body text not null, category varchar(50) not null); +insert into ft_cbitmap values +(1, 'machine learning is a subset of artificial intelligence', 'tech'), +(2, 'deep learning uses neural networks to learn from data', 'tech'), +(3, 'using machine learning to recommend recipes from ingredients', 'food'), +(4, 'french cooking techniques from around the world', 'food'), +(5, 'machine learning is transforming sports analytics today', 'sports'), +(6, 'a beginners guide to running and training plans', 'sports'), +(7, 'machine learning for fraud detection and risk control', 'finance'), +(8, 'long term investment strategies for building wealth', 'finance'), +(9, 'a curated list of machine learning research papers', 'tech'), +(10, 'best practices for data pipelines and infrastructure', 'tech'), +(11, 'reinforcement learning in robotics and gaming systems', 'tech'), +(12, 'transfer learning reuses pretrained machine learning models', 'tech'); +create fulltext2 index ftidx_cbitmap on ft_cbitmap (body); +set fulltext_bloom_filter_pushdown = 0; +select id, category from ft_cbitmap where match(body) against('learning') and category = 'tech' order by id; +id category +1 tech +2 tech +9 tech +11 tech +12 tech +select id, category from ft_cbitmap where match(body) against('learning') and id > 6 order by id; +id category +7 finance +9 tech +11 tech +12 tech +select id from ft_cbitmap where match(body) against('learning') and category = 'food' order by id; +id +3 +select id from ft_cbitmap where match(body) against('investment') and category = 'tech' order by id; +id +select id from ft_cbitmap where match(body) against('learning') and category = 'nope' order by id; +id +select count(*) from ft_cbitmap where match(body) against('learning') and category = 'tech'; +count(*) +5 +set fulltext_bloom_filter_pushdown = 1; +select id, category from ft_cbitmap where match(body) against('learning') and category = 'tech' order by id; +id category +1 tech +2 tech +9 tech +11 tech +12 tech +select id, category from ft_cbitmap where match(body) against('learning') and id > 6 order by id; +id category +7 finance +9 tech +11 tech +12 tech +select id from ft_cbitmap where match(body) against('learning') and category = 'food' order by id; +id +3 +select id from ft_cbitmap where match(body) against('investment') and category = 'tech' order by id; +id +select id from ft_cbitmap where match(body) against('learning') and category = 'nope' order by id; +id +select count(*) from ft_cbitmap where match(body) against('learning') and category = 'tech'; +count(*) +5 +create table ft_croaring (id bigint primary key, body text not null, category varchar(50) not null); +insert into ft_croaring values +(2000000, 'machine learning is a subset of artificial intelligence', 'tech'), +(4000000, 'deep learning uses neural networks to learn from data', 'tech'), +(6000000, 'using machine learning to recommend recipes from ingredients', 'food'), +(8000000, 'french cooking techniques from around the world', 'food'), +(10000000, 'machine learning is transforming sports analytics today', 'sports'), +(12000000, 'a beginners guide to running and training plans', 'sports'), +(14000000, 'machine learning for fraud detection and risk control', 'finance'), +(16000000, 'long term investment strategies for building wealth', 'finance'), +(18000000, 'a curated list of machine learning research papers', 'tech'), +(20000000, 'best practices for data pipelines and infrastructure', 'tech'), +(22000000, 'reinforcement learning in robotics and gaming systems', 'tech'), +(24000000, 'transfer learning reuses pretrained machine learning models', 'tech'); +create fulltext2 index ftidx_croaring on ft_croaring (body); +select id, category from ft_croaring where match(body) against('learning') and category = 'tech' order by id; +id category +2000000 tech +4000000 tech +18000000 tech +22000000 tech +24000000 tech +select count(*) from ft_croaring where match(body) against('learning') and category = 'tech'; +count(*) +5 +create table ft_bloom (id varchar(64) primary key, body text not null, category varchar(50) not null); +insert into ft_bloom values +('doc-01', 'machine learning is a subset of artificial intelligence', 'tech'), +('doc-02', 'deep learning uses neural networks to learn from data', 'tech'), +('doc-03', 'using machine learning to recommend recipes from ingredients', 'food'), +('doc-04', 'french cooking techniques from around the world', 'food'), +('doc-05', 'machine learning is transforming sports analytics today', 'sports'), +('doc-06', 'a beginners guide to running and training plans', 'sports'), +('doc-07', 'machine learning for fraud detection and risk control', 'finance'), +('doc-08', 'long term investment strategies for building wealth', 'finance'), +('doc-09', 'a curated list of machine learning research papers', 'tech'), +('doc-10', 'best practices for data pipelines and infrastructure', 'tech'), +('doc-11', 'reinforcement learning in robotics and gaming systems', 'tech'), +('doc-12', 'transfer learning reuses pretrained machine learning models', 'tech'); +create fulltext2 index ftidx_bloom on ft_bloom (body); +select id, category from ft_bloom where match(body) against('learning') and category = 'tech' order by id; +id category +doc-01 tech +doc-02 tech +doc-09 tech +doc-11 tech +doc-12 tech +select count(*) from ft_bloom where match(body) against('learning') and category = 'tech'; +count(*) +5 +set fulltext_bloom_filter_pushdown = 0; +drop database fulltext2_pushdown; diff --git a/test/distributed/cases/fulltext2/fulltext2_membership.sql b/test/distributed/cases/fulltext2/fulltext2_membership.sql new file mode 100644 index 0000000000000..862f63cea750d --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_membership.sql @@ -0,0 +1,98 @@ +-- fulltext2 membership-filter pushdown (the WHERE-clause prefilter ported from +-- classic fulltext's fulltext_membership + bm25_pushdown). A MATCH combined with a +-- non-MATCH WHERE predicate builds the pre-filter 2-JOIN that pushes the predicate +-- into the WAND walk as a membership bitset, so fulltext2_search only scores the +-- qualifying docs. The pushdown is a pure performance optimization: results MUST be +-- identical to the pushdown=OFF path. This also exercises all three filter +-- structures docfilter.Build selects by primary-key type: +-- section 1: integer PK, small ids -> dense cbitmap (+ OFF==ON parity) +-- section 2: integer PK, wide id span (>2^23) -> compact CRoaring bitset +-- section 3: varchar (non-integer) PK -> CBloomFilter (approx) +set experimental_fulltext2_index = 1; +drop database if exists fulltext2_pushdown; +create database fulltext2_pushdown; +use fulltext2_pushdown; + +-- ============================================================================ +-- section 1: integer PK, small ids -> cbitmap. Run every query pushdown OFF then +-- ON: the two blocks MUST return identical rows (the prefilter is transparent). +-- ============================================================================ +create table ft_cbitmap (id bigint primary key, body text not null, category varchar(50) not null); +insert into ft_cbitmap values +(1, 'machine learning is a subset of artificial intelligence', 'tech'), +(2, 'deep learning uses neural networks to learn from data', 'tech'), +(3, 'using machine learning to recommend recipes from ingredients', 'food'), +(4, 'french cooking techniques from around the world', 'food'), +(5, 'machine learning is transforming sports analytics today', 'sports'), +(6, 'a beginners guide to running and training plans', 'sports'), +(7, 'machine learning for fraud detection and risk control', 'finance'), +(8, 'long term investment strategies for building wealth', 'finance'), +(9, 'a curated list of machine learning research papers', 'tech'), +(10, 'best practices for data pipelines and infrastructure', 'tech'), +(11, 'reinforcement learning in robotics and gaming systems', 'tech'), +(12, 'transfer learning reuses pretrained machine learning models', 'tech'); +create fulltext2 index ftidx_cbitmap on ft_cbitmap (body); + +-- ---- pushdown OFF (baseline: filter applied after search) ---- +set fulltext_bloom_filter_pushdown = 0; +select id, category from ft_cbitmap where match(body) against('learning') and category = 'tech' order by id; +select id, category from ft_cbitmap where match(body) against('learning') and id > 6 order by id; +select id from ft_cbitmap where match(body) against('learning') and category = 'food' order by id; +select id from ft_cbitmap where match(body) against('investment') and category = 'tech' order by id; +select id from ft_cbitmap where match(body) against('learning') and category = 'nope' order by id; +select count(*) from ft_cbitmap where match(body) against('learning') and category = 'tech'; + +-- ---- pushdown ON (2-JOIN membership-bitset prefilter): SAME rows ---- +set fulltext_bloom_filter_pushdown = 1; +select id, category from ft_cbitmap where match(body) against('learning') and category = 'tech' order by id; +select id, category from ft_cbitmap where match(body) against('learning') and id > 6 order by id; +select id from ft_cbitmap where match(body) against('learning') and category = 'food' order by id; +select id from ft_cbitmap where match(body) against('investment') and category = 'tech' order by id; +select id from ft_cbitmap where match(body) against('learning') and category = 'nope' order by id; +select count(*) from ft_cbitmap where match(body) against('learning') and category = 'tech'; + +-- ============================================================================ +-- section 2: integer PK, wide id span (>2^23) -> CRoaring bitset +-- ============================================================================ +create table ft_croaring (id bigint primary key, body text not null, category varchar(50) not null); +insert into ft_croaring values +(2000000, 'machine learning is a subset of artificial intelligence', 'tech'), +(4000000, 'deep learning uses neural networks to learn from data', 'tech'), +(6000000, 'using machine learning to recommend recipes from ingredients', 'food'), +(8000000, 'french cooking techniques from around the world', 'food'), +(10000000, 'machine learning is transforming sports analytics today', 'sports'), +(12000000, 'a beginners guide to running and training plans', 'sports'), +(14000000, 'machine learning for fraud detection and risk control', 'finance'), +(16000000, 'long term investment strategies for building wealth', 'finance'), +(18000000, 'a curated list of machine learning research papers', 'tech'), +(20000000, 'best practices for data pipelines and infrastructure', 'tech'), +(22000000, 'reinforcement learning in robotics and gaming systems', 'tech'), +(24000000, 'transfer learning reuses pretrained machine learning models', 'tech'); +create fulltext2 index ftidx_croaring on ft_croaring (body); +select id, category from ft_croaring where match(body) against('learning') and category = 'tech' order by id; +select count(*) from ft_croaring where match(body) against('learning') and category = 'tech'; + +-- ============================================================================ +-- section 3: varchar PK -> CBloomFilter (approximate; join to source removes any +-- false positive, so the returned rows are still exact) +-- ============================================================================ +create table ft_bloom (id varchar(64) primary key, body text not null, category varchar(50) not null); +insert into ft_bloom values +('doc-01', 'machine learning is a subset of artificial intelligence', 'tech'), +('doc-02', 'deep learning uses neural networks to learn from data', 'tech'), +('doc-03', 'using machine learning to recommend recipes from ingredients', 'food'), +('doc-04', 'french cooking techniques from around the world', 'food'), +('doc-05', 'machine learning is transforming sports analytics today', 'sports'), +('doc-06', 'a beginners guide to running and training plans', 'sports'), +('doc-07', 'machine learning for fraud detection and risk control', 'finance'), +('doc-08', 'long term investment strategies for building wealth', 'finance'), +('doc-09', 'a curated list of machine learning research papers', 'tech'), +('doc-10', 'best practices for data pipelines and infrastructure', 'tech'), +('doc-11', 'reinforcement learning in robotics and gaming systems', 'tech'), +('doc-12', 'transfer learning reuses pretrained machine learning models', 'tech'); +create fulltext2 index ftidx_bloom on ft_bloom (body); +select id, category from ft_bloom where match(body) against('learning') and category = 'tech' order by id; +select count(*) from ft_bloom where match(body) against('learning') and category = 'tech'; + +set fulltext_bloom_filter_pushdown = 0; +drop database fulltext2_pushdown; diff --git a/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.result b/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.result new file mode 100644 index 0000000000000..94f0e5e3c57b1 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.result @@ -0,0 +1,57 @@ +set experimental_fulltext2_index = 1; +drop database if exists ft2_merge_fs; +create database ft2_merge_fs; +use ft2_merge_fs; +create table t (id bigint primary key, body text); +insert into t values +(1,'alpha beta gamma'),(2,'beta gamma delta'),(3,'gamma delta epsilon'), +(4,'delta epsilon zeta'),(5,'epsilon zeta alpha'),(6,'zeta alpha beta'); +create fulltext2 index ft on t(body) max_index_capacity 2; +show create table t; +➤ Table[12,1,0] ¦ Create Table[12,160,0] 𝄀 +t ¦ CREATE TABLE `t` ( + `id` bigint NOT NULL, + `body` text DEFAULT NULL, + PRIMARY KEY (`id`), + FULLTEXT2 `ft`(`body`) WITH PARSER ngram max_index_capacity = 2 +) +select id from t where match(body) against('alpha' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +5 𝄀 +6 +select id from t where match(body) against('gamma' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +2 𝄀 +3 +select id from t where match(body) against('gamma delta') order by id; +➤ id[-5,64,0] 𝄀 +2 𝄀 +3 +alter table t alter reindex ft fulltext2 merge force_sync; +select id from t where match(body) against('alpha' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +5 𝄀 +6 +select id from t where match(body) against('gamma delta') order by id; +➤ id[-5,64,0] 𝄀 +2 𝄀 +3 +alter table t alter reindex ft fulltext2 force_sync; +select id from t where match(body) against('alpha' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +5 𝄀 +6 +select id from t where match(body) against('zeta' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +4 𝄀 +5 𝄀 +6 +select id from t where match(body) against('epsilon zeta') order by id; +➤ id[-5,64,0] 𝄀 +4 𝄀 +5 +drop database ft2_merge_fs; diff --git a/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.sql b/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.sql new file mode 100644 index 0000000000000..9aa2dafc99c5d --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.sql @@ -0,0 +1,46 @@ +-- fulltext2 MERGE / REBUILD via FORCE_SYNC — SYNCHRONOUS (no async CDC), so this +-- lives in the sync cases dir. FORCE_SYNC runs the reindex INLINE during the ALTER +-- (a plain reindex is async — CompactSegments would run in a background goroutine +-- that may not finish before mo-service exits), exercising, all synchronously: +-- REINDEX ... MERGE FORCE_SYNC -> CompactSegments (compact.go) folds the base +-- sub-segments + loads/persists chunks (storage.go) +-- REINDEX ... FORCE_SYNC (rebuild)-> buildFromSource + chunk persist (storage.go) +-- MATCH after each -> loads the (changed) index into the per-CN +-- cache (search_cache.go) + streamChunksToFile +-- A small max_index_capacity makes several tag=0 base sub-segments so MERGE folds +-- more than one. No post-create DML — all segment work is driven by the synchronous +-- build + FORCE_SYNC reindex. + +set experimental_fulltext2_index = 1; +drop database if exists ft2_merge_fs; +create database ft2_merge_fs; +use ft2_merge_fs; + +-- max_index_capacity 2 => the 6 rows seal into ~3 tag=0 base sub-segments. +create table t (id bigint primary key, body text); +insert into t values + (1,'alpha beta gamma'),(2,'beta gamma delta'),(3,'gamma delta epsilon'), + (4,'delta epsilon zeta'),(5,'epsilon zeta alpha'),(6,'zeta alpha beta'); +create fulltext2 index ft on t(body) max_index_capacity 2; +show create table t; + +-- initial multi-segment query: loads the freshly built base into the per-CN cache. +select id from t where match(body) against('alpha' in bm25 mode) order by id; +select id from t where match(body) against('gamma' in bm25 mode) order by id; +-- exact phrase (positional) over the multi-segment base. +select id from t where match(body) against('gamma delta') order by id; + +-- MERGE FORCE_SYNC: fold the base sub-segments synchronously (CompactSegments + +-- storage load/persist run inline here). +alter table t alter reindex ft fulltext2 merge force_sync; +-- post-merge query: the index changed, so the cache reloads the merged base. +select id from t where match(body) against('alpha' in bm25 mode) order by id; +select id from t where match(body) against('gamma delta') order by id; + +-- REBUILD FORCE_SYNC: rebuild the base from source synchronously. +alter table t alter reindex ft fulltext2 force_sync; +select id from t where match(body) against('alpha' in bm25 mode) order by id; +select id from t where match(body) against('zeta' in bm25 mode) order by id; +select id from t where match(body) against('epsilon zeta') order by id; + +drop database ft2_merge_fs; diff --git a/test/distributed/cases/fulltext2/fulltext2_parser.result b/test/distributed/cases/fulltext2/fulltext2_parser.result new file mode 100644 index 0000000000000..d72f84bf5a5aa --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_parser.result @@ -0,0 +1,79 @@ +drop database if exists fulltext2_parser; +create database fulltext2_parser; +use fulltext2_parser; +set experimental_fulltext2_index = 1; +drop table if exists zh; +create table zh(id bigint primary key, body text); +insert into zh values +(0,'遠東兒童中文學習教材'), +(1,'中文短篇小說適合初學者'), +(2,'兒童學習樂趣'), +(3,'教學指引與生字卡'), +(4,'中文學習中文學習'); +create fulltext2 index ft on zh(body) with parser ngram; +show create table zh; +Table Create Table +zh CREATE TABLE `zh` (\n `id` bigint NOT NULL,\n `body` text DEFAULT NULL,\n PRIMARY KEY (`id`),\n FULLTEXT2 `ft`(`body`) WITH PARSER ngram\n) +select id from zh where match(body) against('中文學習') order by id; +id +0 +4 +select id from zh where match(body) against('兒童') order by id; +id +0 +2 +select id from zh where match(body) against('+中文 +學習' in boolean mode) order by id; +id +0 +4 +select id from zh where match(body) against('+兒童 -中文' in boolean mode) order by id; +id +2 +drop table if exists zj; +create table zj(id bigint primary key, body text); +insert into zj values +(0,'我来到北京清华大学'), +(1,'苹果香蕉都好吃'), +(2,'我爱北京天安门'), +(3,'清华大学在北京'), +(4,'香蕉和苹果'); +create fulltext2 index ft on zj(body) with parser gojieba; +select id from zj where match(body) against('北京') order by id; +id +0 +2 +3 +select id from zj where match(body) against('清华大学') order by id; +id +0 +3 +select id from zj where match(body) against('北京清华大学') order by id; +id +0 +select id from zj where match(body) against('+苹果 +香蕉' in boolean mode) order by id; +id +1 +4 +select id from zj where match(body) against('+北京 -苹果' in boolean mode) order by id; +id +0 +2 +3 +drop table if exists js; +create table js(id bigint primary key, doc json); +insert into js values +(0,'{"a":1,"b":"red apple"}'), +(1,'{"a":2,"b":"中文學習教材"}'), +(2,'{"a":3,"b":"red blue"}'); +create fulltext2 index ft on js(doc) with parser json; +select id from js where match(doc) against('red' in boolean mode) order by id; +id +0 +2 +select id from js where match(doc) against('+red +apple' in boolean mode) order by id; +id +0 +select id from js where match(doc) against('中文學習') order by id; +id +1 +drop database fulltext2_parser; diff --git a/test/distributed/cases/fulltext2/fulltext2_parser.sql b/test/distributed/cases/fulltext2/fulltext2_parser.sql new file mode 100644 index 0000000000000..fea346d6c9ebf --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_parser.sql @@ -0,0 +1,60 @@ +-- fulltext2 parsers (synchronous build): ngram (default — CJK sliding 3-gram, +-- Latin whole-word), gojieba (dictionary word segmentation), json (values +-- flattened + indexed as ngram). NL is an EXACT ordered phrase for every parser +-- (no bag-of-words); gojieba segments into words first. + +drop database if exists fulltext2_parser; +create database fulltext2_parser; +use fulltext2_parser; + +-- CREATE FULLTEXT2 INDEX is gated behind experimental_fulltext2_index (default off). +set experimental_fulltext2_index = 1; + +-- ================= ngram parser: CJK bag-of-words ================= +drop table if exists zh; +create table zh(id bigint primary key, body text); +insert into zh values + (0,'遠東兒童中文學習教材'), + (1,'中文短篇小說適合初學者'), + (2,'兒童學習樂趣'), + (3,'教學指引與生字卡'), + (4,'中文學習中文學習'); +create fulltext2 index ft on zh(body) with parser ngram; +show create table zh; + +select id from zh where match(body) against('中文學習') order by id; +select id from zh where match(body) against('兒童') order by id; +select id from zh where match(body) against('+中文 +學習' in boolean mode) order by id; +select id from zh where match(body) against('+兒童 -中文' in boolean mode) order by id; + +-- ================= gojieba parser: word segmentation, exact phrase ================= +drop table if exists zj; +create table zj(id bigint primary key, body text); +insert into zj values + (0,'我来到北京清华大学'), + (1,'苹果香蕉都好吃'), + (2,'我爱北京天安门'), + (3,'清华大学在北京'), + (4,'香蕉和苹果'); +create fulltext2 index ft on zj(body) with parser gojieba; + +select id from zj where match(body) against('北京') order by id; +select id from zj where match(body) against('清华大学') order by id; +select id from zj where match(body) against('北京清华大学') order by id; +select id from zj where match(body) against('+苹果 +香蕉' in boolean mode) order by id; +select id from zj where match(body) against('+北京 -苹果' in boolean mode) order by id; + +-- ================= json parser (JSON-typed column) ================= +drop table if exists js; +create table js(id bigint primary key, doc json); +insert into js values + (0,'{"a":1,"b":"red apple"}'), + (1,'{"a":2,"b":"中文學習教材"}'), + (2,'{"a":3,"b":"red blue"}'); +create fulltext2 index ft on js(doc) with parser json; + +select id from js where match(doc) against('red' in boolean mode) order by id; +select id from js where match(doc) against('+red +apple' in boolean mode) order by id; +select id from js where match(doc) against('中文學習') order by id; + +drop database fulltext2_parser; diff --git a/test/distributed/cases/fulltext2/fulltext2_position_free.result b/test/distributed/cases/fulltext2/fulltext2_position_free.result new file mode 100644 index 0000000000000..d2db0a13fc786 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_position_free.result @@ -0,0 +1,53 @@ +set experimental_fulltext2_index = 1; +drop database if exists ft2_posfree; +create database ft2_posfree; +use ft2_posfree; +create table t (id bigint primary key, body text); +insert into t values +(1,'我家有三个人'),(2,'三个人都住在我家'),(3,'我家的花园很漂亮'), +(4,'教室里有三个人'),(5,'昨天我家有三个人来吃饭'); +create fulltext2 index bad1 on t(body) with parser ngram position_free = true; +invalid input: fulltext2 POSITION_FREE requires WITH PARSER gojieba (got "ngram"): a position-free index answers only IN BM25 MODE bag-of-words, and ngram/json tokens are meaningless without positions +create fulltext2 index bad2 on t(body) position_free = true; +invalid input: fulltext2 POSITION_FREE requires WITH PARSER gojieba (got "ngram"): a position-free index answers only IN BM25 MODE bag-of-words, and ngram/json tokens are meaningless without positions +create fulltext2 index ft on t(body) with parser gojieba position_free = true; +select algo_params from mo_catalog.mo_indexes where name = 'ft' and algo_params like '%parser%' limit 1; +➤ algo_params[12,2048,0] 𝄀 +{"parser":"gojieba","position_free":"true","session_vars":{"cfg":{"lower_case_table_names":{"t":"I","v":1}}}} +select id from t where match(body) against('我家有三个人' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +2 𝄀 +3 𝄀 +4 𝄀 +5 +select id from t where match(body) against('教室' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +4 +select id from t where match(body) against('我家有三个人' in boolean mode) order by id; +invalid input: fulltext2 index "ft" is POSITION_FREE (bag-of-words only): query it with MATCH(...) AGAINST(... IN BM25 MODE); it has no positions for natural-language / boolean phrase matching +select id from t where match(body) against('我家有三个人' in natural language mode) order by id; +invalid input: fulltext2 index "ft" is POSITION_FREE (bag-of-words only): query it with MATCH(...) AGAINST(... IN BM25 MODE); it has no positions for natural-language / boolean phrase matching +select id from t where match(body) against('我家有三个人') order by id; +invalid input: fulltext2 index "ft" is POSITION_FREE (bag-of-words only): query it with MATCH(...) AGAINST(... IN BM25 MODE); it has no positions for natural-language / boolean phrase matching +alter table t alter reindex ft fulltext2 position_free = false; +select algo_params from mo_catalog.mo_indexes where name = 'ft' and algo_params like '%parser%' limit 1; +➤ algo_params[12,2048,0] 𝄀 +{"parser":"gojieba","session_vars":{"cfg":{"lower_case_table_names":{"t":"I","v":1}}}} +select id from t where match(body) against('我家有三个人' in boolean mode) order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +5 +alter table t alter reindex ft fulltext2 position_free = true; +select algo_params from mo_catalog.mo_indexes where name = 'ft' and algo_params like '%parser%' limit 1; +➤ algo_params[12,2048,0] 𝄀 +{"parser":"gojieba","position_free":"true","session_vars":{"cfg":{"lower_case_table_names":{"t":"I","v":1}}}} +select id from t where match(body) against('我家有三个人' in boolean mode) order by id; +invalid input: fulltext2 index "ft" is POSITION_FREE (bag-of-words only): query it with MATCH(...) AGAINST(... IN BM25 MODE); it has no positions for natural-language / boolean phrase matching +select id from t where match(body) against('三个人' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +2 𝄀 +4 𝄀 +5 +drop database ft2_posfree; diff --git a/test/distributed/cases/fulltext2/fulltext2_position_free.sql b/test/distributed/cases/fulltext2/fulltext2_position_free.sql new file mode 100644 index 0000000000000..b33721d566ce6 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_position_free.sql @@ -0,0 +1,48 @@ +-- fulltext2 POSITION_FREE: a position-free index (WITH PARSER gojieba POSITION_FREE=TRUE) +-- drops the positional payload — bag-of-words retrieval only, ~half the footprint, FST +-- kept — and is queried with IN BM25 MODE. NL / BOOLEAN / default (phrase) modes are +-- rejected because there are no positions. Only the gojieba WORD parser is allowed +-- (ngram/json emit overlapping trigrams/values whose bag-of-words result is noise). +-- REINDEX POSITION_FREE=TRUE|FALSE toggles it both ways (a REBUILD from source). +set experimental_fulltext2_index = 1; +drop database if exists ft2_posfree; +create database ft2_posfree; +use ft2_posfree; + +create table t (id bigint primary key, body text); +insert into t values + (1,'我家有三个人'),(2,'三个人都住在我家'),(3,'我家的花园很漂亮'), + (4,'教室里有三个人'),(5,'昨天我家有三个人来吃饭'); + +-- POSITION_FREE requires the gojieba parser: ngram is rejected ... +create fulltext2 index bad1 on t(body) with parser ngram position_free = true; +-- ... and the default parser (ngram) with a bare POSITION_FREE=TRUE is rejected too +create fulltext2 index bad2 on t(body) position_free = true; + +-- valid: gojieba + POSITION_FREE=TRUE +create fulltext2 index ft on t(body) with parser gojieba position_free = true; +select algo_params from mo_catalog.mo_indexes where name = 'ft' and algo_params like '%parser%' limit 1; + +-- IN BM25 MODE: bag-of-words — every doc containing any of 我家/有/三个/人 +select id from t where match(body) against('我家有三个人' in bm25 mode) order by id; +-- a single token: only doc 4 has 教室 +select id from t where match(body) against('教室' in bm25 mode) order by id; + +-- NL / BOOLEAN / default phrase modes need positions -> rejected on a position-free index +select id from t where match(body) against('我家有三个人' in boolean mode) order by id; +select id from t where match(body) against('我家有三个人' in natural language mode) order by id; +select id from t where match(body) against('我家有三个人') order by id; + +-- REINDEX POSITION_FREE=FALSE -> positional rebuild: BOOLEAN exact phrase now works. +-- '我家有三个人' is a contiguous substring only in docs 1 and 5. +alter table t alter reindex ft fulltext2 position_free = false; +select algo_params from mo_catalog.mo_indexes where name = 'ft' and algo_params like '%parser%' limit 1; +select id from t where match(body) against('我家有三个人' in boolean mode) order by id; + +-- REINDEX POSITION_FREE=TRUE -> position-free again: BOOLEAN rejected, IN BM25 MODE works. +alter table t alter reindex ft fulltext2 position_free = true; +select algo_params from mo_catalog.mo_indexes where name = 'ft' and algo_params like '%parser%' limit 1; +select id from t where match(body) against('我家有三个人' in boolean mode) order by id; +select id from t where match(body) against('三个人' in bm25 mode) order by id; + +drop database ft2_posfree; diff --git a/test/distributed/cases/fulltext2/fulltext2_posting_capacity.result b/test/distributed/cases/fulltext2/fulltext2_posting_capacity.result new file mode 100644 index 0000000000000..402bf9eaa0cda --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_posting_capacity.result @@ -0,0 +1,65 @@ +drop database if exists ft2_postcap; +create database ft2_postcap; +use ft2_postcap; +set experimental_fulltext2_index = 1; +drop table if exists docs; +create table docs(id bigint primary key, body text); +insert into docs values +(0,'the quick brown fox jumps'), +(1,'a quick brown dog runs'), +(2,'the lazy fox sleeps here'), +(3,'brown bear and lazy cat'), +(4,'quick quick quick fox fox'); +create fulltext2 index ft on docs(body) max_index_capacity 1000000 max_postings_capacity 8; +select distinct algo, algo_params from mo_catalog.mo_indexes where name = 'ft'; +➤ algo[12,11,0] ¦ algo_params[12,2048,0] 𝄀 +fulltext2 ¦ {"max_index_capacity":"1000000","max_postings_capacity":"8","parser":"ngram","session_vars":{"cfg":{"lower_case_table_names":{"t":"I","v":1}}}} +select id from docs where match(body) against('quick brown fox') order by id; +➤ id[-5,64,0] 𝄀 +0 +select id from docs where match(body) against('brown') order by id; +➤ id[-5,64,0] 𝄀 +0 𝄀 +1 𝄀 +3 +select id from docs where match(body) against('+quick +fox' in boolean mode) order by id; +➤ id[-5,64,0] 𝄀 +0 𝄀 +4 +select id from docs where match(body) against('lazy') order by id; +➤ id[-5,64,0] 𝄀 +2 𝄀 +3 +alter table docs alter reindex ft fulltext2; +select id from docs where match(body) against('brown') order by id; +➤ id[-5,64,0] 𝄀 +0 𝄀 +1 𝄀 +3 +alter table docs alter reindex ft fulltext2 max_postings_capacity 4000000; +select distinct algo_params from mo_catalog.mo_indexes where name = 'ft' and table_id = (select rel_id from mo_catalog.mo_tables where relname = 'docs'); +➤ algo_params[12,2048,0] 𝄀 +{"max_index_capacity":"1000000","max_postings_capacity":"4000000","parser":"ngram","session_vars":{"cfg":{"lower_case_table_names":{"t":"I","v":1}}}} +select id from docs where match(body) against('brown') order by id; +➤ id[-5,64,0] 𝄀 +0 𝄀 +1 𝄀 +3 +drop table if exists docs2; +create table docs2(id bigint primary key, body text); +insert into docs2 values +(0,'the quick brown fox jumps'), +(1,'a quick brown dog runs'), +(2,'the lazy fox sleeps here'); +create fulltext2 index ft on docs2(body) max_postings_capacity 8000000; +select distinct algo_params from mo_catalog.mo_indexes where name = 'ft' and table_id = (select rel_id from mo_catalog.mo_tables where relname = 'docs2'); +➤ algo_params[12,2048,0] 𝄀 +{"max_postings_capacity":"8000000","parser":"ngram","session_vars":{"cfg":{"lower_case_table_names":{"t":"I","v":1}}}} +select id from docs2 where match(body) against('brown') order by id; +➤ id[-5,64,0] 𝄀 +0 𝄀 +1 +create table bad(id bigint primary key, body text); +create fulltext2 index ftbad on bad(body) max_postings_capacity 0; +SQL parser error: You have an error in your SQL syntax; check the manual that corresponds to your MatrixOne server version for the right syntax to use. MAX_POSTINGS_CAPACITY should be greater than 0 at line 1 column 65 near " 0;"; +drop database ft2_postcap; diff --git a/test/distributed/cases/fulltext2/fulltext2_posting_capacity.sql b/test/distributed/cases/fulltext2/fulltext2_posting_capacity.sql new file mode 100644 index 0000000000000..a7cfe836b06c7 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_posting_capacity.sql @@ -0,0 +1,61 @@ +-- fulltext2 max_postings_capacity: a segment seals on whichever cap (docs OR +-- postings) is reached first. max_index_capacity (docs) alone is a poor memory +-- proxy — a doc can hold one token or thousands — so max_postings_capacity bounds +-- per-segment build memory by term-occurrence count. Here a TINY posting cap forces +-- the tag=0 base to split into several segments; the multi-segment split is +-- transparent to MATCH (the Index spans segments), so results are unchanged. + +drop database if exists ft2_postcap; +create database ft2_postcap; +use ft2_postcap; +set experimental_fulltext2_index = 1; + +drop table if exists docs; +create table docs(id bigint primary key, body text); +insert into docs values + (0,'the quick brown fox jumps'), + (1,'a quick brown dog runs'), + (2,'the lazy fox sleeps here'), + (3,'brown bear and lazy cat'), + (4,'quick quick quick fox fox'); + +-- max_postings_capacity 8: ~5 postings/doc => a fresh segment seals about every 2 +-- docs, so the base is built as multiple tag=0 sub-indexes rather than one. +create fulltext2 index ft on docs(body) max_index_capacity 1000000 max_postings_capacity 8; + +-- Both caps persist in the index's algo_params (SHOW CREATE renders only the parser +-- for a fulltext family index, so assert the params via mo_indexes). +select distinct algo, algo_params from mo_catalog.mo_indexes where name = 'ft'; + +-- Queries span every sealed segment (multi-segment split is transparent). +select id from docs where match(body) against('quick brown fox') order by id; +select id from docs where match(body) against('brown') order by id; +select id from docs where match(body) against('+quick +fox' in boolean mode) order by id; +select id from docs where match(body) against('lazy') order by id; + +-- REBUILD (no MERGE) re-derives the base from source under the same posting cap. +alter table docs alter reindex ft fulltext2; +select id from docs where match(body) against('brown') order by id; + +-- ALTER REINDEX can RE-BOUND the posting cap: the override is merged into algo_params +-- and the rebuild seals at the new value (max_index_capacity stays as-is). +alter table docs alter reindex ft fulltext2 max_postings_capacity 4000000; +select distinct algo_params from mo_catalog.mo_indexes where name = 'ft' and table_id = (select rel_id from mo_catalog.mo_tables where relname = 'docs'); +select id from docs where match(body) against('brown') order by id; + +-- A generous posting cap keeps the base as a single segment; results identical. +drop table if exists docs2; +create table docs2(id bigint primary key, body text); +insert into docs2 values + (0,'the quick brown fox jumps'), + (1,'a quick brown dog runs'), + (2,'the lazy fox sleeps here'); +create fulltext2 index ft on docs2(body) max_postings_capacity 8000000; +select distinct algo_params from mo_catalog.mo_indexes where name = 'ft' and table_id = (select rel_id from mo_catalog.mo_tables where relname = 'docs2'); +select id from docs2 where match(body) against('brown') order by id; + +-- Guard: max_postings_capacity must be > 0. +create table bad(id bigint primary key, body text); +create fulltext2 index ftbad on bad(body) max_postings_capacity 0; + +drop database ft2_postcap; diff --git a/test/distributed/cases/fulltext2/fulltext2_prepare.result b/test/distributed/cases/fulltext2/fulltext2_prepare.result new file mode 100644 index 0000000000000..1541f56f86f04 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_prepare.result @@ -0,0 +1,117 @@ +drop database if exists test_fulltext2_prepare; +create database test_fulltext2_prepare; +use test_fulltext2_prepare; +set experimental_fulltext2_index = 1; +drop table if exists docs; +create table docs(id bigint primary key, body text); +insert into docs values +(0,'the quick brown fox jumps'), +(1,'a quick brown dog'), +(2,'the lazy fox sleeps'), +(3,'brown bear and lazy cat'), +(4,'quick quick quick fox fox'); +create fulltext2 index ft on docs(body); +prepare ft_bool from 'select id from docs where match(body) against(? in boolean mode) order by id'; +set @q = '+quick +fox'; +execute ft_bool using @q; +➤ id[-5,64,0] 𝄀 +0 𝄀 +4 +set @q = ''; +execute ft_bool using @q; +➤ id[-5,64,0] +set @q = '+lazy'; +execute ft_bool using @q; +➤ id[-5,64,0] 𝄀 +2 𝄀 +3 +set @q = null; +execute ft_bool using @q; +➤ id[-5,64,0] +set @q = '+quick +fox'; +execute ft_bool using @q; +➤ id[-5,64,0] 𝄀 +0 𝄀 +4 +deallocate prepare ft_bool; +prepare ft_nl from 'select id from docs where match(body) against(? in natural language mode) order by id'; +set @q = 'quick brown fox'; +execute ft_nl using @q; +➤ id[-5,64,0] 𝄀 +0 +set @q = 'brown fox'; +execute ft_nl using @q; +➤ id[-5,64,0] 𝄀 +0 +set @q = ''; +execute ft_nl using @q; +➤ id[-5,64,0] +set @q = 'quick brown fox'; +execute ft_nl using @q; +➤ id[-5,64,0] 𝄀 +0 +deallocate prepare ft_nl; +drop table if exists bw; +create table bw(id bigint primary key, body text); +insert into bw values +(0,'the quick brown fox jumps'), +(1,'a quick brown dog'), +(2,'the lazy fox sleeps'), +(3,'brown bear and lazy cat'), +(4,'quick quick quick fox fox'); +create fulltext2 index ft on bw(body) with parser gojieba position_free = true; +prepare ft_bm25 from 'select id from bw where match(body) against(? in bm25 mode) order by id'; +set @q = 'quick fox'; +execute ft_bm25 using @q; +➤ id[-5,64,0] 𝄀 +0 𝄀 +1 𝄀 +2 𝄀 +4 +set @q = 'lazy'; +execute ft_bm25 using @q; +➤ id[-5,64,0] 𝄀 +2 𝄀 +3 +set @q = 'quick fox'; +execute ft_bm25 using @q; +➤ id[-5,64,0] 𝄀 +0 𝄀 +1 𝄀 +2 𝄀 +4 +deallocate prepare ft_bm25; +drop table if exists lim; +create table lim(id bigint primary key, body text); +insert into lim values +(1,'fox zeta zeta'), +(2,'fox fox zeta'), +(3,'fox fox fox'), +(4,'alpha alpha alpha'), +(5,'beta beta beta'); +create fulltext2 index ft on lim(body); +prepare ft_lim from 'select id from lim where match(body) against(? in bm25 mode) limit ?'; +set @q = 'fox'; +set @k = 1; +execute ft_lim using @q, @k; +➤ id[-5,64,0] 𝄀 +3 +set @k = 2; +execute ft_lim using @q, @k; +➤ id[-5,64,0] 𝄀 +3 𝄀 +2 +set @k = 3; +execute ft_lim using @q, @k; +➤ id[-5,64,0] 𝄀 +3 𝄀 +2 𝄀 +1 +set @k = 10; +execute ft_lim using @q, @k; +➤ id[-5,64,0] 𝄀 +3 𝄀 +2 𝄀 +1 +deallocate prepare ft_lim; +drop database test_fulltext2_prepare; diff --git a/test/distributed/cases/fulltext2/fulltext2_prepare.sql b/test/distributed/cases/fulltext2/fulltext2_prepare.sql new file mode 100644 index 0000000000000..426340bc23a61 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_prepare.sql @@ -0,0 +1,96 @@ +-- fulltext2 PREPARED-STATEMENT test: MATCH(col) AGAINST(? IN ) with a runtime +-- parameter pattern. The fulltext2 rewrite passes the pattern as a bound expression +-- (not a literal), so a prepared '?' pattern flows into the fulltext2_search TVF and is +-- evaluated per execution. Covers BOOLEAN / NATURAL LANGUAGE (positional index) and +-- BM25 (POSITION_FREE index), plus valid -> empty -> valid -> null -> valid reuse so a +-- failed/empty execution does not wedge the prepared statement. + +drop database if exists test_fulltext2_prepare; +create database test_fulltext2_prepare; +use test_fulltext2_prepare; + +-- CREATE FULLTEXT2 INDEX is gated behind experimental_fulltext2_index (default off). +set experimental_fulltext2_index = 1; + +drop table if exists docs; +create table docs(id bigint primary key, body text); +insert into docs values + (0,'the quick brown fox jumps'), + (1,'a quick brown dog'), + (2,'the lazy fox sleeps'), + (3,'brown bear and lazy cat'), + (4,'quick quick quick fox fox'); +create fulltext2 index ft on docs(body); + +-- ============ prepared BOOLEAN mode ============ +prepare ft_bool from 'select id from docs where match(body) against(? in boolean mode) order by id'; +set @q = '+quick +fox'; +execute ft_bool using @q; +set @q = ''; +execute ft_bool using @q; +set @q = '+lazy'; +execute ft_bool using @q; +set @q = null; +execute ft_bool using @q; +set @q = '+quick +fox'; +execute ft_bool using @q; +deallocate prepare ft_bool; + +-- ============ prepared NATURAL LANGUAGE mode (exact phrase) ============ +prepare ft_nl from 'select id from docs where match(body) against(? in natural language mode) order by id'; +set @q = 'quick brown fox'; +execute ft_nl using @q; +set @q = 'brown fox'; +execute ft_nl using @q; +set @q = ''; +execute ft_nl using @q; +set @q = 'quick brown fox'; +execute ft_nl using @q; +deallocate prepare ft_nl; + +-- ============ prepared BM25 mode on a POSITION_FREE index (bag-of-words) ============ +drop table if exists bw; +create table bw(id bigint primary key, body text); +insert into bw values + (0,'the quick brown fox jumps'), + (1,'a quick brown dog'), + (2,'the lazy fox sleeps'), + (3,'brown bear and lazy cat'), + (4,'quick quick quick fox fox'); +create fulltext2 index ft on bw(body) with parser gojieba position_free = true; +prepare ft_bm25 from 'select id from bw where match(body) against(? in bm25 mode) order by id'; +set @q = 'quick fox'; +execute ft_bm25 using @q; +set @q = 'lazy'; +execute ft_bm25 using @q; +set @q = 'quick fox'; +execute ft_bm25 using @q; +deallocate prepare ft_bm25; + +-- ============ prepared LIMIT ? : the '?' LIMIT is a bound parameter, not a literal, so it +-- must be resolved at EXECUTE and drive the fulltext2_search top-k. Before the fix a +-- non-literal LIMIT was ignored (st.limit stayed 0 -> unbounded stream). Distinct per-doc term +-- frequencies (same doc length) give distinct BM25 scores so the top-k order is deterministic. +drop table if exists lim; +create table lim(id bigint primary key, body text); +insert into lim values + (1,'fox zeta zeta'), + (2,'fox fox zeta'), + (3,'fox fox fox'), + (4,'alpha alpha alpha'), + (5,'beta beta beta'); +create fulltext2 index ft on lim(body); +prepare ft_lim from 'select id from lim where match(body) against(? in bm25 mode) limit ?'; +set @q = 'fox'; +set @k = 1; +execute ft_lim using @q, @k; +set @k = 2; +execute ft_lim using @q, @k; +set @k = 3; +execute ft_lim using @q, @k; +-- reuse with a different bound LIMIT proves the '?' is re-evaluated per EXECUTE. +set @k = 10; +execute ft_lim using @q, @k; +deallocate prepare ft_lim; + +drop database test_fulltext2_prepare; diff --git a/test/distributed/cases/fulltext2/fulltext2_pushdown.result b/test/distributed/cases/fulltext2/fulltext2_pushdown.result new file mode 100644 index 0000000000000..437eba58ad5a7 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_pushdown.result @@ -0,0 +1,131 @@ +set experimental_fulltext2_index = 1; +set fulltext_bloom_filter_pushdown = 1; +drop database if exists ft2_pushdown_test; +create database ft2_pushdown_test; +use ft2_pushdown_test; +create table articles ( +id bigint primary key, +title varchar(200) not null, +body text not null, +category varchar(50) not null, +status int not null default 1 +); +insert into articles values +(1, 'Introduction to Machine Learning', 'Machine learning is a subset of artificial intelligence that focuses on building systems that learn from data', 'tech', 1), +(2, 'Deep Learning Fundamentals', 'Deep learning uses neural networks with many layers to learn representations of data', 'tech', 1), +(3, 'Natural Language Processing', 'NLP combines computational linguistics with machine learning to process human language', 'tech', 1), +(4, 'Database Systems Overview', 'Modern database systems support both transactional and analytical workloads efficiently', 'tech', 1), +(5, 'Cloud Computing Basics', 'Cloud computing delivers computing services over the internet including servers storage and databases', 'tech', 0), +(6, 'Cooking with Machine Learning', 'Using machine learning algorithms to recommend recipes based on available ingredients', 'food', 1), +(7, 'The Art of French Cooking', 'French cooking techniques have influenced cuisines around the world for centuries', 'food', 1), +(8, 'Italian Pasta Guide', 'A comprehensive guide to making authentic Italian pasta from scratch', 'food', 1), +(9, 'Healthy Eating Habits', 'Building healthy eating habits requires understanding nutrition and meal planning', 'food', 0), +(10, 'Sports Analytics with ML', 'Machine learning is transforming sports analytics and player performance prediction', 'sports', 1), +(11, 'Basketball Strategy Guide', 'Modern basketball strategy relies heavily on data analytics and machine learning models', 'sports', 1), +(12, 'Running for Beginners', 'A beginners guide to running including training plans and injury prevention tips', 'sports', 1), +(13, 'Swimming Techniques', 'Advanced swimming techniques for competitive swimmers and triathletes', 'sports', 0), +(14, 'Machine Learning in Finance', 'Financial institutions use machine learning for fraud detection and risk assessment', 'finance', 1), +(15, 'Investment Strategies', 'Long term investment strategies for building wealth through diversified portfolios', 'finance', 1), +(16, 'Cryptocurrency Overview', 'Understanding blockchain technology and cryptocurrency markets', 'finance', 0), +(17, 'Travel Guide to Japan', 'Exploring Japanese culture food and technology during your visit to Japan', 'travel', 1), +(18, 'European Road Trip', 'Planning the perfect European road trip through France Italy and Spain', 'travel', 1), +(19, 'Machine Learning Research Papers', 'A curated list of the most influential machine learning research papers of the decade', 'tech', 1), +(20, 'Data Engineering Best Practices', 'Best practices for building robust data pipelines and data infrastructure', 'tech', 1), +(21, 'Reinforcement Learning Applications', 'Reinforcement learning applications in robotics gaming and autonomous systems', 'tech', 1), +(22, 'Computer Vision Advances', 'Recent advances in computer vision powered by deep learning and neural networks', 'tech', 1), +(23, 'Graph Neural Networks', 'Graph neural networks for learning on graph structured data and social networks', 'tech', 1), +(24, 'AutoML and Neural Architecture Search', 'Automated machine learning and neural architecture search for model optimization', 'tech', 1), +(25, 'Federated Learning', 'Federated learning enables training machine learning models across decentralized data', 'tech', 1), +(26, 'MLOps and Model Deployment', 'Best practices for deploying machine learning models in production environments', 'tech', 1), +(27, 'Transfer Learning Guide', 'Transfer learning allows reusing pre-trained machine learning models for new tasks', 'tech', 1), +(28, 'Explainable AI', 'Making machine learning models interpretable and explainable for critical applications', 'tech', 1), +(29, 'Edge Computing and ML', 'Running machine learning inference on edge devices for real time applications', 'tech', 1), +(30, 'Quantum Machine Learning', 'Exploring the intersection of quantum computing and machine learning algorithms', 'tech', 0); +create fulltext2 index ftidx_articles on articles (title, body); +select id, title, category from articles +where match(title, body) against('machine learning') +order by id; +id title category +1 Introduction to Machine Learning tech +3 Natural Language Processing tech +6 Cooking with Machine Learning food +10 Sports Analytics with ML sports +11 Basketball Strategy Guide sports +14 Machine Learning in Finance finance +19 Machine Learning Research Papers tech +24 AutoML and Neural Architecture Search tech +25 Federated Learning tech +26 MLOps and Model Deployment tech +27 Transfer Learning Guide tech +28 Explainable AI tech +29 Edge Computing and ML tech +30 Quantum Machine Learning tech +select id, title, category from articles +where match(title, body) against('machine learning') +and category = 'tech' +order by id; +id title category +1 Introduction to Machine Learning tech +3 Natural Language Processing tech +19 Machine Learning Research Papers tech +24 AutoML and Neural Architecture Search tech +25 Federated Learning tech +26 MLOps and Model Deployment tech +27 Transfer Learning Guide tech +28 Explainable AI tech +29 Edge Computing and ML tech +30 Quantum Machine Learning tech +select id, title, category, status from articles +where match(title, body) against('machine learning') +and category = 'tech' +and status = 1 +order by id; +id title category status +1 Introduction to Machine Learning tech 1 +3 Natural Language Processing tech 1 +19 Machine Learning Research Papers tech 1 +24 AutoML and Neural Architecture Search tech 1 +25 Federated Learning tech 1 +26 MLOps and Model Deployment tech 1 +27 Transfer Learning Guide tech 1 +28 Explainable AI tech 1 +29 Edge Computing and ML tech 1 +select id, title, category from articles +where match(title, body) against('machine learning') +and id > 10 +order by id; +id title category +11 Basketball Strategy Guide sports +14 Machine Learning in Finance finance +19 Machine Learning Research Papers tech +24 AutoML and Neural Architecture Search tech +25 Federated Learning tech +26 MLOps and Model Deployment tech +27 Transfer Learning Guide tech +28 Explainable AI tech +29 Edge Computing and ML tech +30 Quantum Machine Learning tech +select count(*) from articles +where match(title, body) against('machine learning') +and category = 'tech'; +count(*) +10 +select count(*) from ( +select id from articles +where match(title, body) against('machine learning') +limit 5 offset 3 +) as page; +count(*) +5 +select id from articles +where match(title, body) against('machine learning') +order by id +limit 5 offset 3; +id +10 +11 +14 +19 +24 +set fulltext_bloom_filter_pushdown = 0; +drop database ft2_pushdown_test; diff --git a/test/distributed/cases/fulltext2/fulltext2_pushdown.sql b/test/distributed/cases/fulltext2/fulltext2_pushdown.sql new file mode 100644 index 0000000000000..8360b0e6d0f83 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_pushdown.sql @@ -0,0 +1,101 @@ +-- fulltext2 pushdown: verify correctness of fulltext2 search with additional filter +-- predicates (ported from classic fulltext's fulltext_pushdown). The membership-filter +-- pushdown is transparent to query results, so this verifies result correctness across +-- extra category / status / range filters, count, and LIMIT+OFFSET pagination — the +-- WHERE predicate is pushed into the WAND walk as a membership bitset. fulltext2 shares +-- classic fulltext's MATCH surface AND its NL semantics (a multi-word AGAINST is an exact +-- phrase in BOTH engines — verified row-identical), so these matched sets are the same +-- rows classic fulltext returns; the ported result is a genuine parity fixture. +set experimental_fulltext2_index = 1; +set fulltext_bloom_filter_pushdown = 1; + +drop database if exists ft2_pushdown_test; +create database ft2_pushdown_test; +use ft2_pushdown_test; + +create table articles ( + id bigint primary key, + title varchar(200) not null, + body text not null, + category varchar(50) not null, + status int not null default 1 +); + +insert into articles values +(1, 'Introduction to Machine Learning', 'Machine learning is a subset of artificial intelligence that focuses on building systems that learn from data', 'tech', 1), +(2, 'Deep Learning Fundamentals', 'Deep learning uses neural networks with many layers to learn representations of data', 'tech', 1), +(3, 'Natural Language Processing', 'NLP combines computational linguistics with machine learning to process human language', 'tech', 1), +(4, 'Database Systems Overview', 'Modern database systems support both transactional and analytical workloads efficiently', 'tech', 1), +(5, 'Cloud Computing Basics', 'Cloud computing delivers computing services over the internet including servers storage and databases', 'tech', 0), +(6, 'Cooking with Machine Learning', 'Using machine learning algorithms to recommend recipes based on available ingredients', 'food', 1), +(7, 'The Art of French Cooking', 'French cooking techniques have influenced cuisines around the world for centuries', 'food', 1), +(8, 'Italian Pasta Guide', 'A comprehensive guide to making authentic Italian pasta from scratch', 'food', 1), +(9, 'Healthy Eating Habits', 'Building healthy eating habits requires understanding nutrition and meal planning', 'food', 0), +(10, 'Sports Analytics with ML', 'Machine learning is transforming sports analytics and player performance prediction', 'sports', 1), +(11, 'Basketball Strategy Guide', 'Modern basketball strategy relies heavily on data analytics and machine learning models', 'sports', 1), +(12, 'Running for Beginners', 'A beginners guide to running including training plans and injury prevention tips', 'sports', 1), +(13, 'Swimming Techniques', 'Advanced swimming techniques for competitive swimmers and triathletes', 'sports', 0), +(14, 'Machine Learning in Finance', 'Financial institutions use machine learning for fraud detection and risk assessment', 'finance', 1), +(15, 'Investment Strategies', 'Long term investment strategies for building wealth through diversified portfolios', 'finance', 1), +(16, 'Cryptocurrency Overview', 'Understanding blockchain technology and cryptocurrency markets', 'finance', 0), +(17, 'Travel Guide to Japan', 'Exploring Japanese culture food and technology during your visit to Japan', 'travel', 1), +(18, 'European Road Trip', 'Planning the perfect European road trip through France Italy and Spain', 'travel', 1), +(19, 'Machine Learning Research Papers', 'A curated list of the most influential machine learning research papers of the decade', 'tech', 1), +(20, 'Data Engineering Best Practices', 'Best practices for building robust data pipelines and data infrastructure', 'tech', 1), +(21, 'Reinforcement Learning Applications', 'Reinforcement learning applications in robotics gaming and autonomous systems', 'tech', 1), +(22, 'Computer Vision Advances', 'Recent advances in computer vision powered by deep learning and neural networks', 'tech', 1), +(23, 'Graph Neural Networks', 'Graph neural networks for learning on graph structured data and social networks', 'tech', 1), +(24, 'AutoML and Neural Architecture Search', 'Automated machine learning and neural architecture search for model optimization', 'tech', 1), +(25, 'Federated Learning', 'Federated learning enables training machine learning models across decentralized data', 'tech', 1), +(26, 'MLOps and Model Deployment', 'Best practices for deploying machine learning models in production environments', 'tech', 1), +(27, 'Transfer Learning Guide', 'Transfer learning allows reusing pre-trained machine learning models for new tasks', 'tech', 1), +(28, 'Explainable AI', 'Making machine learning models interpretable and explainable for critical applications', 'tech', 1), +(29, 'Edge Computing and ML', 'Running machine learning inference on edge devices for real time applications', 'tech', 1), +(30, 'Quantum Machine Learning', 'Exploring the intersection of quantum computing and machine learning algorithms', 'tech', 0); + +create fulltext2 index ftidx_articles on articles (title, body); + +-- scenario 1: pure fulltext search (no extra filter) +select id, title, category from articles +where match(title, body) against('machine learning') +order by id; + +-- scenario 2: fulltext search + category filter (triggers pushdown) +select id, title, category from articles +where match(title, body) against('machine learning') + and category = 'tech' +order by id; + +-- scenario 3: fulltext search + multiple filters +select id, title, category, status from articles +where match(title, body) against('machine learning') + and category = 'tech' + and status = 1 +order by id; + +-- scenario 4: fulltext search + range filter +select id, title, category from articles +where match(title, body) against('machine learning') + and id > 10 +order by id; + +-- scenario 5: fulltext search + filter + count +select count(*) from articles +where match(title, body) against('machine learning') + and category = 'tech'; + +-- scenario 6: internal fulltext top-k must retain LIMIT+OFFSET candidates +select count(*) from ( + select id from articles + where match(title, body) against('machine learning') + limit 5 offset 3 +) as page; + +-- scenario 7: explicit ordering owns pagination; the relevance stream must not be truncated first +select id from articles +where match(title, body) against('machine learning') +order by id +limit 5 offset 3; + +set fulltext_bloom_filter_pushdown = 0; +drop database ft2_pushdown_test; diff --git a/test/distributed/cases/fulltext2/fulltext2_review_regress.result b/test/distributed/cases/fulltext2/fulltext2_review_regress.result new file mode 100644 index 0000000000000..be85c2991a013 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_review_regress.result @@ -0,0 +1,57 @@ +set experimental_fulltext2_index = 1; +drop database if exists ft2_review; +create database ft2_review; +use ft2_review; +create table b3 (id bigint primary key, body text not null, score int, doc_id int); +insert into b3 values (1,'quick brown fox',111,7),(2,'lazy fox sleeps',222,8),(3,'quick fox',333,9); +create fulltext2 index b3idx on b3 (body) include (score, doc_id); +select id, score from b3 where match(body) against('fox') order by id; +➤ id[-5,64,0] ¦ score[4,32,0] 𝄀 +1 ¦ 111 𝄀 +2 ¦ 222 𝄀 +3 ¦ 333 +select id, doc_id from b3 where match(body) against('fox') order by id; +➤ id[-5,64,0] ¦ doc_id[4,32,0] 𝄀 +1 ¦ 7 𝄀 +2 ¦ 8 𝄀 +3 ¦ 9 +select id, score, doc_id from b3 where match(body) against('fox') order by id; +➤ id[-5,64,0] ¦ score[4,32,0] ¦ doc_id[4,32,0] 𝄀 +1 ¦ 111 ¦ 7 𝄀 +2 ¦ 222 ¦ 8 𝄀 +3 ¦ 333 ¦ 9 +select id, score, doc_id from b3 where match(body) against('fox') and score > 150 order by id limit 5; +➤ id[-5,64,0] ¦ score[4,32,0] ¦ doc_id[4,32,0] 𝄀 +2 ¦ 222 ¦ 8 𝄀 +3 ¦ 333 ¦ 9 +create table b2 (bk bit(16) primary key, body text not null, prio int); +insert into b2 values (5,'quick brown fox',10),(6,'lazy fox sleeps',20),(7,'quick fox',30); +create fulltext2 index b2idx on b2 (body) include (prio); +select cast(bk as unsigned) as k, prio from b2 where match(body) against('fox') and bk = 7 order by k; +➤ k[-5,64,0] ¦ prio[4,32,0] 𝄀 +7 ¦ 30 +select cast(bk as unsigned) as k, prio from b2 where match(body) against('fox') and bk in (5,7) order by k; +invalid argument operator in, bad value [BIT BIT] +select cast(bk as unsigned) as k, prio from b2 where match(body) against('fox') and bk > 5 order by k; +➤ k[-5,64,0] ¦ prio[4,32,0] 𝄀 +6 ¦ 20 𝄀 +7 ¦ 30 +create table b1 (id bigint primary key, body text not null); +insert into b1 values (1,'quick brown fox'),(2,'lazy fox sleeps'),(3,'quick fox'); +create fulltext2 index b1idx on b1 (body); +prepare pb1 from 'select id from b1 where match(body) against(?) order by id limit 5'; +set @q = 'fox'; +execute pb1 using @q; +➤ id[-5,64,0] 𝄀 +1 𝄀 +2 𝄀 +3 +set @q = null; +execute pb1 using @q; +➤ id[-5,64,0] +set @q = 'lazy'; +execute pb1 using @q; +➤ id[-5,64,0] 𝄀 +2 +deallocate prepare pb1; +drop database ft2_review; diff --git a/test/distributed/cases/fulltext2/fulltext2_review_regress.sql b/test/distributed/cases/fulltext2/fulltext2_review_regress.sql new file mode 100644 index 0000000000000..adeebe6eff7ec --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_review_regress.sql @@ -0,0 +1,48 @@ +-- Regression cases for the three self-review blockers on the fulltext2 INCLUDE / box-free +-- covered path. Each previously produced a wrong result, a query error, or a panic. +set experimental_fulltext2_index = 1; +drop database if exists ft2_review; +create database ft2_review; +use ft2_review; + +-- B3: an INCLUDE column whose name collides with the fulltext2_search TVF's reserved output +-- names (doc_id / score). Before the fix the covered-path name classifier misrouted it +-- (the include vector got the relevance score, leaving a length-0 vector -> shuffle PANIC). +-- After the fix the TVF's built-in outputs use reserved __mo_ft_* aliases, so the include +-- column keeps its user name and returns its STORED value. +create table b3 (id bigint primary key, body text not null, score int, doc_id int); +insert into b3 values (1,'quick brown fox',111,7),(2,'lazy fox sleeps',222,8),(3,'quick fox',333,9); +create fulltext2 index b3idx on b3 (body) include (score, doc_id); +select id, score from b3 where match(body) against('fox') order by id; +select id, doc_id from b3 where match(body) against('fox') order by id; +select id, score, doc_id from b3 where match(body) against('fox') order by id; +-- also with a LIMIT (non-streaming covered path) and a filter on the colliding include col +select id, score, doc_id from b3 where match(body) against('fox') and score > 150 order by id limit 5; + +-- B2: a pk predicate on a BIT primary key gets peeled into the covered TVF prefilter. Before +-- the fix the evaluator rejected T_bit ("col -1 type not comparable") and the whole query +-- failed; now BIT is evaluated on the unsigned path. +create table b2 (bk bit(16) primary key, body text not null, prio int); +insert into b2 values (5,'quick brown fox',10),(6,'lazy fox sleeps',20),(7,'quick fox',30); +create fulltext2 index b2idx on b2 (body) include (prio); +select cast(bk as unsigned) as k, prio from b2 where match(body) against('fox') and bk = 7 order by k; +select cast(bk as unsigned) as k, prio from b2 where match(body) against('fox') and bk in (5,7) order by k; +select cast(bk as unsigned) as k, prio from b2 where match(body) against('fox') and bk > 5 order by k; + +-- B1: a reused prepared MATCH with a pushed LIMIT, executed with a non-NULL pattern then a +-- NULL pattern. Before the fix the NULL execution re-emitted the previous execution's rows +-- (the reused output buffer was not cleared on the NULL-pattern early return); now NULL +-- returns the empty set. +create table b1 (id bigint primary key, body text not null); +insert into b1 values (1,'quick brown fox'),(2,'lazy fox sleeps'),(3,'quick fox'); +create fulltext2 index b1idx on b1 (body); +prepare pb1 from 'select id from b1 where match(body) against(?) order by id limit 5'; +set @q = 'fox'; +execute pb1 using @q; +set @q = null; +execute pb1 using @q; +set @q = 'lazy'; +execute pb1 using @q; +deallocate prepare pb1; + +drop database ft2_review; diff --git a/test/distributed/cases/fulltext2/fulltext2_showcreate.result b/test/distributed/cases/fulltext2/fulltext2_showcreate.result new file mode 100644 index 0000000000000..6a675054f0dd6 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_showcreate.result @@ -0,0 +1,49 @@ +set experimental_fulltext2_index = 1; +drop database if exists ft2_showcreate; +create database ft2_showcreate; +use ft2_showcreate; +create table t1 (id bigint primary key, body text); +create fulltext2 index ft on t1(body) max_index_capacity 2; +show create table t1; +➤ Table[12,2,0] ¦ Create Table[12,161,0] 𝄀 +t1 ¦ CREATE TABLE `t1` ( + `id` bigint NOT NULL, + `body` text DEFAULT NULL, + PRIMARY KEY (`id`), + FULLTEXT2 `ft`(`body`) WITH PARSER ngram max_index_capacity = 2 +) +create table t2 (id bigint primary key, body text); +create fulltext2 index ft on t2(body) auto_update = true second 5; +show create table t2; +➤ Table[12,2,0] ¦ Create Table[12,169,0] 𝄀 +t2 ¦ CREATE TABLE `t2` ( + `id` bigint NOT NULL, + `body` text DEFAULT NULL, + PRIMARY KEY (`id`), + FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true second = 5 +) +create table t3 (id bigint primary key, body text); +create fulltext2 index ft on t3(body) auto_update = true day 1 hour 2; +show create table t3; +➤ Table[12,2,0] ¦ Create Table[12,176,0] 𝄀 +t3 ¦ CREATE TABLE `t3` ( + `id` bigint NOT NULL, + `body` text DEFAULT NULL, + PRIMARY KEY (`id`), + FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true day = 1 hour = 2 +) +create table t4 ( +id bigint NOT NULL, +body text DEFAULT NULL, +PRIMARY KEY (id), +FULLTEXT2 ft(body) WITH PARSER ngram auto_update = true second = 5 +); +show create table t4; +➤ Table[12,2,0] ¦ Create Table[12,169,0] 𝄀 +t4 ¦ CREATE TABLE `t4` ( + `id` bigint NOT NULL, + `body` text DEFAULT NULL, + PRIMARY KEY (`id`), + FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true second = 5 +) +drop database ft2_showcreate; diff --git a/test/distributed/cases/fulltext2/fulltext2_showcreate.sql b/test/distributed/cases/fulltext2/fulltext2_showcreate.sql new file mode 100644 index 0000000000000..286735dda409e --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_showcreate.sql @@ -0,0 +1,38 @@ +-- SHOW CREATE TABLE round-trip for fulltext2 index options. The shared option renderer +-- (IndexParamsToStringList, also used by checkpoint restore) must emit every persisted +-- build/maintenance option so a rebuilt / restored / cloned index keeps them instead of +-- silently reverting to defaults. In particular the AUTO_UPDATE compaction cadence +-- (DAY/HOUR/SECOND) must round-trip — SECOND was previously dropped. The rendered DDL must +-- also re-parse (a table created from SHOW CREATE output produces the identical SHOW CREATE). +set experimental_fulltext2_index = 1; + +drop database if exists ft2_showcreate; +create database ft2_showcreate; +use ft2_showcreate; + +-- (1) parser + capacity options round-trip. +create table t1 (id bigint primary key, body text); +create fulltext2 index ft on t1(body) max_index_capacity 2; +show create table t1; + +-- (2) AUTO_UPDATE cadence: SECOND must appear (regression guard for the dropped-SECOND bug). +create table t2 (id bigint primary key, body text); +create fulltext2 index ft on t2(body) auto_update = true second 5; +show create table t2; + +-- (3) DAY/HOUR cadence also round-trips. +create table t3 (id bigint primary key, body text); +create fulltext2 index ft on t3(body) auto_update = true day 1 hour 2; +show create table t3; + +-- (4) re-parse: a table created verbatim from a rendered SHOW CREATE clause is accepted and +-- renders identically (the rendered DDL is valid, re-executable SQL). +create table t4 ( + id bigint NOT NULL, + body text DEFAULT NULL, + PRIMARY KEY (id), + FULLTEXT2 ft(body) WITH PARSER ngram auto_update = true second = 5 +); +show create table t4; + +drop database ft2_showcreate; diff --git a/test/distributed/cases/fulltext2/fulltext2_wiki.result b/test/distributed/cases/fulltext2/fulltext2_wiki.result new file mode 100644 index 0000000000000..ca6512ab926fe --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_wiki.result @@ -0,0 +1,63 @@ +drop database if exists ft2_wiki; +create database ft2_wiki; +use ft2_wiki; +set experimental_fulltext2_index = 1; +create table en(id bigint primary key, body text); +load data infile '$resources/fulltext2/wiki_en_1k.csv' into table en fields terminated by ',' enclosed by '"' lines terminated by '\n'; +select count(*) docs from en; +➤ docs[-5,64,0] 𝄀 +1000 +create fulltext2 index ft on en(body) with parser gojieba; +select count(*) from en where match(body) against('anarchism'); +➤ count(*)[-5,64,0] 𝄀 +7 +select count(*) from en where match(body) against('philosophy'); +➤ count(*)[-5,64,0] 𝄀 +35 +select count(*) from en where match(body) against('government'); +➤ count(*)[-5,64,0] 𝄀 +56 +select count(*) from en where match(body) against('zzznotawordzzz'); +➤ count(*)[-5,64,0] 𝄀 +0 +select count(*) from en where match(body) against('+state +government' in boolean mode); +➤ count(*)[-5,64,0] 𝄀 +18 +select count(*) from en where match(body) against('+anarchism -philosophy' in boolean mode); +➤ count(*)[-5,64,0] 𝄀 +5 +create table zh(id bigint primary key, body text); +load data infile '$resources/fulltext2/wiki_zh_1k.csv' into table zh fields terminated by ',' enclosed by '"' lines terminated by '\n'; +select count(*) docs from zh; +➤ docs[-5,64,0] 𝄀 +1000 +create fulltext2 index ft on zh(body) with parser ngram; +select count(*) from zh where match(body) against('数学'); +➤ count(*)[-5,64,0] 𝄀 +43 +select count(*) from zh where match(body) against('中国'); +➤ count(*)[-5,64,0] 𝄀 +264 +select count(*) from zh where match(body) against('历史'); +➤ count(*)[-5,64,0] 𝄀 +103 +create table en_cap(id bigint primary key, body text); +insert into en_cap select * from en; +create fulltext2 index ft on en_cap(body) with parser gojieba max_postings_capacity 20000; +select count(*) from en_cap where match(body) against('anarchism'); +➤ count(*)[-5,64,0] 𝄀 +7 +select count(*) from en_cap where match(body) against('philosophy'); +➤ count(*)[-5,64,0] 𝄀 +35 +select count(*) from en_cap where match(body) against('+state +government' in boolean mode); +➤ count(*)[-5,64,0] 𝄀 +18 +alter table en_cap alter reindex ft fulltext2; +select count(*) from en_cap where match(body) against('anarchism'); +➤ count(*)[-5,64,0] 𝄀 +7 +select count(*) from en_cap where match(body) against('+state +government' in boolean mode); +➤ count(*)[-5,64,0] 𝄀 +18 +drop database ft2_wiki; diff --git a/test/distributed/cases/fulltext2/fulltext2_wiki.sql b/test/distributed/cases/fulltext2/fulltext2_wiki.sql new file mode 100644 index 0000000000000..d6682d1338ba1 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_wiki.sql @@ -0,0 +1,54 @@ +-- fulltext2 on a real 1K-doc Wikipedia corpus (English + Chinese), loaded from +-- $resources/fulltext2/wiki_{en,zh}_1k.csv. Unlike the tiny hand-written cases this +-- drives a realistic build (≈1M postings) so the segment build/spill and the +-- max_postings_capacity multi-segment seal are exercised on real text. All assertions +-- are COUNT-based (order-independent) so results are stable regardless of top-k tie +-- ordering. Sync-only (no async CDC) to keep it deterministic. + +drop database if exists ft2_wiki; +create database ft2_wiki; +use ft2_wiki; +set experimental_fulltext2_index = 1; + +-- ================= English (gojieba word tokens) ================= +create table en(id bigint primary key, body text); +load data infile '$resources/fulltext2/wiki_en_1k.csv' into table en fields terminated by ',' enclosed by '"' lines terminated by '\n'; +select count(*) docs from en; +create fulltext2 index ft on en(body) with parser gojieba; + +-- NL single-term membership counts (deterministic). +select count(*) from en where match(body) against('anarchism'); +select count(*) from en where match(body) against('philosophy'); +select count(*) from en where match(body) against('government'); +select count(*) from en where match(body) against('zzznotawordzzz'); +-- boolean mode: both terms present / one excluded. +select count(*) from en where match(body) against('+state +government' in boolean mode); +select count(*) from en where match(body) against('+anarchism -philosophy' in boolean mode); + +-- ================= Chinese (ngram / CJK) ================= +create table zh(id bigint primary key, body text); +load data infile '$resources/fulltext2/wiki_zh_1k.csv' into table zh fields terminated by ',' enclosed by '"' lines terminated by '\n'; +select count(*) docs from zh; +create fulltext2 index ft on zh(body) with parser ngram; + +select count(*) from zh where match(body) against('数学'); +select count(*) from zh where match(body) against('中国'); +select count(*) from zh where match(body) against('历史'); + +-- ================= max_postings_capacity multi-segment transparency ================= +-- A small posting cap seals the SAME corpus into many tag=0 base segments; every +-- COUNT must be identical to the single-segment English index above (the Index spans +-- segments). This exercises the posting-seal + per-segment spill on real data. +create table en_cap(id bigint primary key, body text); +insert into en_cap select * from en; +create fulltext2 index ft on en_cap(body) with parser gojieba max_postings_capacity 20000; +select count(*) from en_cap where match(body) against('anarchism'); +select count(*) from en_cap where match(body) against('philosophy'); +select count(*) from en_cap where match(body) against('+state +government' in boolean mode); + +-- ================= REBUILD (reindex) leaves results unchanged ================= +alter table en_cap alter reindex ft fulltext2; +select count(*) from en_cap where match(body) against('anarchism'); +select count(*) from en_cap where match(body) against('+state +government' in boolean mode); + +drop database ft2_wiki; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.result new file mode 100644 index 0000000000000..6f7677cc7c7f4 --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.result @@ -0,0 +1,41 @@ +set experimental_fulltext2_index = 1; +create table src (id bigint primary key, body varchar, title text, FULLTEXT2 ftidx (body, title)); +insert into src values (0, 'color is red', 't1'), (1, 'car is yellow', 'crazy car'), (2, 'sky is blue', 'no limit'), (3, 'blue is not red', 'colorful'), +(4, '遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。', '遠東兒童中文'), +(5, '每冊均採用近百張全幅彩圖及照片,生動活潑、自然真實,加深兒童學習印象,洋溢學習樂趣。', '遠東兒童中文'), +(6, '各個單元主題內容涵蓋中華文化及生活應用的介紹。本套教材含課本、教學指引、生字卡、學生作業本與CD,中英對照,精美大字版。本系列有繁體字及簡體字兩種版本印行。', '中文短篇小說'), +(7, '59個簡單的英文和中文短篇小說', '適合初學者'), +(8, NULL, 'NOT INCLUDED'), +(9, 'NOT INCLUDED BODY', NULL), +(10, NULL, NULL); +create table src2 (id1 varchar, id2 bigint, body char(128), title text, primary key (id1, id2), FULLTEXT2 ftidx(body, title)); +insert into src2 values ('id0', 0, 'red', 't1'), ('id1', 1, 'yellow', 't2'), ('id2', 2, 'blue', 't3'), ('id3', 3, 'blue red', 't4'), ('id4', 4, 'bright red null', NULL); +select sleep(30); +sleep(30) +0 +show create table src; +Table Create Table +src CREATE TABLE `src` (\n `id` bigint NOT NULL,\n `body` varchar(65535) DEFAULT NULL,\n `title` text DEFAULT NULL,\n PRIMARY KEY (`id`),\n FULLTEXT2 `ftidx`(`body`,`title`) WITH PARSER ngram\n) +select id1, id2 from src2 where match(body, title) against('red') order by id1; +id1 id2 +id0 0 +id3 3 +show create table src2; +Table Create Table +src2 CREATE TABLE `src2` (\n `id1` varchar(65535) NOT NULL,\n `id2` bigint NOT NULL,\n `body` char(128) DEFAULT NULL,\n `title` text DEFAULT NULL,\n PRIMARY KEY (`id1`,`id2`),\n FULLTEXT2 `ftidx`(`body`,`title`) WITH PARSER ngram\n) +update src set body = 'color is green' where id = 0; +delete from src where id = 3; +select sleep(30); +sleep(30) +0 +select id from src where match(body, title) against('red') order by id; +id +select id from src where match(body, title) against('green') order by id; +id +0 +alter table src rename to src1; +select id from src1 where match(body, title) against('green') order by id; +id +0 +drop table src1; +drop table src2; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.sql new file mode 100644 index 0000000000000..ee733803ffdfa --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.sql @@ -0,0 +1,58 @@ +-- fulltext2 CDC (always-async) — ported from pessimistic_transaction/fulltext +-- (fulltext_async). fulltext2 is AlwaysAsync (no ASYNC keyword): the inline index +-- builds an empty tag=0 base at CREATE, then post-create INSERT/UPDATE/DELETE flow +-- into the tag=1 CdcTail via ISCP CDC. sleep() waits for CDC to settle, then MATCH +-- reflects the mutations. + +-- CREATE FULLTEXT2 INDEX is gated behind experimental_fulltext2_index (default off). +set experimental_fulltext2_index = 1; + +-- create src async (multi-column body+title) +create table src (id bigint primary key, body varchar, title text, FULLTEXT2 ftidx (body, title)); + +insert into src values (0, 'color is red', 't1'), (1, 'car is yellow', 'crazy car'), (2, 'sky is blue', 'no limit'), (3, 'blue is not red', 'colorful'), +(4, '遠東兒童中文是針對6到9歲的小朋友精心設計的中文學習教材,共三冊,目前已出版一、二冊。', '遠東兒童中文'), +(5, '每冊均採用近百張全幅彩圖及照片,生動活潑、自然真實,加深兒童學習印象,洋溢學習樂趣。', '遠東兒童中文'), +(6, '各個單元主題內容涵蓋中華文化及生活應用的介紹。本套教材含課本、教學指引、生字卡、學生作業本與CD,中英對照,精美大字版。本系列有繁體字及簡體字兩種版本印行。', '中文短篇小說'), +(7, '59個簡單的英文和中文短篇小說', '適合初學者'), +(8, NULL, 'NOT INCLUDED'), +(9, 'NOT INCLUDED BODY', NULL), +(10, NULL, NULL); + +-- composite primary key +create table src2 (id1 varchar, id2 bigint, body char(128), title text, primary key (id1, id2), FULLTEXT2 ftidx(body, title)); + +insert into src2 values ('id0', 0, 'red', 't1'), ('id1', 1, 'yellow', 't2'), ('id2', 2, 'blue', 't3'), ('id3', 3, 'blue red', 't4'), ('id4', 4, 'bright red null', NULL); + +-- wait for the initial CDC sync of src and src2 +select sleep(30); + +-- src's initial-sync state is verified via src2 below (a never-re-mutated table). +-- src itself is deliberately NOT searched here: fulltext2's per-CN index cache is +-- not cross-invalidated by the CDC consumer (which evicts only its own CN), so a +-- pre-update search on the mo-tester CN would pin the pre-update snapshot and the +-- post-update queries below would then read it stale on multi-CN. src is first +-- searched only after its final mutation has settled, so its cache loads fresh. +show create table src; + +-- select src2 (composite pk): id0 (red), id3 (blue red) +select id1, id2 from src2 where match(body, title) against('red') order by id1; +show create table src2; + +-- post-create UPDATE + DELETE flow through CDC too +update src set body = 'color is green' where id = 0; +delete from src where id = 3; +select sleep(30); + +-- doc 0's old text (red) is superseded, doc 3 is deleted -> 'red' now empty. +-- This is src's FIRST MATCH, so its per-CN cache loads the fresh post-update index. +select id from src where match(body, title) against('red') order by id; +-- doc 0's new text +select id from src where match(body, title) against('green') order by id; + +-- rename keeps the index + CDC +alter table src rename to src1; +select id from src1 where match(body, title) against('green') order by id; + +drop table src1; +drop table src2; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.result new file mode 100644 index 0000000000000..10296ae27452d --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.result @@ -0,0 +1,60 @@ +set experimental_fulltext2_index = 1; +drop database if exists ft2_bugfix; +create database ft2_bugfix; +use ft2_bugfix; +create table pk_dt (id datetime primary key, body text); +create fulltext2 index ftidx on pk_dt(body); +insert into pk_dt values ('2020-01-02 03:04:05','alpha beta'), ('2021-06-07 08:09:10','beta gamma'); +create table pk_dec (id decimal(18,4) primary key, body text); +create fulltext2 index ftidx on pk_dec(body); +insert into pk_dec values (123.4567,'alpha beta'), (890.1234,'beta gamma'); +create table j1 (id bigint primary key, doc json); +create fulltext2 index ftidx on j1(doc) with parser json; +insert into j1 values (1,'{"title":"quantum physics","tag":"science"}'), (2,'{"title":"cooking recipes","tag":"food"}'); +create table j2 (id bigint primary key, a json, b json); +create fulltext2 index ftidx on j2(a,b) with parser json; +insert into j2 values (1,'{"x":"hello world"}','{"y":"foo bar"}'), (2,'{"x":"lorem ipsum"}','{"y":"dolor sit"}'); +select sleep(45); +sleep(45) +0 +select id from pk_dt where match(body) against('beta') order by id; +id +2020-01-02 03:04:05 +2021-06-07 08:09:10 +select id from pk_dt where match(body) against('gamma') order by id; +id +2021-06-07 08:09:10 +select id from pk_dec where match(body) against('beta') order by id; +id +123.4567 +890.1234 +select id from j1 where match(doc) against('quantum') order by id; +id +1 +select id from j1 where match(doc) against('food') order by id; +id +2 +select id from j2 where match(a,b) against('hello') order by id; +id +1 +select id from j2 where match(a,b) against('bar') order by id; +id +1 +select id from j2 where match(a,b) against('dolor') order by id; +id +2 +create table reb (id bigint primary key, body text); +create fulltext2 index ftidx on reb(body); +insert into reb values (1,'stale zebra'),(2,'stale zebra'); +select sleep(45); +sleep(45) +0 +select count(*) from reb where match(body) against('zebra'); +count(*) +2 +delete from reb; +alter table reb alter reindex ftidx fulltext2; +select count(*) from reb where match(body) against('zebra'); +count(*) +0 +drop database ft2_bugfix; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.sql new file mode 100644 index 0000000000000..d064f614521b2 --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.sql @@ -0,0 +1,67 @@ +-- BVT for fulltext2 CDC/rebuild correctness fixes (self-review findings): +-- #1 temporal/decimal PK: CDC INSERT must NOT panic encodePk — the consumer now +-- extracts PKs as native Go values (ReprNative), not SQL-display strings. +-- #3 T_json column via CDC is searchable — each json column is flattened +-- per-column, binary-aware (bytejson.ByteJson), matching the CREATE build. +-- #4 multi-column json via CDC is searchable on EVERY column — per-column flatten +-- instead of joining raw columns then flattening the blob once (zero tokens). +-- #6 REBUILD over an emptied table drops the stale tag=0 base — no longer serves +-- deleted docs when the rebuild sees zero source rows. +-- All four are CDC/async-path bugs, so the index is created on an empty table and the +-- rows flow in through ISCP CDC; sleep() waits for CDC to settle. +set experimental_fulltext2_index = 1; +drop database if exists ft2_bugfix; +create database ft2_bugfix; +use ft2_bugfix; + +-- #1: datetime PK — CDC path (index created before the rows exist) +create table pk_dt (id datetime primary key, body text); +create fulltext2 index ftidx on pk_dt(body); +insert into pk_dt values ('2020-01-02 03:04:05','alpha beta'), ('2021-06-07 08:09:10','beta gamma'); + +-- #1: decimal PK +create table pk_dec (id decimal(18,4) primary key, body text); +create fulltext2 index ftidx on pk_dec(body); +insert into pk_dec values (123.4567,'alpha beta'), (890.1234,'beta gamma'); + +-- #3: single T_json column +create table j1 (id bigint primary key, doc json); +create fulltext2 index ftidx on j1(doc) with parser json; +insert into j1 values (1,'{"title":"quantum physics","tag":"science"}'), (2,'{"title":"cooking recipes","tag":"food"}'); + +-- #4: multi-column json +create table j2 (id bigint primary key, a json, b json); +create fulltext2 index ftidx on j2(a,b) with parser json; +insert into j2 values (1,'{"x":"hello world"}','{"y":"foo bar"}'), (2,'{"x":"lorem ipsum"}','{"y":"dolor sit"}'); + +-- wait for the initial CDC drain of all four tables +select sleep(45); + +-- #1: both temporal/decimal-PK tables searchable => the consumer did not crash +select id from pk_dt where match(body) against('beta') order by id; +select id from pk_dt where match(body) against('gamma') order by id; +select id from pk_dec where match(body) against('beta') order by id; + +-- #3: T_json flattened + searchable (both values and both docs) +select id from j1 where match(doc) against('quantum') order by id; +select id from j1 where match(doc) against('food') order by id; + +-- #4: BOTH json columns searchable (a and b) +select id from j2 where match(a,b) against('hello') order by id; +select id from j2 where match(a,b) against('bar') order by id; +select id from j2 where match(a,b) against('dolor') order by id; + +-- #6: REBUILD over an emptied table must not serve the stale (deleted) base +create table reb (id bigint primary key, body text); +create fulltext2 index ftidx on reb(body); +insert into reb values (1,'stale zebra'),(2,'stale zebra'); +select sleep(45); +-- both rows searchable before the rebuild +select count(*) from reb where match(body) against('zebra'); +-- empty the table, then REBUILD (rebuilds the base from the now-empty source) +delete from reb; +alter table reb alter reindex ftidx fulltext2; +-- the stale base is cleared => zero matches (before the fix this returned 2) +select count(*) from reb where match(body) against('zebra'); + +drop database ft2_bugfix; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.result new file mode 100644 index 0000000000000..5ce67b63c9b5d --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.result @@ -0,0 +1,34 @@ +set experimental_fulltext2_index = 1; +create stage ft2stage URL='file://$resources/fulltext/'; +create table dl (id bigint primary key, fpath datalink, FULLTEXT2 ftidx(fpath)); +insert into dl values (0, 'stage://ft2stage/mo.pdf'), (1, 'stage://ft2stage/chinese.pdf'); +select sleep(30); +➤ sleep(30)[-6,8,0] 𝄀 +0 +select id from dl where match(fpath) against('matrixone'); +➤ id[-5,64,0] 𝄀 +0 +select id from dl where match(fpath) against('慢慢地' in natural language mode); +➤ id[-5,64,0] 𝄀 +1 +select id from dl where match(fpath) against('ft2stage'); +➤ id[-5,64,0] +select id from dl where match(fpath) against('mo'); +➤ id[-5,64,0] +create table dl2 (id bigint primary key, fpath datalink, FULLTEXT2 ftidx(fpath)); +insert into dl2 values (0, 'stage://ft2stage/mo.pdf'); +select sleep(30); +➤ sleep(30)[-6,8,0] 𝄀 +0 +update dl2 set fpath='stage://ft2stage/file-sample_100kB.docx' where id=0; +select sleep(30); +➤ sleep(30)[-6,8,0] 𝄀 +0 +select id from dl2 where match(fpath) against('Nulla facilisi' in natural language mode); +➤ id[-5,64,0] 𝄀 +0 +select id from dl2 where match(fpath) against('matrixone'); +➤ id[-5,64,0] +drop table dl; +drop table dl2; +drop stage ft2stage; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.sql new file mode 100644 index 0000000000000..ea828e858a314 --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.sql @@ -0,0 +1,45 @@ +-- fulltext2 DATALINK content indexing on the async CDC path. A DATALINK column must be +-- indexed by its resolved file CONTENT (PDF/DOCX text extracted via load_text/GetPlainText), +-- not by the URL string — the SAME as the synchronous build. fulltext2 is AlwaysAsync, so an +-- inline index builds an empty tag=0 base at CREATE and the datalink rows below flow through +-- ISCP CDC; the fulltext2 CDC writer resolves each datalink via SELECT load_text(...) run by +-- the internal SQL executor (which has a full proc: FileService + catalog for stage://). +-- sleep() waits for CDC to settle (the per-CN search cache is not cross-invalidated, so a +-- too-early MATCH can pin a stale snapshot), then MATCH against the file CONTENT must find +-- them. Regression guard for the CDC-vs-build datalink parity fix. +set experimental_fulltext2_index = 1; + +create stage ft2stage URL='file://$resources/fulltext/'; + +-- INSERT parity: an inline FULLTEXT2 index (empty base) + datalink rows via CDC. Default +-- (word) parser, mirroring the classic fulltext datalink test: mo.pdf contains 'matrixone', +-- chinese.pdf contains '慢慢地'. +create table dl (id bigint primary key, fpath datalink, FULLTEXT2 ftidx(fpath)); +insert into dl values (0, 'stage://ft2stage/mo.pdf'), (1, 'stage://ft2stage/chinese.pdf'); +select sleep(30); + +-- CDC must have indexed the resolved CONTENT, not the URL string. +select id from dl where match(fpath) against('matrixone'); +select id from dl where match(fpath) against('慢慢地' in natural language mode); +-- If CDC had indexed the URL string instead, these URL tokens would match — they must NOT. +select id from dl where match(fpath) against('ft2stage'); +select id from dl where match(fpath) against('mo'); + +-- UPDATE parity: a SEPARATE table so its first MATCH runs only AFTER the final mutation +-- has settled — fulltext2's per-CN search cache is not cross-invalidated by the CDC +-- consumer, so a pre-update MATCH could pin a stale snapshot on multi-CN (see +-- fulltext2_async). Updating the datalink must replace the old content terms with the +-- new file's terms. +create table dl2 (id bigint primary key, fpath datalink, FULLTEXT2 ftidx(fpath)); +insert into dl2 values (0, 'stage://ft2stage/mo.pdf'); +select sleep(30); +update dl2 set fpath='stage://ft2stage/file-sample_100kB.docx' where id=0; +select sleep(30); + +-- id 0's content is now the docx ('Nulla facilisi'); the old mo.pdf 'matrixone' is gone. +select id from dl2 where match(fpath) against('Nulla facilisi' in natural language mode); +select id from dl2 where match(fpath) against('matrixone'); + +drop table dl; +drop table dl2; +drop stage ft2stage; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.result new file mode 100644 index 0000000000000..4a8156fe3dc67 --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.result @@ -0,0 +1,21 @@ +drop account if exists ft2tenant; +create account ft2tenant admin_name 'admin' identified by 'test123'; +set experimental_fulltext2_index = 1; +create database ft2db; +use ft2db; +create stage ft2stage URL='file://$resources/fulltext/'; +create table dl (id bigint primary key, fpath datalink, FULLTEXT2 ftidx(fpath)); +insert into dl values (0, 'stage://ft2stage/mo.pdf'), (1, 'stage://ft2stage/chinese.pdf'); +select sleep(30); +➤ sleep(30)[-6,8,0] 𝄀 +0 +select id from dl where match(fpath) against('matrixone'); +➤ id[-5,64,0] 𝄀 +0 +select id from dl where match(fpath) against('慢慢地' in natural language mode); +➤ id[-5,64,0] 𝄀 +1 +drop table dl; +drop stage ft2stage; +drop database ft2db; +drop account ft2tenant; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.sql new file mode 100644 index 0000000000000..996fa5015e36e --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.sql @@ -0,0 +1,33 @@ +-- fulltext2 DATALINK CDC resolution under a NON-SYSTEM tenant. A datalink stage:// is a +-- per-account catalog object (mo_stages is tenant-scoped). ISCP forces the CDC consumer +-- ctx tenant to System_Account, so the resolver must run under the SOURCE tenant that owns +-- the table (r.GetAccountID()) — not the ctx tenant — else the tenant's stage is looked up +-- in the system tenant's mo_stages and resolution fails (empty MATCH) or hits an unrelated +-- same-named system stage. This runs the whole datalink flow inside a fresh tenant so a +-- regression to GetAccountId(ctx) is caught: the MATCH below would go empty. +drop account if exists ft2tenant; +create account ft2tenant admin_name 'admin' identified by 'test123'; + +-- @session:id=2&user=ft2tenant:admin&password=test123 +set experimental_fulltext2_index = 1; +create database ft2db; +use ft2db; + +-- stage + fulltext2 datalink index owned by THIS tenant. +create stage ft2stage URL='file://$resources/fulltext/'; +create table dl (id bigint primary key, fpath datalink, FULLTEXT2 ftidx(fpath)); +insert into dl values (0, 'stage://ft2stage/mo.pdf'), (1, 'stage://ft2stage/chinese.pdf'); +select sleep(30); + +-- CDC must have resolved the datalink CONTENT under this tenant's mo_stages: 'matrixone' +-- (mo.pdf), '慢慢地' (chinese.pdf). If the resolver ran under System_Account, the tenant's +-- ft2stage would not be found and these would be empty. +select id from dl where match(fpath) against('matrixone'); +select id from dl where match(fpath) against('慢慢地' in natural language mode); + +drop table dl; +drop stage ft2stage; +drop database ft2db; +-- @session + +drop account ft2tenant; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.result new file mode 100644 index 0000000000000..3910926b90d77 --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.result @@ -0,0 +1,72 @@ +set experimental_fulltext2_index = 1; +create table src_ins (id bigint primary key, body varchar(200), status varchar(20), prio int, +FULLTEXT2 ftidx (body) INCLUDE (status, prio)); +insert into src_ins values +(1, 'quick brown fox', 'active', 10), +(2, 'lazy fox sleeps', 'archived', 20), +(3, 'red fox runs', 'active', 30), +(4, 'quiet sleepy cat', 'active', 40); +create table src_upd (id bigint primary key, body varchar(200), status varchar(20), prio int, +FULLTEXT2 ftidx (body) INCLUDE (status, prio)); +insert into src_upd values +(10, 'silver fox hunts', 'active', 100), +(11, 'golden fox naps', 'active', 200), +(12, 'bronze fox digs', 'archived', 300); +select sleep(30); +➤ sleep(30)[-6,8,0] 𝄀 +0 +select id, status, prio from src_ins where match(body) against('fox') order by id; +➤ id[-5,64,0] ¦ status[12,20,0] ¦ prio[4,32,0] 𝄀 +1 ¦ active ¦ 10 𝄀 +2 ¦ archived ¦ 20 𝄀 +3 ¦ active ¦ 30 +select id from src_ins where match(body) against('fox') and status = 'active' order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +3 +select id, prio from src_ins where match(body) against('fox') and prio > 15 order by id; +➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 +2 ¦ 20 𝄀 +3 ¦ 30 +update src_upd set status = 'archived', prio = 999 where id = 10; +delete from src_upd where id = 11; +select sleep(30); +➤ sleep(30)[-6,8,0] 𝄀 +0 +select id, status, prio from src_upd where match(body) against('fox') order by id; +➤ id[-5,64,0] ¦ status[12,20,0] ¦ prio[4,32,0] 𝄀 +10 ¦ archived ¦ 999 𝄀 +12 ¦ archived ¦ 300 +select id from src_upd where match(body) against('fox') and status = 'active' order by id; +➤ id[-5,64,0] +select id from src_upd where match(body) against('fox') and status = 'archived' order by id; +➤ id[-5,64,0] 𝄀 +10 𝄀 +12 +select id, prio from src_upd where match(body) against('fox') and prio > 500 order by id; +➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 +10 ¦ 999 +create table src_phantom (id bigint primary key, body varchar(200), status varchar(20), prio int, +FULLTEXT2 ftidx (body) INCLUDE (status, prio)); +insert into src_phantom values +(20, 'silver fox alpha', 'active', 500), +(21, 'golden fox beta', 'active', 600); +begin; +update src_phantom set body = 'silver fox gamma', status = 'updated', prio = 999 where id = 20; +delete from src_phantom where id = 20; +commit; +select sleep(30); +➤ sleep(30)[-6,8,0] 𝄀 +0 +explain select id, status, prio from src_phantom where match(body) against('fox'); +➤ TP QUERY PLAN[12,0,0] 𝄀 +Project 𝄀 + -> Sort 𝄀 + Sort Key: mo_fulltext_alias_0.__mo_ft_score DESC 𝄀 + -> Table Function on fulltext2_search +select id, status, prio from src_phantom where match(body) against('fox') order by match(body) against('fox') desc, id; +➤ id[-5,64,0] ¦ status[12,20,0] ¦ prio[4,32,0] 𝄀 +21 ¦ active ¦ 600 +drop table src_ins; +drop table src_upd; +drop table src_phantom; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.sql new file mode 100644 index 0000000000000..b1d314eac78fe --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.sql @@ -0,0 +1,74 @@ +-- fulltext2 INCLUDE columns over CDC (always-async) — the actual include values +-- (status, prio) must travel through the ISCP CDC tail on INSERT/UPDATE/DELETE, so +-- the in-index prefilter + covering projection reflect the mutations. Modeled on +-- fulltext2_async: inline FULLTEXT2 is AlwaysAsync, sleep() waits for CDC to settle. +-- Cache-safe (per-CN index cache is not cross-invalidated): each table is searched +-- only AFTER its final mutation has settled, so its cache loads fresh. +set experimental_fulltext2_index = 1; + +-- src_ins: insert-only — verifies INSERT carries include values through CDC. +create table src_ins (id bigint primary key, body varchar(200), status varchar(20), prio int, + FULLTEXT2 ftidx (body) INCLUDE (status, prio)); +insert into src_ins values +(1, 'quick brown fox', 'active', 10), +(2, 'lazy fox sleeps', 'archived', 20), +(3, 'red fox runs', 'active', 30), +(4, 'quiet sleepy cat', 'active', 40); + +-- src_upd: mutated below (UPDATE include value + DELETE) before its first search. +create table src_upd (id bigint primary key, body varchar(200), status varchar(20), prio int, + FULLTEXT2 ftidx (body) INCLUDE (status, prio)); +insert into src_upd values +(10, 'silver fox hunts', 'active', 100), +(11, 'golden fox naps', 'active', 200), +(12, 'bronze fox digs', 'archived', 300); + +-- wait for the initial CDC sync +select sleep(30); + +-- src_ins is never re-mutated: safe to search now (first search loads fresh cache). +-- covering projection carries include values; cat(4) has no 'fox'. +select id, status, prio from src_ins where match(body) against('fox') order by id; +-- in-index prefilter on the CDC-carried include value. +select id from src_ins where match(body) against('fox') and status = 'active' order by id; +select id, prio from src_ins where match(body) against('fox') and prio > 15 order by id; + +-- mutate src_upd: UPDATE an include value (LWW) + DELETE, BEFORE its first search. +update src_upd set status = 'archived', prio = 999 where id = 10; +delete from src_upd where id = 11; +select sleep(30); + +-- src_upd's FIRST search (fresh cache post-mutation): id10 include values updated +-- (active/100 -> archived/999), id11 gone, id12 unchanged. +select id, status, prio from src_upd where match(body) against('fox') order by id; +-- prefilter reflects the NEW include values: no 'active' left (10 updated, 11 deleted). +select id from src_upd where match(body) against('fox') and status = 'active' order by id; +select id from src_upd where match(body) against('fox') and status = 'archived' order by id; +-- id10's updated prio wins. +select id, prio from src_upd where match(body) against('fox') and prio > 500 order by id; + +-- src_phantom: the covered-path CDC last-writer-wins regression. An UPSERT (UPDATE) followed +-- by a DELETE of the SAME pk in ONE transaction (so both mutations flow through CDC together) +-- must resolve to the DELETE. The classic 2-JOIN path masked a resurrected row via the source +-- join; the covered 0-JOIN path has no such join, so a phantom would surface a deleted row with +-- stale INCLUDE values. Ordered by score (never ORDER BY id) so the covered fast path is used. +create table src_phantom (id bigint primary key, body varchar(200), status varchar(20), prio int, + FULLTEXT2 ftidx (body) INCLUDE (status, prio)); +insert into src_phantom values +(20, 'silver fox alpha', 'active', 500), +(21, 'golden fox beta', 'active', 600); +-- upsert then delete the SAME pk in one txn: the exact upsert->delete phantom case. +begin; +update src_phantom set body = 'silver fox gamma', status = 'updated', prio = 999 where id = 20; +delete from src_phantom where id = 20; +commit; +select sleep(30); + +-- covered fast path (0-JOIN: Project -> Sort(score) -> Table Function, no base Table Scan/Join). +explain select id, status, prio from src_phantom where match(body) against('fox'); +-- pk 20 must be GONE (no phantom, no stale 'updated'/999); only pk 21 remains. +select id, status, prio from src_phantom where match(body) against('fox') order by match(body) against('fox') desc, id; + +drop table src_ins; +drop table src_upd; +drop table src_phantom; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.result new file mode 100644 index 0000000000000..1085cf3072f00 --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.result @@ -0,0 +1,55 @@ +set experimental_fulltext2_index = 1; +drop database if exists ft2_posfree_cdc; +create database ft2_posfree_cdc; +use ft2_posfree_cdc; +create table t (id bigint primary key, body text); +insert into t values (1,'我家有三个人'),(2,'三个人都住在我家'),(3,'我家的花园很漂亮'); +create fulltext2 index ft on t(body) with parser gojieba position_free = true; +insert into t values (4,'教室里有三个人'),(5,'昨天我家有三个人来吃饭'); +delete from t where id = 3; +select sleep(30); +➤ sleep(30)[-6,8,0] 𝄀 +0 +select id from t where match(body) against('我家' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +2 𝄀 +5 +select id from t where match(body) against('教室' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +4 +alter table t alter reindex ft fulltext2 merge; +select algo_params from mo_catalog.mo_indexes where name = 'ft' and algo_params like '%parser%' limit 1; +➤ algo_params[12,2048,0] 𝄀 +{"parser":"gojieba","position_free":"true","session_vars":{"cfg":{"lower_case_table_names":{"t":"I","v":1}}}} +select id from t where match(body) against('我家' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +2 𝄀 +5 +select id from t where match(body) against('我家' in boolean mode) order by id; +invalid input: fulltext2 index "ft" is POSITION_FREE (bag-of-words only): query it with MATCH(...) AGAINST(... IN BM25 MODE); it has no positions for natural-language / boolean phrase matching +insert into t values (6,'我家门口有三个人'); +update t set body = '教室里没有人' where id = 4; +select sleep(30); +➤ sleep(30)[-6,8,0] 𝄀 +0 +alter table t alter reindex ft fulltext2; +select algo_params from mo_catalog.mo_indexes where name = 'ft' and algo_params like '%parser%' limit 1; +➤ algo_params[12,2048,0] 𝄀 +{"parser":"gojieba","position_free":"true","session_vars":{"cfg":{"lower_case_table_names":{"t":"I","v":1}}}} +select id from t where match(body) against('我家' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +2 𝄀 +5 +select id from t where match(body) against('三个人' in bm25 mode) order by id; +➤ id[-5,64,0] 𝄀 +1 𝄀 +2 𝄀 +4 𝄀 +5 𝄀 +6 +select id from t where match(body) against('我家' in boolean mode) order by id; +invalid input: fulltext2 index "ft" is POSITION_FREE (bag-of-words only): query it with MATCH(...) AGAINST(... IN BM25 MODE); it has no positions for natural-language / boolean phrase matching +drop database ft2_posfree_cdc; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.sql new file mode 100644 index 0000000000000..c64d0a31f5cd5 --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.sql @@ -0,0 +1,42 @@ +-- fulltext2 POSITION_FREE through the CDC lifecycle: a position-free index (gojieba, +-- bag-of-words only) must STAY position-free across post-create INSERT/DELETE/UPDATE +-- (the tag=1 CdcTail is built position-free — slice 2b-tail), a REINDEX MERGE that folds +-- the tail into the tag=0 base (reconstructs from tf, no positions — slice 2b-compact), +-- and a REINDEX REBUILD from source. IN BM25 MODE reflects the mutations at each step; +-- BOOLEAN phrase stays rejected (no positions). sleep() waits for the always-async CDC. +set experimental_fulltext2_index = 1; +drop database if exists ft2_posfree_cdc; +create database ft2_posfree_cdc; +use ft2_posfree_cdc; + +create table t (id bigint primary key, body text); +insert into t values (1,'我家有三个人'),(2,'三个人都住在我家'),(3,'我家的花园很漂亮'); +create fulltext2 index ft on t(body) with parser gojieba position_free = true; + +-- post-create INSERT/DELETE flow through the CDC tail (built position-free) +insert into t values (4,'教室里有三个人'),(5,'昨天我家有三个人来吃饭'); +delete from t where id = 3; +select sleep(30); + +-- IN BM25 MODE reflects the mutations: 我家 now in docs 1,2,5 (doc 3 deleted); 教室 in doc 4 +select id from t where match(body) against('我家' in bm25 mode) order by id; +select id from t where match(body) against('教室' in bm25 mode) order by id; + +-- REINDEX MERGE: fold the tag=1 tail into the tag=0 base, staying position-free +alter table t alter reindex ft fulltext2 merge; +select algo_params from mo_catalog.mo_indexes where name = 'ft' and algo_params like '%parser%' limit 1; +select id from t where match(body) against('我家' in bm25 mode) order by id; +-- still position-free: BOOLEAN phrase mode is rejected +select id from t where match(body) against('我家' in boolean mode) order by id; + +-- more mutations, then REINDEX REBUILD (rebuild the base from source, stays position-free) +insert into t values (6,'我家门口有三个人'); +update t set body = '教室里没有人' where id = 4; +select sleep(30); +alter table t alter reindex ft fulltext2; +select algo_params from mo_catalog.mo_indexes where name = 'ft' and algo_params like '%parser%' limit 1; +select id from t where match(body) against('我家' in bm25 mode) order by id; +select id from t where match(body) against('三个人' in bm25 mode) order by id; +select id from t where match(body) against('我家' in boolean mode) order by id; + +drop database ft2_posfree_cdc; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.result new file mode 100644 index 0000000000000..c430fc40c72ad --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.result @@ -0,0 +1,54 @@ +drop database if exists ft2_restore; +create database ft2_restore; +use ft2_restore; +set experimental_fulltext2_index = 1; +create table src(id int primary key, body text, FULLTEXT2 ftidx(body)); +insert into src values +(1,'alpha keyword'),(2,'beta keyword'), +(3,'gamma topic'),(4,'delta topic'); +select sleep(30); +sleep(30) +0 +select id from src where match(body) against('alpha' in boolean mode) order by id; +id +1 +set experimental_fulltext2_index = 0; +create table dst clone src; +select count(*) from dst; +count(*) +4 +show create table dst; +Table Create Table +dst CREATE TABLE `dst` (\n `id` int NOT NULL,\n `body` text DEFAULT NULL,\n PRIMARY KEY (`id`),\n FULLTEXT2 `ftidx`(`body`) WITH PARSER ngram\n) +insert into dst values (5,'epsilon fresh'); +select sleep(45); +sleep(45) +0 +select id from dst where match(body) against('beta' in boolean mode) order by id; +id +2 +select id from dst where match(body) against('topic' in boolean mode) order by id; +id +3 +4 +select id from dst where match(body) against('epsilon' in boolean mode) order by id; +id +5 +select id from dst where match(body) against('alpha' in boolean mode) order by id; +id +1 +create snapshot ft2_sp for account; +drop table src; +restore database sys.ft2_restore {snapshot="ft2_sp"}; +select sleep(30); +sleep(30) +0 +select id from src where match(body) against('alpha' in boolean mode) order by id; +id +1 +select id from src where match(body) against('keyword' in boolean mode) order by id; +id +1 +2 +drop snapshot ft2_sp; +drop database ft2_restore; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.sql new file mode 100644 index 0000000000000..209272f7d8f98 --- /dev/null +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.sql @@ -0,0 +1,76 @@ +-- Execution-level proof for the clone/restore path of a fulltext2 index (ported +-- from pessimistic_transaction/fulltext fulltext_restore_clone). fulltext2 is +-- AlwaysAsync (CDC-backed, no ASYNC keyword). This drives the real surface: +-- (a) CREATE TABLE ... CLONE of a table carrying a fulltext2 index succeeds and +-- the CLONED index still answers MATCH ... AGAINST; +-- (b) the background CDC maintenance is RE-ARMED on the clone (RestoreInitSQL): +-- rows inserted AFTER the clone become matchable; +-- (c) CREATE SNAPSHOT + RESTORE DATABASE rebuilds the fulltext2 index from the +-- restored rows and it answers MATCH; +-- (d) the experimental_fulltext2_index gate is AUTO-SKIPPED during clone/restore +-- (an existing index is replayed even with the flag toggled off). +-- +-- MATCH ... AGAINST requires a fulltext index (no full-scan fallback), so every +-- match that returns a row is served by a working index. Boolean mode is used so +-- results don't depend on relevancy thresholds. + +drop database if exists ft2_restore; +create database ft2_restore; +use ft2_restore; + +-- CREATE FULLTEXT2 INDEX is gated behind experimental_fulltext2_index (default off). +set experimental_fulltext2_index = 1; + +-- Source: fulltext2 index over unique tokens (unambiguous matches). +create table src(id int primary key, body text, FULLTEXT2 ftidx(body)); +insert into src values + (1,'alpha keyword'),(2,'beta keyword'), + (3,'gamma topic'),(4,'delta topic'); + +-- let the async CDC build finish, then confirm the source index answers MATCH +select sleep(30); +select id from src where match(body) against('alpha' in boolean mode) order by id; + +-- ================= CLONE (gate auto-skipped: flag toggled OFF) ================= +set experimental_fulltext2_index = 0; + +-- (a) clone succeeds and copies the rows + fulltext2 index definition +create table dst clone src; +select count(*) from dst; +show create table dst; + +-- (c) maintenance re-armed: insert a row AFTER the clone. Done BEFORE any dst MATCH +-- so the clone's per-CN index cache is first loaded fresh (with the new row present). +-- fulltext2's index cache is not cross-CN invalidated by the CDC consumer, so a dst +-- MATCH issued before this insert settles would pin a pre-insert snapshot and the +-- 'epsilon' query would read it stale on multi-CN. +insert into dst values (5,'epsilon fresh'); + +-- let RestoreInitSQL's re-registered CDC + the post-clone insert settle on the clone +select sleep(45); + +-- (b) the cloned index answers MATCH on the copied rows ... +select id from dst where match(body) against('beta' in boolean mode) order by id; +select id from dst where match(body) against('topic' in boolean mode) order by id; +-- (c) ... and on the row inserted after the clone +select id from dst where match(body) against('epsilon' in boolean mode) order by id; +-- pre-existing cloned rows remain matchable too +select id from dst where match(body) against('alpha' in boolean mode) order by id; + +-- ================= SNAPSHOT / RESTORE (gate auto-skipped, flag still OFF) ======= +create snapshot ft2_sp for account; + +-- mutate after the snapshot: drop the indexed source table +drop table src; + +-- restore the database from the snapshot -> rebuilds the fulltext2 index from the +-- restored rows (flag is OFF, but restore replays the existing index) +restore database sys.ft2_restore {snapshot="ft2_sp"}; + +-- the restored index answers MATCH +select sleep(30); +select id from src where match(body) against('alpha' in boolean mode) order by id; +select id from src where match(body) against('keyword' in boolean mode) order by id; + +drop snapshot ft2_sp; +drop database ft2_restore; diff --git a/test/distributed/resources/fulltext2/wiki_en_1k.csv b/test/distributed/resources/fulltext2/wiki_en_1k.csv new file mode 100644 index 0000000000000..6e8ff6920580c --- /dev/null +++ b/test/distributed/resources/fulltext2/wiki_en_1k.csv @@ -0,0 +1,1000 @@ +1,"Anarchism is a political philosophy and movement that is against all forms of authority and seeks to abolish the institutions it claims maintain unnecessary coercion and hierarchy, typically including the state and capitalism. Anarchism advocates for the replacement of the state with stateless societies and voluntary free associations. A historically left-wing movement, anarchism is usually described as the libertarian wing of the socialist movement (libertarian socialism). Although traces of anarchist ideas are found all throughout history, modern anarchism emerged from the Enlightenment. During the latter half of the 19th and the first decades of the 20th century, the anarchist movement flourished in most parts of the world and had a significant role in workers' struggles for emancipation. Various anarchist schools of thought formed during this period. Anarchists have taken part in several revolutions, most notably in the Paris Commune, the Russian Civil War and the Spanish Civil War" +2,"a wave of then-unprecedented globalisation occurred from 1880 to 1914. This era of classical anarchism lasted until the end of the Spanish Civil War and is considered the golden age of anarchism. Drawing from mutualism, Mikhail Bakunin founded collectivist anarchism and entered the International Workingmen's Association, a class worker union later known as the First International that formed in 1864 to unite diverse revolutionary currents. The International became a significant political force, with Karl Marx being a leading figure and a member of its General Council. Bakunin's faction (the Jura Federation) and Proudhon's followers (the mutualists) opposed state socialism, advocating political abstentionism and small property holdings. After bitter disputes, the Bakuninists were expelled from the International by the Marxists at the 1872 Hague Congress. Anarchists were treated similarly in the Second International, being ultimately expelled in 1896. Bakunin predicted that if revolution" +3,"some features of how contemporary anarchist groups work: direct action, working together and in solidarity with those left behind. While doing so, Food Not Bombs provides consciousness raising about the rising rates of world hunger and suggest policies to tackle hunger, ranging from de-funding the arms industry to addressing Monsanto seed-saving policies and patents, helping farmers, and resisting the commodification of food and housing. Honeywell also emphasizes that contemporary anarchists are interested in the flourishing not only of humans, but non-humans and the environment as well. Honeywell argues that their analysis of capitalism and governments results in anarchists rejecting representative democracy and the state as a whole. == Schools of thought == Anarchist schools of thought have been generally grouped into two main historical traditions, social anarchism and individualist anarchism, owing to their different origins, values and evolution. The individualist current emphasis" +4,"anthropology and history to engage with the anarchist movement, although contemporary anarchism favours actions over academic theory. Various anarchist groups, tendencies, and schools of thought exist today, making it difficult to describe the contemporary anarchist movement. While theorists and activists have established relatively stable constellations of anarchist principles , there is no consensus on which principles are core and commentators describe multiple anarchisms, rather than a singular anarchism, in which common principles are shared between schools of anarchism while each group prioritizes those principles differently. Gender equality can be a common principle, although it ranks as a higher priority to anarcha-feminists than anarcho-communists. Anarchists are generally committed against coercive authority in all forms, namely all centralized and hierarchical forms of government (e.g., monarchy, representative democracy, state socialism, etc.), economic class systems (e.g." +5,"leading group. One way is for an individual from the group to play the role of facilitator to help achieve a consensus without taking part in the discussion themselves or promoting a specific point. Minorities usually accept rough consensus, except when they feel the proposal contradicts anarchist ethics, goals and values. Anarchists usually form small groups (5–20 individuals) to enhance autonomy and friendships among their members. These kinds of groups more often than not interconnect with each other, forming larger networks. Anarchists still support and participate in strikes, especially wildcat strikes as these are leaderless strikes not organised centrally by a syndicate. As in the past, newspapers and journals are used, and anarchists have gone online to spread their message. Anarchists have found it easier to create websites because of distributional and other difficulties, hosting electronic libraries and other portals. Anarchists were also involved in developing various softw" +6,"education Concept Education as self-mastery Education as service Management Community based State run Methods Practice-based learning Vocational training Aims Being a critical member of society Being a productive member of society The interest of anarchists in education stretches back to the first emergence of classical anarchism. Anarchists consider proper education, one which sets the foundations of the future autonomy of the individual and the society, to be an act of mutual aid. Anarchist writers such as William Godwin (Political Justice) and Max Stirner ( The False Principle of Our Education ) attacked both state education and private education as another means by which the ruling class replicate their privileges. In 1901, Catalan anarchist and free thinker Francisco Ferrer established the Escuela Moderna in Barcelona as an opposition to the established education system which was dictated largely by the Catholic Church. Ferrer's approach was secular, rejecting both state and churc" +7,"organization, such as hierarchies, monopolies and inequality, outweigh the benefits. Philosophy lecturer Andrew G. Fiala composed a list of common arguments against anarchism which includes critiques such as that anarchism is innately related to violence and destruction, not only in the pragmatic world, such as at protests, but in the world of ethics as well. Secondly, anarchism is evaluated as unfeasible or utopian since the state cannot be defeated practically. This line of arguments most often calls for political action within the system to reform it. The third argument is that anarchism is self-contradictory as a ruling theory that has no ruling theory. Anarchism also calls for collective action while endorsing the autonomy of the individual, hence no collective action can be taken. Lastly, Fiala mentions a critique towards philosophical anarchism of being ineffective (all talk and thoughts) and in the meantime capitalism and bourgeois class remains strong. Philosophical anarchism" +8,"Albedo ( ; ) is the fraction of sunlight that is diffusely reflected by a body. It is measured on a scale from 0 (corresponding to a black body that absorbs all incident radiation) to 1 (corresponding to a body that reflects all incident radiation). Surface albedo is defined as the ratio of radiosity Je to the irradiance Ee (flux per unit area) received by a surface. The proportion reflected is not only determined by properties of the surface itself, but also by the spectral and angular distribution of solar radiation reaching the Earth's surface. These factors vary with atmospheric composition, geographic location, and time (see position of the Sun). While directional-hemispherical reflectance factor is calculated for a single angle of incidence (i.e., for a given position of the Sun), albedo is the directional integration of reflectance over all solar angles in a given period. The temporal resolution may range from seconds (as obtained from flux measurements) to daily, monthly, or an" +9,"the albedo of surfaces from very low to high values, so long as a thermal emittance of at least 90% can be achieved. The tens of thousands of hectares of greenhouses in Almería, Spain form a large expanse of whitened plastic roofs. A 2008 study found that this anthropogenic change lowered the local surface area temperature of the high-albedo area, although changes were localized. A follow-up study found that CO2-eq. emissions associated to changes in surface albedo are a consequence of land transformation and can reduce surface temperature increases associated with climate change. ==Examples of terrestrial albedo effects== === Illumination === Albedo is not directly dependent on the illumination because changing the amount of incoming light proportionally changes the amount of reflected light, except in circumstances where a change in illumination induces a change in the Earth's surface at that location (e.g. through melting of reflective ice). However, albedo and illumination both var" +10,"reduces albedo on glaciers and ice sheets. Changing Greenland – Melt Zone page 3, of 4, article by Mark Jenkins in National Geographic June 2010, accessed 8 July 2010 The dynamical nature of albedo in response to positive feedback, together with the effects of small errors in the measurement of albedo, can lead to large errors in energy estimates. Because of this, in order to reduce the error of energy estimates, it is important to measure the albedo of snow-covered areas through remote sensing techniques rather than applying a single value for albedo over broad regions. ===Small-scale effects=== Albedo works on a smaller scale, too. In sunlight, dark clothes absorb more heat and light-coloured clothes reflect it better, thus allowing some control over body temperature by exploiting the albedo effect of the colour of external clothing. === Solar photovoltaic effects === Albedo can affect the electrical energy output of solar photovoltaic devices. For example, the effects of a spectrall" +11,"of heavy commercial airliner traffic. A study following the burning of the Kuwaiti oil fields during Iraqi occupation showed that temperatures under the burning oil fires were as much as colder than temperatures several miles away under clear skies. ===Aerosol effects=== Aerosols (very fine particles/droplets in the atmosphere) have both direct and indirect effects on Earth's radiative balance. The direct (albedo) effect is generally to cool the planet; the indirect effect (the particles act as cloud condensation nuclei and thereby change cloud properties) is less certain. ===Black carbon=== Another albedo-related effect on the climate is from black carbon particles. The size of this effect is difficult to quantify: the Intergovernmental Panel on Climate Change estimates that the global mean radiative forcing for black carbon aerosols from fossil fuels is +0.2 W m−2, with a range +0.1 to +0.4 W m−2. Black carbon is a bigger cause of the melting of the polar ice cap in the Arctic than c" +12,"denominator is the effective cross-sectional area of the target object with mean radius, r. A smooth metallic sphere would have \hat{\sigma}_\text{OC} = 1. ====Radar albedos of Solar System objects==== Object \hat{\sigma}_\text{OC} Moon 0.06 Mercury 0.05 Venus 0.10 Mars 0.06 Avg. S-type asteroid 0.14 Avg. C-type asteroid 0.13 Avg. M-type asteroid 0.26 Comet P/2005 JQ5 0.02 The values reported for the Moon, Mercury, Mars, Venus, and Comet P/2005 JQ5 are derived from the total (OC+SC) radar albedo reported in those references. ====Relationship to surface bulk density==== In the event that most of the echo is from first surface reflections (\hat{\sigma}_\text{OC} < 0.1 or so), the OC radar albedo is a first-order approximation of the Fresnel reflection coefficient (aka reflectivity) and can be used to estimate the bulk density of a planetary surface to a depth of a meter or so (a few wavelengths of the radar wavelength which is typically at the decimeter scale) using the following empiric" +13,"A, or a, is the first letter and the first vowel letter of the Latin alphabet, used in the modern English alphabet, and others worldwide. Its name in English is a (pronounced ), plural aes. It is similar in shape to the Ancient Greek letter alpha, from which it derives. The uppercase version consists of the two slanting sides of a triangle, crossed in the middle by a horizontal bar. The lowercase version is often written in one of two forms: the double-storey and single-storey . The latter is commonly used in handwriting and fonts based on it, especially fonts intended to be read by children, and is also found in italic type. In English, a is the indefinite article, with the alternative form an. == Name == In English, the name of the letter is the long A sound, pronounced . Its name in most other languages matches the letter's pronunciation in open syllables. == History == The earliest known ancestor of A is aleph—the first letter of the Phoenician alphabet—where it represented a glott" +14,"American—which is closer to its original Latin and Greek sound *the open back rounded vowel (merged with as in General American) in was and what *the open-mid back rounded vowel in water *the diphthong as in ace and major, usually when is followed by one, or occasionally two, consonants and then another vowel letter—this results from Middle English lengthening followed by the Great Vowel Shift *a schwa in many unstressed syllables, as in about, comma, solar The double sequence does not occur in native English words, but is found in some words derived from foreign languages such as Aaron and aardvark. However, occurs in many common digraphs, all with their own sound or sounds, particularly , , , , and . is the third-most-commonly used letter in English after and , as well as in French; it is the second most common in Spanish, and the most common in Portuguese. represents approximately 8.2% of letters as used in English texts; the figure is around 7.6% in French 11.5% in Spanish, and 14." +15,"Alabama ( ) is a state in the Southeastern region of the United States. It borders Tennessee to the north, Georgia to the east, Florida and the Gulf of Mexico to the south, and Mississippi to the west. Alabama is the 30th largest by area and the 24th-most populous of the 50 U.S. states. Alabama is nicknamed the Yellowhammer State, after the state bird. Alabama is also known as the Heart of Dixie and the Cotton State . The state has diverse geography, with the north dominated by the mountainous Tennessee Valley and the south by Mobile Bay, a historically significant port. Alabama's capital is Montgomery, and its largest city by population and area is Huntsville. Its oldest city is Mobile, founded by French colonists (Alabama Creoles) in 1702 as the capital of French Louisiana. Greater Birmingham is Alabama's largest metropolitan area and its economic center. Politically, as part of the Deep South, Alabama is predominantly a conservative state, and is known for its Southern culture. With" +16,"part of British West Florida from 1763 to 1783. After the U.S. victory in the American Revolutionary War, the territory was divided between the United States and Spain. The latter retained control of this western territory from 1783 until the surrender of the Spanish garrison at Mobile to U.S. forces on April 13, 1813. Thomas Bassett, a loyalist to the British monarchy during the Revolutionary era, was one of the earliest white settlers in the state outside Mobile. He settled in the Tombigbee District during the early 1770s. The district's boundaries were roughly limited to the area within a few miles of the Tombigbee River and included portions of what is today southern Clarke County, northernmost Mobile County, and most of Washington County. What are now Baldwin and Mobile counties became part of Spanish West Florida in 1783, part of the independent Republic of West Florida in 1810, and finally part of the Mississippi Territory in 1812. Most of what is now the northern two-thirds of" +17,"being used to finance religious-affiliated schools. The same year, legislation was approved that called for racially segregated schools. Railroad passenger cars were segregated in 1891. === 20th century === The new 1901 Constitution of Alabama included provisions for voter registration that effectively disenfranchised large portions of the population, including nearly all African Americans and Native Americans, and tens of thousands of poor European Americans, through making voter registration difficult, requiring a poll tax and literacy test.Morgan Kousser. The Shaping of Southern Politics: Suffrage Restriction and the Establishment of the One-Party South, New Haven: Yale University Press, 1974 The 1901 constitution required racial segregation of public schools. By 1903 only 2,980 African Americans were registered in Alabama, although at least 74,000 were literate. This compared to more than 181,000 African Americans eligible to vote in 1900. The numbers dropped even more in later dec" +18,"Civil Rights Act of 1964 and Voting Rights Act of 1965 by the U.S. Congress. Legal segregation ended in the states in 1964, but Jim Crow customs often continued until specifically challenged in court. According to The New York Times, by 2017, many of Alabama's African Americans were living in Alabama's cities such as Birmingham and Montgomery. Also, the Black Belt region across central Alabama is home to largely poor counties that are predominantly African-American. These counties include Dallas, Lowndes, Marengo and Perry. In 1972, for the first time since 1901, the legislature completed the congressional redistricting based on the decennial census. This benefited the urban areas that had developed, as well as all in the population who had been underrepresented for more than sixty years. Other changes were made to implement representative state house and senate districts. Alabama has made some changes since the late 20th century and has used new types of voting to increase representat" +19,"Montgomery. This is the Wetumpka crater, the site of Alabama's greatest natural disaster . A -wide meteorite hit the area about 80 million years ago. The hills just east of downtown Wetumpka showcase the eroded remains of the impact crater that was blasted into the bedrock, with the area labeled the Wetumpka crater or astrobleme ( star-wound ) because of the concentric rings of fractures and zones of shattered rock that can be found beneath the surface. The Wetumpka Astrobleme by John C. Hall, Alabama Heritage, Fall 1996, Number 42. In 2002, Christian Koeberl with the Institute of Geochemistry University of Vienna published evidence and established the site as the 157th recognized impact crater on Earth. === Climate === The state is classified as humid subtropical (Cfa) under the Koppen Climate Classification. The average annual temperature is 64°F (18°C). Temperatures tend to be warmer in the southern part of the state with its proximity to the Gulf of Mexico, while the northern parts" +20,"Population(2020 census) County(ies) 1 Huntsville Madison, Limestone, Morgan 2 Montgomery Montgomery 3 Birmingham Jefferson, Shelby 4 Mobile Mobile 5 Tuscaloosa Tuscaloosa 6 Hoover Jefferson, Shelby 7 Auburn Lee 8 Dothan Houston, Dale, Henry 9 Madison Madison, Limestone 10 Decatur Morgan, Limestone 11 Florence Lauderdale 12 Prattville Autauga, Elmore 13 Vestavia Hills Jefferson, Shelby 14 Phenix City Russell 15 Alabaster Shelby == Demographics == According to the 2020 United States census the population of Alabama was 5,024,279 on April 1, 2020, which represents an increase of 244,543 or 5.12%, since the 2010 census. This includes a natural increase since the last census of 121,054 (502,457 births minus 381,403 deaths) and an increase due to net migration of 104,991 into the state. Immigration from outside the U.S. resulted in a net increase of 31,180 people, and migration within the country produced a net gain of 73,811 people. The state had 108,000 foreign-born (2.4% of the state popu" +21,"established in the Great Awakening of the early 19th century, when preachers proselytized across the South. The Assemblies of God had almost 60,000 members, the Churches of Christ had nearly 120,000 members. The Presbyterian churches, strongly associated with Scots-Irish immigrants of the 18th century and their descendants, had a combined membership around 75,000 (PCA—28,009 members in 108 congregations, PC(USA)—26,247 members in 147 congregations, the Cumberland Presbyterian Church—6,000 members in 59 congregations, the Cumberland Presbyterian Church in America—5,000 members and fifty congregations plus the EPC and Associate Reformed Presbyterians with 230 members and nine congregations). In a 2007 survey, nearly 70% of respondents could name all four of the Christian Gospels. Of those who indicated a religious preference, 59% said they possessed a full understanding of their faith and needed no further learning. In a 2007 poll, 92% of Alabamians reported having at least some confiden" +22,"Tuscaloosa Huntsville City Schools Huntsville Huntsville Hospital System Huntsville Hyundai Motor Manufacturing Alabama Montgomery Infirmary Health System Mobile Jefferson County Board of Education Birmingham Marshall Space Flight Center Huntsville Mercedes-Benz U.S. International Vance Montgomery Public Schools Montgomery Regions Financial Corporation Multiple Boeing Multiple University of Alabama Tuscaloosa University of South Alabama Mobile Walmart Multiple === Agriculture === Alabama's agricultural outputs include poultry and eggs, cattle, fish, plant nursery items, peanuts, cotton, grains such as corn and sorghum, vegetables, milk, soybeans, and peaches. Although known as The Cotton State , Alabama ranks between eighth and tenth in national cotton production, according to various reports, with Texas, Georgia and Mississippi comprising the top three. === Aquaculture === Aquaculture is a large part of the economy of Alabama.Hanson, TE. 2015. Economics of Aquaculture Production in Al" +23,"Rocket Center in Huntsville with an attendance of 849,981, followed by the Birmingham Zoo with 543,090. Of the parks and natural destinations, Alabama's Gulf Coast topped the list with 6,700,000 visitors. Alabama has historically been a popular region for film shoots due to its diverse landscapes and contrast of environments. Movies filmed in Alabama include Close Encounters of the Third Kind, Get Out, 42, Selma, Big Fish, The Final Destination, Due Date, and Need for Speed. === Healthcare === UAB Hospital, USA Health University Hospital, Huntsville Hospital, and Children's Hospital of Alabama are the only LevelI trauma centers in Alabama. UAB is the largest state government employer in Alabama, with a workforce of about 18,000. A 2017 study found that Alabama had the least competitive health insurance market in the country, with Blue Cross and Blue Shield of Alabama having a market share of 84% followed by UnitedHealth Group at 7%. === Banking === Regions Financial Corporation is the" +24,"penalty rate in the country. In some years, it imposes more death sentences than does Texas, a state which has a population five times larger. However, executions per capita are significantly higher in Texas. Some of its cases have been highly controversial; the U.S. Supreme Court has overturned 24 convictions in death penalty cases. It was the only state to allow judges to override jury decisions in whether or not to use a death sentence; in 10 cases judges overturned sentences of life imprisonment without parole that were voted unanimously by juries. This judicial authority was removed in April 2017. On May 14, 2019, Alabama passed the Human Life Protection Act, banning abortion at any stage of pregnancy unless there is a serious health risk , with no exceptions for rape and incest. The law subjects doctors who perform abortions with 10 to 99 years imprisonment. The law was originally supposed to take effect the following November, but on October 29, 2019, U.S. District Judge Myron T" +25,"through violence, fraud, and intimidation. After 1890, a coalition of White Democratic politicians passed laws to segregate and disenfranchise African American residents, a process completed in provisions of the 1901 constitution. Provisions which disenfranchised blacks resulted in excluding many poor Whites. By 1941 more Whites than Blacks had been disenfranchised: 600,000 to 520,000. The total effects were greater on the black community, as almost all its citizens were disfranchised and relegated to separate and unequal treatment under the law. From 1901 through the 1960s, the state did not redraw election districts as population grew and shifted within the state during urbanization and industrialization of certain areas. As counties were the basis of election districts, the result was a rural minority that dominated state politics through nearly three-quarters of the century, until a series of federal court cases required redistricting in 1972 to meet equal representation. Alabama s" +26,"2007, more than 82 percent of schools made adequate yearly progress (AYP) toward student proficiency under the National No Child Left Behind law, using measures determined by the state of Alabama. While Alabama's public education system has improved in recent decades, it lags behind in achievement compared to other states. According to U.S. Census data (2000), Alabama's high school graduation rate (75%) is the fourth lowest in the U.S. (after Kentucky, Louisiana and Mississippi). The largest educational gains were among people with some college education but without degrees.Education Statistics . CensusScope.org According to National Assessment of Educational Progress (NEAP), Alabama ranks 39 in reading and 40 in math among fourth-grade students in the rankings from 2022. Generally prohibited in the West at large, school corporal punishment is not unusual in Alabama, with 27,260 public school students paddled at least one time, according to government data for the 2011–2012 school year" +27,"Tennis tournament held from 1973 to 1980. Alabama has hosted several professional golf tournaments, such as the 1984 and 1990 PGA Championship at Shoal Creek, the Barbasol Championship (PGA Tour), the Mobile LPGA Tournament of Champions, Airbus LPGA Classic, and Yokohama Tire LPGA Classic (LPGA Tour), and The Tradition (Champions Tour). ==== College sports ==== College football is extremely popular in Alabama, particularly the University of Alabama Crimson Tide and Auburn University Tigers, rivals in the Southeastern Conference. Alabama averages over 100,000 fans per game and Auburn averages over 80,000—both numbers among the top twenty in the nation. Bryant–Denny Stadium is the home of the Alabama football team, and has a seating capacity of 101,821, and is the fifth largest stadium in America. Jordan-Hare Stadium is the home field of the Auburn football team and seats up to 87,451. Protective Stadium is home of the UAB Blazers football program and the Birmingham Bowl. It seats 45,000" +28,"In Greek mythology, Achilles ( ) or Achilleus () was a hero of the Trojan War who was known as being the greatest of all the Greek warriors. The central character in Homer's Iliad, he was the son of the Nereid Thetis and Peleus, king of Phthia and famous Argonaut. Achilles was raised in Phthia along with his childhood companion Patroclus and received his education by the centaur Chiron. In the Iliad, he is presented as the commander of the mythical tribe of the Myrmidons. Achilles' most notable feat during the Trojan War was the slaying of the Trojan prince Hector outside the gates of Troy. Although the death of Achilles is not presented in the Iliad, other sources concur that he was killed near the end of the Trojan War by Paris, who shot him with an arrow. Later legends (beginning with Statius' unfinished epic Achilleid, written in the 1st century AD) state that Achilles was invulnerable in all of his body except for one heel. According to that myth, when his mother Thetis dipped him" +29,"to Thetis' chaste resistance to the advances of Zeus, pointing out that Thetis was so loyal to Hera's marriage bond that she coolly rejected the father of gods. Thetis, although a daughter of the sea-god Nereus, was also brought up by Hera, further explaining her resistance to the advances of Zeus. Zeus was furious and decreed that she would never marry an immortal.Pseudo-Apollodorus, Bibliotheca 3.13.5. According to the Achilleid, written by Statius in the 1st century AD, and to non-surviving previous sources, when Achilles was born Thetis tried to make him immortal by dipping him in the river Styx; however, he was left vulnerable at the part of the body by which she held him: his left heelStatius, Achilleid 1.269; Hyginus, Fabulae 107. . It is not clear if this version of events was known earlier. In another version of this story, Thetis anointed the boy in ambrosia and put him on top of a fire in order to burn away the mortal parts of his body. She was interrupted by Peleus and aban" +30,"in panic, Achilles prepared to defend the court, thus giving his identity away. == In the Trojan War == According to the Iliad, Achilles arrived at Troy with 50 ships, each carrying 50 Myrmidons. He appointed five leaders (each leader commanding 500 Myrmidons): Menesthius, Eudorus, Peisander, Phoenix and Alcimedon.Iliad 16.168–197. === Telephus === When the Greeks left for the Trojan War, they accidentally stopped in Mysia, ruled by King Telephus. In the resulting battle, Achilles gave Telephus a wound that would not heal; Telephus consulted an oracle, who stated that he that wounded shall heal . Guided by the oracle, he arrived at Argos, where Achilles healed him in order that he might become their guide for the voyage to Troy. According to other reports in Euripides' lost play about Telephus, he went to Aulis pretending to be a beggar and asked Achilles to heal his wound. Achilles refused, claiming to have no medical knowledge. Alternatively, Telephus held Orestes for ransom, the ran" +31,"Patroclus had been wearing, which was taken by Hector. The new armour includes the Shield of Achilles, described in great detail in the poem. Enraged over the death of Patroclus, Achilles ends his refusal to fight and takes the field, killing many men in his rage but always seeking out Hector. Achilles even engages in battle with the river god Scamander, who has become angry that Achilles is choking his waters with all the men he has killed. The god tries to drown Achilles but is stopped by Hera and Hephaestus. Zeus himself takes note of Achilles' rage and sends the gods to restrain him so that he will not go on to sack Troy itself before the time allotted for its destruction, seeming to show that the unhindered rage of Achilles can defy fate itself. Finally, Achilles finds his prey. Achilles chases Hector around the wall of Troy three times before Athena, in the form of Hector's favorite and dearest brother, Deiphobus, persuades Hector to stop running and fight Achilles face to face." +32,"to Troy (with an arrow to the heel according to Statius). In book 23, the sad spirit of dead Patroclus visits Achilles just as he drifts off into slumber, requesting that his bones be placed with those of Achilles in his golden vase, a gift of his mother. In book 11 of the Odyssey, Odysseus sails to the underworld and converses with the shades. One of these is Achilles, who when greeted as blessed in life, blessed in death , responds that he would rather be a slave to the worst of masters than be king of all the dead. But Achilles then asks Odysseus of his son's exploits in the Trojan war, and Odysseus tells him of Neoptolemus' actions.Odyssey 11.467–564. Book 24 of Odyssey gives dead King Agamemnon's ghostly account of Achilles' death: the bleached bones from Achilles' funeral pyre had been mixed with those of Patroclus and put into his mother's golden vase. Also, the bones of Antilochus, who had become closer to Achilles than any other following Patroclus' death, were separately encl" +33,"of the Black Sea, today identified with Snake Island (Ukrainian Зміїний, Zmiinyi, near Kiliya, Ukraine). Early dedicatory inscriptions from the Greek colonies on the Black Sea (graffiti and inscribed clay disks, these possibly being votive offerings, from Olbia, the area of Berezan Island and the Tauric Chersonese) attest the existence of a heroic cult of Achilles from the sixth century BC onwards. The cult was still thriving in the third century CE, when dedicatory stelae from Olbia refer to an Achilles Pontárchēs (Ποντάρχης, roughly lord of the sea , or lord of the Pontus Euxinus ), who was invoked as a protector of the city of Olbia, venerated on par with Olympian gods such as the local Apollo Prostates, Hermes Agoraeus, or Poseidon.Quintus Smyrnaeus, 3.770–779. Pliny the Elder (23–79 AD) in his Natural History mentions a port of the Achæi and an island of Achilles , famous for the tomb of that man (), situated somewhat nearby Olbia and the Dnieper-Bug Estuary; furthermore, at 125 R" +34,"and although it was treeless, was called Alsos (ἄλσος).Strabo, Geography, 7.3.19 Alsos in Greek means grove . A number of important commercial port cities of the Greek waters were dedicated to Achilles. Herodotus, Pliny the Elder and Strabo reported on the existence of a town Achílleion (Ἀχίλλειον), built by settlers from Mytilene in the sixth century BC, close to the hero's presumed burial mound in the Troad. Later attestations point to an Achílleion in Messenia (according to Stephanus Byzantinus) and an Achílleios (Ἀχίλλειος) in Laconia.Pausanias, Description of Greece 3.25.4. Nicolae Densuşianu recognized a connection to Achilles in the names of Aquileia and of the northern arm of the Danube delta, called Chilia (presumably from an older Achileii), though his conclusion, that Leuce had sovereign rights over the Black Sea, evokes modern rather than archaic sea-law.Nicolae Densuşianu: Dacia preistorică. Bucharest: Carol Göbl, 1913. The kings of Epirus claimed to be descended from Achi" +35,"Johann Wolfgang von Goethe. * In 1899, the Polish playwright, painter and poet Stanisław Wyspiański published a national drama, based on Polish history, named Achilles. * In 1921, Edward Shanks published The Island of Youth and Other Poems, concerned among others with Achilles. * The 1983 novel Kassandra by Christa Wolf also treats the death of Achilles. * H.D.'s 1961 long poem Helen in Egypt features Achilles prominently as a figure whose irrational hatred of Helen traumatizes her, the bulk of the poem's plot being about her recovery. * Akhilles is killed by a poisoned Kentaur arrow shot by Kassandra in Marion Zimmer Bradley's novel The Firebrand (1987). * Achilles is one of various 'narrators' in Colleen McCullough's novel The Song of Troy (1998). * The Death of Achilles (Смерть Ахиллеса, 1998) is an historical detective novel by Russian writer Boris Akunin that alludes to various figures and motifs from the Iliad. * The character Achilles in Ender's Shadow (1999), by Orson Scott Car" +36,"on Achilles. * The Wellington Monument is a statue representing Achilles erected in 1822 as a memorial to Arthur Wellesley, the first duke of Wellington, and his victories in the Peninsular War and the latter stages of the Napoleonic Wars. == Namesakes == * The name of Achilles has been used for at least nine Royal Navy warships since 1744 – both as and with the French spelling . A 60-gun ship of that name served at the Battle of Belleisle in 1761 while a 74-gun ship served at the Battle of Trafalgar. Other battle honours include Walcheren 1809. An armored cruiser of that name served in the Royal Navy during the First World War. * was a which served with the Royal New Zealand Navy in World War II. It became famous for its part in the Battle of the River Plate, alongside and . In addition to earning the battle honour 'River Plate', HMNZS Achilles also served at Guadalcanal 1942–1943 and Okinawa in 1945. After returning to the Royal Navy, the ship was sold to the Indian Navy in 1948, but" +37,"Abraham Lincoln ( ; February 12, 1809 – April 15, 1865) was the 16th president of the United States, serving from 1861 until his assassination in 1865. He led the United States through the American Civil War, defending the nation as a constitutional union, defeating the Confederacy, playing a major role in the abolition of slavery, expanding the power of the federal government, and modernizing the U.S. economy. Lincoln was born into poverty in a log cabin in Kentucky and was raised on the frontier, mainly in Indiana. He was self-educated and became a lawyer, Whig Party leader, Illinois state legislator, and U.S. representative from Illinois. In 1849, he returned to his successful law practice in Springfield, Illinois. In 1854, angered by the Kansas–Nebraska Act, which opened the territories to slavery, he re-entered politics. He soon became a leader of the new Republican Party. He reached a national audience in the 1858 Senate campaign debates against Stephen A. Douglas. Lincoln ran fo" +38,"Abraham became close to his stepmother and called her Mother . Dennis Hanks said he was lazy, for all his reading—scribbling—writing—ciphering—writing poetry .Blumenthal, Sidney, A Self-Made Man: The Political Life of Abraham Lincoln, 1809-1849. New York: Simon & Schuster, 2016, p. 29. His stepmother acknowledged he did not enjoy physical labor but loved to read. ===Education and move to Illinois=== Lincoln was largely self-educated. His formal schooling was from itinerant teachers. It included two short stints in Kentucky, where he learned to read, but probably not to write. In Indiana at age seven, due to farm chores, he attended school only sporadically, for a total of fewer than 12 months in aggregate by age 15. Nonetheless, he remained an avid reader and retained a lifelong interest in learning. Family, neighbors, and schoolmates recalled that his readings included the King James Bible, Aesop's Fables, John Bunyan's The Pilgrim's Progress, Daniel Defoe's Robinson Crusoe, and The A" +39,"licenses for $7 each for Lincoln and himself, and in 1833 the Lincoln-Berry General Store became a tavern as well. As licensed bartenders, Lincoln and Berry were able to sell spirits, including liquor, for 12 cents a pint. They offered a wide range of alcoholic beverages as well as food, including takeout dinners. But Berry became an alcoholic, was often too drunk to work, and Lincoln ended up running the store by himself. Although the economy was booming, the business struggled and went into debt, causing Lincoln to sell his share. In his first campaign speech after returning from his military service, Lincoln observed a supporter in the crowd under attack, grabbed the assailant by his neck and the seat of his trousers , and tossed him. In the campaign, Lincoln advocated for navigational improvements on the Sangamon River. He could draw crowds as a raconteur, but lacked the requisite formal education, powerful friends, and money, and lost the election. WP article Lincoln finished eigh" +40,"and resumed his law practice. ==Prairie lawyer== In his Springfield practice, Lincoln handled every kind of business that could come before a prairie lawyer . Twice a year he appeared for 10 consecutive weeks in county seats in the Midstate county courts; this continued for 16 years. Lincoln handled transportation cases in the midst of the nation's western expansion, particularly river barge conflicts under the many new railroad bridges. As a riverboat man, Lincoln initially favored those interests, but ultimately represented whoever hired him. He later represented a bridge company against a riverboat company in Hurd v. Rock Island Bridge Company, a landmark case involving a canal boat that sank after hitting a bridge. In 1849 he received a patent for a flotation device for the movement of boats in shallow water. The idea was never commercialized, but it made Lincoln the only president to hold a patent. Lincoln appeared before the Illinois Supreme Court in 175 cases; he was sole counse" +41,"James Buchanan and the Know-Nothings nominated former Whig President Millard Fillmore. Buchanan prevailed, while Republican William Henry Bissell won election as Governor of Illinois, and Lincoln became a leading Republican in Illinois. ==== Dred Scott v. Sandford ==== Dred Scott was a slave whose master took him from a slave state to a territory that was free as a result of the Missouri Compromise. After Scott was returned to the slave state, he petitioned a federal court for his freedom. His petition was denied in Dred Scott v. Sandford (1857). In his opinion, Supreme Court Chief Justice Roger B. Taney wrote that black people were not citizens and derived no rights from the Constitution, and that the Missouri Compromise was unconstitutional for infringing upon slave owners' property rights. While many Democrats hoped that Dred Scott would end the dispute over slavery in the territories, the decision sparked further outrage in the North. Lincoln denounced it as the product of a conspi" +42,"On May 9–10, 1860, the Illinois Republican State Convention was held in Decatur. Lincoln's followers organized a campaign team led by David Davis, Norman Judd, Leonard Swett, and Jesse DuBois, and Lincoln received his first endorsement. Exploiting his embellished frontier legend (clearing land and splitting fence rails), Lincoln's supporters adopted the label of The Rail Candidate . In 1860, Lincoln described himself: I am in height, six feet, four inches, nearly; lean in flesh, weighing, on an average, one hundred and eighty pounds; dark complexion, with coarse black hair, and gray eyes. Michael Martinez wrote about the effective imaging of Lincoln by his campaign. At times he was presented as the plain-talking Rail Splitter and at other times he was Honest Abe , unpolished but trustworthy. On May 18 at the Republican National Convention in Chicago, Lincoln won the nomination on the third ballot, beating candidates such as Seward and Chase. A former Democrat, Hannibal Hamlin of Maine," +43,"was given to him and his train. En route to his inauguration, Lincoln addressed crowds and legislatures across the North. The president-elect evaded suspected assassins in Baltimore. He traveled in disguise, wearing a soft felt hat instead of his customary stovepipe hat and draping an overcoat over his shoulders while hunching slightly to conceal his height. His friend Congressman Elihu B. Washburne recognized him on the platform upon arrival and loudly called out to him. On February 23, 1861, he arrived in Washington, D.C., which was placed under substantial military guard. Lincoln directed his inaugural address to the South, proclaiming once again that he had no inclination to abolish slavery in the Southern states: Lincoln cited his plans for banning the expansion of slavery as the key source of conflict between North and South, stating One section of our country believes slavery is right and ought to be extended, while the other believes it is wrong and ought not to be extended. Th" +44,"presidential nominee, without consulting Washington, issued a martial edict freeing slaves of the rebels. Lincoln canceled the proclamation as violating the Confiscation Act of 1861 and beyond Frémont's authority to issue. As a result, Union enlistments from Maryland, Kentucky, and Missouri increased by over 40,000. Internationally, Lincoln wanted to forestall foreign military aid to the Confederacy. He relied on his combative Secretary of State William Seward while working closely with Senate Foreign Relations Committee chairman Charles Sumner. In the 1861 Trent Affair, which threatened war with Great Britain, the U.S. Navy illegally intercepted a British mail ship, the Trent, on the high seas and seized two Confederate envoys; Britain protested vehemently while the U.S. cheered. Lincoln ended the crisis by releasing the two diplomats. Biographer James G. Randall dissected Lincoln's successful techniques: quoted in Peraino, Kevin (2013) Lincoln in the World: The Making of a Statesman" +45,"Salmon Chase poly 569 893 535 708 427 613 357 562 377 456 393 404 468 351 451 317 473 259 520 256 544 283 530 339 526 374 559 401 594 431 639 494 715 542 692 551 693 579 672 546 623 552 596 617 698 629 680 852 Abraham Lincoln poly 692 514 740 441 788 407 772 350 800 303 831 297 861 329 867 381 868 409 913 430 913 471 847 532 816 533 709 533 Gideon Welles poly 703 783 752 769 825 627 907 620 929 569 905 538 886 563 833 563 873 502 930 450 1043 407 1043 389 1036 382 1042 363 1058 335 1052 333 1052 324 1081 318 1124 338 1133 374 1116 412 1132 466 1145 509 1117 588 1087 632 1083 706 William Seward poly 905 418 941 328 987 295 995 284 982 244 990 206 1036 207 1046 247 1047 284 1066 312 1071 314 1049 327 1044 354 1033 383 1033 407 921 453 Caleb Smith poly 1081 308 1102 255 1095 220 1093 181 1109 161 1145 160 1169 191 1153 227 1153 246 1199 268 1230 310 1239 377 1237 443 1220 486 1125 451 1118 412 1136 378 1124 342 Montgomery Blair poly 1224 479 1298 416 1304 379 1295 329 1325 310 1360 324 13" +46,"1789, but in 1776, conceived in Liberty, and dedicated to the proposition that all men are created equal . He defined the war as dedicated to the principles of liberty and equality for all. He declared that the deaths of so many brave soldiers would not be in vain, that the future of democracy would be assured, and that government of the people, by the people, for the people, shall not perish from the earth . Defying his prediction that the world will little note, nor long remember what we say here , the Address became the most quoted speech in American history. ====Promoting General Grant==== Image:The Peacemakers 1868.jpg|thumb|upright=1.25|The Peacemakers, an 1868 painting by George P.A. Healy of events aboard the River Queen in March 1865 (clickable image—use cursor to identify)|alt=Painting of four men conferring in a ship's cabin, entitled The Peacemakers . poly 486 710 579 672 663 739 687 863 649 936 579 965 739 1133 782 1037 800 956 910 905 905 1025 855 1305 576 1223 513 1371 6" +47,"supported the war and repudiated the platform. Meanwhile, Lincoln emboldened Grant with more troops and Republican party support. Sherman's capture of Atlanta in September and David Farragut's capture of Mobile ended defeatism. The Democratic Party was deeply split, with some leaders and most soldiers openly for Lincoln. The National Union Party was united by Lincoln's support for emancipation. State Republican parties stressed the perfidy of the Copperheads. On November 8, Lincoln carried all but three states, including 78 percent of Union soldiers. On March 4, 1865, Lincoln delivered his second inaugural address. In it, he deemed the war casualties to be God's will. Historian Mark Noll places the speech among the small handful of semi-sacred texts by which Americans conceive their place in the world; it is inscribed in the Lincoln Memorial. Lincoln said: Among those present for this speech was actor John Wilkes Booth, who, on April 14, 1865, just over a month after Lincoln’s second i" +48,"a Colonel United States Volunteers to command the U.S. force tasked with fighting the war and that eventually defeated Little Crow's forces at the Battle of Wood Lake. The day the Mdewakanton force surrendered at Camp Release, a Chippewa war council met at Minnesota's capitol with another Chippewa offer to Lincoln, to fight the Sioux.Chippewa Chiefs held a war council with Gov. Ramsey to fight the Sioux on September 26, 1862 Sibley ordered a military commission to review the actions of the captured, to try those that had committed war crimes. The legitimacy of military commissions trying opposing combatants had been established during the Mexican War.Difference Between Court-Martial and Military Tribunal, Ernesto Gapasin, Military Trial Lawyers, Gapasin Law Group, LLC Blog, 1736 E Sunshine St Suite 713, Springfield, MO., October 26, 2015, Sibley thought he had 16-20 of the men he wanted for trial, while Gen. Pope ordered all detained be tried. 303 were given death sentences that were s" +49,"and had served as a judge in the Illinois court circuit where Lincoln practiced. Democrat Stephen Johnson Field, a previous California Supreme Court justice, provided geographic and political balance. Finally, Lincoln's Treasury Secretary, Salmon P. Chase, became Chief Justice. Lincoln believed Chase was an able jurist, would support Reconstruction legislation, and that his appointment united the Republican Party. ===Foreign policy=== Lincoln named his main political rival, William H. Seward, as Secretary of State and left most diplomatic issues in Seward's portfolio. However, Lincoln did select some top diplomats as part of his patronage policy.Neill F. Sanders, 'When A House Is on Fire': The English Consulates and Lincoln's Patronage Policy. Lincoln Herald (1981), 83#4, pp. 579–59. He also closely watched the handling of the Trent Affair in late 1861 to make sure the situation did not escalate into war with Britain.Kevin Peraino, Lincoln in the World: The Making of a Statesman and th" +50,"1864, to a newspaper editor in Kentucky: This spirituality can best be seen in his second inaugural address, considered by some scholars; White Jr., Ronald C., Lincoln's Greatest Speech: The Second Inaugural, New York: Simon & Schuster, 2002. as the greatest such address in American history, and by Lincoln himself as his own greatest speech, or one of them at the very least. Lincoln explains therein that the cause, purpose, and result of the war was God's will. Lincoln's frequent use of religious imagery and language toward the end of his life may have reflected his own personal beliefs or might have been a device to reach his audiences, who were mostly evangelical Protestants. On the day Lincoln was assassinated, he reportedly told his wife he desired to visit the Holy Land. ==Health== Lincoln is believed to have had depression, smallpox, and malaria. He took blue mass pills, which contained mercury, to treat constipation. It is unknown to what extent this may have resulted in mercury" +51,"served as a means for seeing the world's disappointments, for making its sufferings not so much explicable as meaningful. Franklin D. Roosevelt, preparing America for war, used the words of the Civil War president to clarify the threat posed by Germany and Japan. Americans asked, What would Lincoln do? However, Schwartz also finds that since World War II Lincoln's symbolic power has lost relevance, and this fading hero is symptomatic of fading confidence in national greatness. He suggested that postmodernism and multiculturalism have diluted greatness as a concept. In the Cold War years Lincoln's image shifted to a symbol of freedom who brought hope to those oppressed by Communist regimes. He had long been known as the Great Emancipator,The origin of the nickname is unknown. A Civil War Mystery: Who Named Lincoln the 'Great Emancipator'? Wheeler, Linda, The Washington Post, May 17, 2001. but, by the late 1960s, some African American intellectuals, led by Lerone Bennett Jr., denied that" +52,"New York: Hurd and Houghton, 1867, pubdate 1868. * * * * * * * * * * * * * * Ellenberg's essay is adapted from his 2021 book, Shape: The Hidden Geometry of Information, Biology, Strategy, Democracy, and Everything Else, Penguin Press. ISBN 9781984879059 * * * * * * * . Second edition, 2022. Wm. B. Eerdmans Publishing Company. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ==External links== ===Official=== * Abraham Lincoln Presidential Library and Museum * The Lincoln Presidential Library's ongoing digitization of all documents written by or to Abraham Lincoln during his lifetime * Collected Works of Abraham Lincoln – complete collected works as edited by Basler et al. (1958) – an online edition available through University of Michigan Library Digital Collections ===Organizations=== * Abraham Lincoln Association * Abraham Lincoln Bicentennial Foundation ===Media coverage=== * ===Other=== * Abraham Lincoln: A" +53,"Aristotle ( Aristotélēs; 384–322 BC) was an Ancient Greek philosopher and polymath. His writings cover a broad range of subjects spanning the natural sciences, philosophy, linguistics, economics, politics, psychology, and the arts. As the founder of the Peripatetic school of philosophy in the Lyceum in Athens, he began the wider Aristotelian tradition that followed, which set the groundwork for the development of modern science. Little is known about Aristotle's life. He was born in the city of Stagira in northern Greece during the Classical period. His father, Nicomachus, died when Aristotle was a child, and he was brought up by a guardian. At 17 or 18, he joined Plato's Academy in Athens and remained there until the age of 37 (). Shortly after Plato died, Aristotle left Athens and, at the request of Philip II of Macedon, tutored his son Alexander the Great beginning in 343 BC. He established a library in the Lyceum, which helped him to produce many of his hundreds of books on papyrus" +54,"the Hierophant reportedly denounced Aristotle for impiety, prompting him to flee to his mother's family estate in Chalcis, on Euboea, at which occasion he was said to have stated: I will not allow the Athenians to sin twice against philosophy – a reference to Athens's trial and execution of Socrates. He died in Chalcis, Euboea of natural causes later that same year, having named his student Antipater as his chief executor and leaving a will in which he asked to be buried next to his wife. == Theoretical philosophy == === Logic === With the Prior Analytics, Aristotle is credited with the earliest study of formal logic, and his conception of it was the dominant form of Western logic until 19th-century advances in mathematical logic. Kant stated in the Critique of Pure Reason that with Aristotle, logic reached its completion. ==== Organon ==== Most of Aristotle's work is probably not in its original form, because it was most likely edited by students and later lecturers. The logical works" +55,"can either 'act' (poiein) or 'be acted upon' (paschein), which can be either innate or learned. For example, the eyes possess the potentiality of sight (innate – being acted upon), while the capability of playing the flute can be possessed by learning (exercise – acting). Actuality is the fulfilment of the end of the potentiality. Because the end (telos) is the principle of every change, and potentiality exists for the sake of the end, actuality, accordingly, is the end. Referring then to the previous example, it can be said that an actuality is when a plant does one of the activities that plants do. In summary, the matter used to make a house has potentiality to be a house and both the activity of building and the form of the final house are actualities, which is also a final cause or end. Then Aristotle proceeds and concludes that the actuality is prior to potentiality in formula, in time and in substantiality. With this definition of the particular substance (i.e., matter and form)," +56,"that the reason for anything coming about can be attributed to four different types of simultaneously active factors. His term aitia is traditionally translated as cause , but it does not always refer to temporal sequence; it might be better translated as explanation , but the traditional rendering will be employed here. * Material cause describes the material out of which something is composed. Thus the material cause of a table is wood. It is not about action. It does not mean that one domino knocks over another domino. * The formal cause is its form, i.e., the arrangement of that matter. It tells one what a thing is, that a thing is determined by the definition, form, pattern, essence, whole, synthesis or archetype. It embraces the account of causes in terms of fundamental principles or general laws, as the whole (i.e., macrostructure) is the cause of its parts, a relationship known as the whole-part causation. Plainly put, the formal cause is the idea in the mind of the sculptor th" +57,"widely disbelieved until the 19th century. He gives accurate descriptions of the four-chambered fore-stomachs of ruminants, and of the ovoviviparous embryological development of the hound shark. He notes that an animal's structure is well matched to function so birds like the heron (which live in marshes with soft mud and live by catching fish) have a long neck, long legs, and a sharp spear-like beak, whereas ducks that swim have short legs and webbed feet. Darwin, too, noted these sorts of differences between similar kinds of animal, but unlike Aristotle used the data to come to the theory of evolution. Aristotle's writings can seem to modern readers close to implying evolution, but while Aristotle was aware that new mutations or hybridizations could occur, he saw these as rare accidents. For Aristotle, accidents, like heat waves in winter, must be considered distinct from natural causes. He was thus critical of Empedocles's materialist theory of a survival of the fittest origin of li" +58,"than the brain. Notable is Aristotle's division of sensation and thought, which generally differed from the concepts of previous philosophers, with the exception of Alcmaeon. In On the Soul, Aristotle famously criticizes Plato's theory of the soul and develops his own in response. The first criticism is against Plato's view of the soul in the Timaeus that the soul takes up space and is able to come into physical contact with bodies.On the Soul I.3 406b26-407a10. For some scholarship, see Carter, Jason W. 2017. 'Aristotle's Criticism of Timaean Psychology' Rhizomata 5: 51–78 and Douglas R. Campbell. 2022. Located in Space: Plato's Theory of Psychic Motion Ancient Philosophy 42 (2): 419–442. 20th-century scholarship overwhelmingly opposed Aristotle's interpretation of Plato and maintained that he had misunderstood him.For instance, W.D. Ross argued that Aristotle may well be criticized as having taken [Plato's] myth as if it were sober prose. See Ross, William D. ed. 1961. Aristotle: De" +59,"person to a person who is overtaken by strong feelings toward a stimulus. For example, a person who has a strong infatuation with someone may begin to think they see that person everywhere because they are so overtaken by their feelings. Since a person sleeping is in a suggestible state and unable to make judgements, they become easily deceived by what appears in their dreams, like the infatuated person. This leads the person to believe the dream is real, even when the dreams are absurd in nature. In De Anima iii 3, Aristotle ascribes the ability to create, to store, and to recall images in the absence of perception to the faculty of imagination, phantasia. One component of Aristotle's theory of dreams disagrees with previously held beliefs. He claimed that dreams are not foretelling and not sent by a divine being. Aristotle reasoned naturalistically that instances in which dreams do resemble future events are simply coincidences. Aristotle claimed that a dream is first established by" +60,"Money came into use because people became dependent on one another, importing what they needed and exporting the surplus. For the sake of convenience, people then agreed to deal in something that is intrinsically useful and easily applicable, such as iron or silver. Aristotle's discussions on retail and interest was a major influence on economic thought in the Middle Ages. He had a low opinion of retail, believing that contrary to using money to procure things one needs in managing the household, retail trade seeks to make a profit. It thus uses goods as a means to an end, rather than as an end unto itself. He believed that retail trade was in this way unnatural. Similarly, Aristotle considered making a profit through interest unnatural, as it makes a gain out of the money itself, and not from its use. Aristotle gave a summary of the function of money that was perhaps remarkably precocious for his time. He wrote that because it is impossible to determine the value of every good through" +61,"biology, political science, zoology, embryology, natural law, scientific method, rhetoric, psychology, realism, criticism, individualism, teleology, and meteorology. The scholar Taneli Kukkonen notes that in the best 20th-century scholarship Aristotle comes alive as a thinker wrestling with the full weight of the Greek philosophical tradition. What follows is an overview of the transmission and influence of his texts and ideas into the modern era. === His successor, Theophrastus === Aristotle's pupil and successor, Theophrastus, wrote the History of Plants, a pioneering work in botany. Some of his technical terms remain in use, such as carpel from carpos, fruit, and pericarp, from pericarpion, seed chamber. Theophrastus was much less concerned with formal causes than Aristotle was, instead pragmatically describing how plants functioned. === Later Greek philosophy === The immediate influence of Aristotle's work was felt as the Lyceum grew into the Peripatetic school. Aristotle's student" +62,"translation. Dante famously refers to him as he / Who is acknowledged Master of those who know .Inferno, Canto IV, lines 115-16 trans., 131 original, Robert Pinksky translation (1994); note to line, p.384 === Medieval Judaism === Moses Maimonides (considered to be the foremost intellectual figure of medieval Judaism) adopted Aristotelianism from the Islamic scholars and based his Guide for the Perplexed on it and that became the basis of Jewish scholastic philosophy. Maimonides also considered Aristotle to be the greatest philosopher that ever lived, and styled him as the chief of the philosophers .Levi ben Gershom, The Wars of the Lord: Book one, Immortality of the soul, p. 35.Leon Simon, Aspects Of The Hebrew Genius: A Volume Of Essays On Jewish Literature And Thought (1910), p. 127.Herbert A. Davidson, Herbert A. |q (Herbert Alan) Davidson, Professor of Hebrew Emeritus Herbert Davidson, Moses Maimonides: The Man and His Works, p. 98. Also, in his letter to Samuel ibn Tibbon, Maimoni" +63,"from those of Theophrastus and other Peripatetics, edited them, and finally compiled them into the more cohesive, larger works as they are known today. According to Strabo and Plutarch, after Aristotle's death, his library and writings went to Theophrastus (Aristotle's successor as head of the Lycaeum and the Peripatetic school). * After the death of Theophrastus, the peripatetic library went to Neleus of Scepsis. Some time later, the Kingdom of Pergamon began conscripting books for a royal library, and the heirs of Neleus hid their collection in a cellar to prevent it from being seized for that purpose. The library was stored there for about a century and a half, in conditions that were not ideal for document preservation. On the death of Attalus III, which also ended the royal library ambitions, the existence of Aristotelian library was disclosed, and it was purchased by Apellicon and returned to Athens in about 100 BC. Apellicon sought to recover the texts, many of which were seriou" +64,"Parmenides Publishing, . * Frede, Michael (1987). Essays in Ancient Philosophy. Minneapolis: University of Minnesota Press. * * Gendlin, Eugene T. (2012). Line by Line Commentary on Aristotle's De Anima , Volume 1: Books I & II; Volume 2: Book III. The Focusing Institute. * Gill, Mary Louise (1989). Aristotle on Substance: The Paradox of Unity. Princeton University Press. * * * * * * Jori, Alberto (2003). Aristotele, Bruno Mondadori (Prize 2003 of the International Academy of the History of Science ), . * * Knight, Kelvin (2007). Aristotelian Philosophy: Ethics and Politics from Aristotle to MacIntyre, Polity Press. * Lewis, Frank A. (1991). Substance and Predication in Aristotle. Cambridge University Press. * Lord, Carnes (1984). Introduction to The Politics, by Aristotle. Chicago University Press. * Loux, Michael J. (1991). Primary Ousia: An Essay on Aristotle's Metaphysics Ζ and Η. Ithaca, NY: Cornell University Press. * Maso, Stefano (Ed.), Natali, Carlo (Ed.), Seel, Gerhard (Ed.)" +65,"An American in Paris is a jazz-influenced symphonic poem (or tone poem) for orchestra by American composer George Gershwin first performed in 1928. It was inspired by the time that Gershwin had spent in Paris and evokes the sights and energy of the French capital during the . Gershwin scored the piece for the standard instruments of the symphony orchestra plus celesta, saxophones, and automobile horns. He brought back four Parisian taxi horns for the New York premiere of the composition, which took place on December 13, 1928, in Carnegie Hall, with Walter Damrosch conducting the New York Philharmonic.Alan Gilbert and the New York Philharmonic: Makoto Ozone to Perform Gershwin's Rhapsody in Blue in One-Night-Only Concert All-American Program Also to Include Bernstein's Candide Overture and Symphonic Dances from West Side Story Gershwin's An American in Paris: April 22, 2014 at nyphil.org Accessed June 20, 2017 It was Damrosch who had commissioned Gershwin to write his Concerto in F foll" +66,"horns as A, B, C, and D with circles, he was referring to the four horns, and not the notes that they played. A major revision of the work by composer and arranger F. Campbell-Watson simplified the instrumentation by reducing the saxophones to only three instruments: alto, tenor and baritone; the soprano and alto saxophone doublings were eliminated to avoid changing instruments. This became the standard performing edition until 2000, when Gershwin specialist Jack Gibbons made his own restoration of the original orchestration of An American in Paris, working directly from Gershwin's original manuscript, including the restoration of Gershwin's soprano saxophone parts removed in Campbell-Watson's revision. Gibbons' restored orchestration of An American in Paris was performed at London's Queen Elizabeth Hall on July 9, 2000, by the City of Oxford Orchestra conducted by Levon Parikian. William Daly arranged the score for piano solo; this was published by New World Music in 1929. ==Preservat" +67,"The Academy Award for Best Production Design recognizes achievement for art direction in film. The category's original name was Best Art Direction, but was changed to its current name in 2012 for the 85th Academy Awards. This change resulted from the Art Directors' branch of the Academy of Motion Picture Arts and Sciences (AMPAS) being renamed the Designers' branch. Since 1947, the award is shared with the set decorators. It is awarded to the best interior design in a film. The films below are listed with their production year (for example, the 2000 Academy Award for Best Art Direction is given to a film from 1999). In the lists below, the winner of the award for each year is shown first, followed by the other nominees in alphabetical order. ==Superlatives== Category Name Superlative Notes Most Awards Cedric Gibbons 11 awards Awards resulted from 39 nominations. Most Nominations Cedric Gibbons 39 nominations Nominations resulted in 11 awards. Most Nominations (without ever winning) Rol" +68,"assistant art directors were eligible for nomination. Black-and-White Black-and-White Black-and-White 1940 Prior to 1941, only credited art directors and assistant art directors were eligible for nomination. Pride and Prejudice Cedric Gibbons and Paul Groesse — 1940 Prior to 1941, only credited art directors and assistant art directors were eligible for nomination. Arise, My Love Hans Dreier and Robert Usher — 1940 Prior to 1941, only credited art directors and assistant art directors were eligible for nomination. Arizona Lionel Banks and Robert Peterson — 1940 Prior to 1941, only credited art directors and assistant art directors were eligible for nomination. The Boys from Syracuse Jack Otterson — 1940 Prior to 1941, only credited art directors and assistant art directors were eligible for nomination. Dark Command John Victor Mackay — 1940 Prior to 1941, only credited art directors and assistant art directors were eligible for nomination. Foreign Correspondent Alexander Golitzen — 194" +69,"Joseph C. Wright Thomas Little 1941 Republic Pictures submitted Sis Hopkins and it was initially named as a nominee. However, the studio later withdrew the film from consideration and it is not considered an official nominee. Louisiana Purchase Raoul Pene Du Bois Stephen Seymour 1942 Black-and-White Black-and-White Black-and-White 1942 This Above All Richard Day and Joseph C. Wright Thomas Little 1942 George Washington Slept Here Max Parker and Mark-Lee Kirk Casey Roberts 1942 The Magnificent Ambersons Albert S. D'Agostino A. Roland Fields and Darrell Silvera 1942 The Pride of the Yankees Perry Ferguson Howard Bristol 1942 Random Harvest Cedric Gibbons and Randall Duell Edwin B. Willis and Jack Moore 1942 The Shanghai Gesture Boris Leven Boris Leven 1942 Silver Queen Ralph Berger Emile Kuri 1942 The Spoilers John B. Goodman and Jack Otterson Russell A. Gausman and Edward Ray Robinson 1942 Take a Letter, Darling Hans Dreier and Roland Anderson Samuel M. Comer 1942 The Talk of the Town L" +70,"submitted Song of the Open Road and it was initially named as a nominee. However, the studio later withdrew the film from consideration and it is not considered an official nominee. Lady in the Dark Hans Dreier and Raoul Pene Du Bois Ray Moyer 1944 United Artists submitted Song of the Open Road and it was initially named as a nominee. However, the studio later withdrew the film from consideration and it is not considered an official nominee. The Princess and the Pirate Ernst Fegté Howard Bristol 1945 Black-and-White Black-and-White Black-and-White 1945 Blood on the Sun Wiard Ihnen A. Roland Fields 1945 Experiment Perilous Albert S. D'Agostino and Jack Okey Darrell Silvera and Claude E. Carpenter 1945 The Keys of the Kingdom James Basevi and William S. Darling Thomas Little and Frank E. Hughes 1945 Love Letters Hans Dreier and Roland Anderson Samuel M. Comer and Ray Moyer 1945 The Picture of Dorian Gray Cedric Gibbons and Hans Peters Edwin B. Willis and John Bonar and Hugh Hunt 1945 Col" +71,"Black-and-White Black-and-White 1954 On the Waterfront Richard Day — 1954 The Country Girl Roland Anderson and Hal Pereira Samuel M. Comer and Grace Gregory 1954 Executive Suite Cedric Gibbons and Edward Carfagno Edwin B. Willis and Emile Kuri 1954 Le Plaisir Max Ophüls — 1954 Sabrina Hal Pereira and Walter H. Tyler Samuel M. Comer and Ray Moyer 1954 Color Color Color 1954 20,000 Leagues Under the Sea John Meehan Emile Kuri 1954 Brigadoon Cedric Gibbons and E. Preston Ames Edwin B. Willis and F. Keogh Gleason 1954 Desiree Lyle R. Wheeler and Leland Fuller Walter M. Scott and Paul S. Fox 1954 Red Garters Hal Pereira and Roland Anderson Samuel M. Comer and Ray Moyer 1954 A Star Is Born Malcolm Bert and Gene Allen Irene Sharaff and George James Hopkins 1955 Black-and-White Black-and-White Black-and-White 1955 The Rose Tattoo Hal Pereira and Tambi Larsen Samuel M. Comer and Arthur Krams 1955 Blackboard Jungle Cedric Gibbons and Randall Duell Edwin B. Willis and Henry Grace 1955 I'll Cry To" +72,"1961 Summer and Smoke Hal Pereira and Walter Tyler Samuel M. Comer and Arthur Krams 1962 Black-and-White Black-and-White Black-and-White 1962 To Kill a Mockingbird Alexander Golitzen and Henry Bumstead Oliver Emert 1962 Days of Wine and Roses Joseph C. Wright George James Hopkins 1962 The Longest Day Ted Haworth, Léon Barsacq and Vincent Korda Gabriel Béchir 1962 Period of Adjustment George Davis and Edward Carfagno Henry Grace and Richard Pefferle 1962 The Pigeon That Took Rome Hal Pereira and Roland Anderson Samuel M. Comer and Frank R. McKelvy 1962 Color Color Color 1962 Lawrence of Arabia John Box and John Stoll Dario Simoni 1962 The Music Man Paul Groesse George James Hopkins 1962 Mutiny on the Bounty George Davis and Joseph McMillan Johnson Henry Grace and Hugh Hunt 1962 That Touch of Mink Alexander Golitzen and Robert Clatworthy George Milo 1962 The Wonderful World of the Brothers Grimm George Davis and Edward Carfagno Henry Grace and Richard Pefferle 1963 Black-and-White Black-" +73,"Ruby Levitt 1971 Bedknobs and Broomsticks John B. Mansbridge and Peter Ellenshaw Emile Kuri and Hal Gausman 1971 Fiddler on the Roof Robert F. Boyle and Michael Stringer Peter Lamont 1971 Mary, Queen of Scots Terence Marsh and Robert Cartwright Peter Howitt 1972 1972 Cabaret Rolf Zehetbauer and Hans Jürgen Kiebach Herbert Strabel 1972 Lady Sings the Blues Carl Anderson Reg Allen 1972 The Poseidon Adventure William Creber Raphaël Bretton 1972 Travels with My Aunt John Box, Gil Parrondo and Robert W. Laing — 1972 Young Winston Donald M. Ashton and Geoffrey Drake John Graysmark, William Hutchinson and Peter James 1973 1973 The Sting Henry Bumstead James W. Payne 1973 Brother Sun, Sister Moon Lorenzo Mongiardino and Gianni Quaranta Carmelo Patrono 1973 The Exorcist Bill Malley Jerry Wunderlich 1973 Tom Sawyer Philip M. Jefferies Robert De Vestel 1973 The Way We Were Stephen B. Grimes William Kiernan 1974 1974 The Godfather Part II Dean Tavoularis and Angelo P. Graham George R. Nelson 1974" +74,"Beecroft Lisa Dean 1990(63rd) The Godfather Part III Dean Tavoularis Gary Fettis 1990(63rd) Hamlet Dante Ferretti Francesca Lo Schiavo 1991(64th) 1991(64th) Bugsy Dennis Gassner Nancy Haigh 1991(64th) Barton Fink Dennis Gassner Nancy Haigh 1991(64th) The Fisher King Mel Bourne Cindy Carr 1991(64th) Hook Norman Garwood Garrett Lewis 1991(64th) The Prince of Tides Paul Sylbert Caryl Heller 1992(65th) 1992(65th) Howards End Luciana Arrighi Ian Whittaker 1992(65th) Bram Stoker's Dracula Thomas E. Sanders Garrett Lewis 1992(65th) Chaplin Stuart Craig Chris A. Butler 1992(65th) Toys Ferdinando Scarfiotti Linda DeScenna 1992(65th) Unforgiven Henry Bumstead Janice Blackie-Goodine 1993(66th) 1993(66th) Schindler's List Allan Starski Ewa Braun 1993(66th) Addams Family Values Ken Adam Marvin March 1993(66th) The Age of Innocence Dante Ferretti Robert J. Franco 1993(66th) Orlando Ben Van Os and Jan Roelfs — 1993(66th) The Remains of the Day Luciana Arrighi Ian Whittaker 1994(67th) 1994(67th) The M" +75,"Hotel Adam Stockhausen Anna Pinnock 2014(87th) The Imitation Game Maria Djurkovic Tatiana Macdonald 2014(87th) Interstellar Nathan Crowley Gary Fettis 2014(87th) Into the Woods Dennis Gassner Anna Pinnock 2014(87th) Mr. Turner Suzie Davies Charlotte Watts 2015(88th) 2015(88th) Mad Max: Fury Road Colin Gibson Lisa Thompson 2015(88th) Bridge of Spies Adam Stockhausen Rena DeAngelo and Bernhard Henrich 2015(88th) The Danish Girl Eve Stewart Michael Standish 2015(88th) The Martian Arthur Max Celia Bobak 2015(88th) The Revenant Jack Fisk Hamish Purdy 2016(89th) 2016(89th) La La Land David Wasco Sandy Reynolds-Wasco 2016(89th) Arrival Patrice Vermette Paul Hotte 2016(89th) Fantastic Beasts and Where to Find Them Stuart Craig Anna Pinnock 2016(89th) Hail, Caesar! Jess Gonchor Nancy Haigh 2016(89th) Passengers Guy Hendrix Dyas Gene Serdena 2017(90th) 2017(90th) The Shape of Water Paul Denham Austerberry Shane Vieau and Jeff Melvin 2017(90th) Beauty and the Beast Sarah Greenwood Katie Spencer 2" +76,"The Academy Awards, commonly known as the Oscars, are awards for artistic and technical merit for the film industry. They are presented annually by the Academy of Motion Picture Arts and Sciences (AMPAS) in the United States in recognition of excellence in cinematic achievements as assessed by the Academy's voting membership. The Oscars are widely considered to be the most prestigious awards in the film industry.Attributed to multiple references: The major award categories are presented during a live-televised Hollywood ceremony in February or March. It is the oldest worldwide entertainment awards ceremony. The 1st Academy Awards were held in 1929. The second ceremony, in 1930, was the first one broadcast by radio. The 1953 ceremony was the first one televised. It is the oldest of the four major annual American entertainment awards. Its equivalents – the Emmy Awards for television, the Tony Awards for theater, and the Grammy Awards for music – are modeled after the Academy Awards. The" +77,"mold was cast in 1928 at the C.W. Shumway & Sons Foundry in Batavia, Illinois, which also contributed to casting the molds for the Vince Lombardi Trophy and Emmy Award statuettes. From 1983 to 2015, approximately 50 Oscars in a tin alloy with gold plating were made each year in Chicago by Illinois manufacturer R.S. Owens & Company. () It would take between three and four weeks to manufacture 50 statuettes. In 2016, the Academy returned to bronze as the core metal of the statuettes, handing manufacturing duties to Walden, New York–based Polich Tallix Fine Art Foundry, now owned and operated by UAP Urban Art Projects. While based on a digital scan of an original 1929 Oscar, the statuettes retain their modern-era dimensions and black pedestal. Cast in liquid bronze from 3D-printed ceramic molds and polished, they are then electroplated in 24-karat gold by Brooklyn, New York–based Epner Technology. The time required to produce 50 such statuettes is roughly three months. R.S. Owens is expec" +78,"time to time (except for the Academy Honorary Award, the Technical Achievement Award, and the Student Academy Awards, which are presented annually): * Governors Awards: ** The Academy Honorary Award (annual) (which may or may not be in the form of an Oscar statuette); ** The Irving G. Thalberg Memorial Award (since 1938) (in the form of a bust of Thalberg); ** The Jean Hersholt Humanitarian Award (since 1957) (in the form of an Oscar statuette); * The Academy Scientific and Technical Awards: ** Academy Award of Merit (non-competitive) (in the form of an Oscar statuette); ** Scientific and Engineering Award (in the form of a bronze tablet); ** Technical Achievement Award (annual) (in the form of a certificate); ** The John A. Bonner Medal of Commendation (since 1978) (in the form of a medal); ** The Gordon E. Sawyer Award (since 1982); and * The Academy Student Academy Awards (annual). The Academy also awards Nicholl Fellowships in Screenwriting. == Nomination == From 2004 to 2020, the" +79,"are multiple methods of qualification. The main method is a week-long theatrical release in either New York City or Los Angeles County during the eligibility period. Films also can qualify by winning specified awards at one of several competitive film festivals designated by the Academy, also without regard to prior public distribution. A film that is selected as a gold, silver, or bronze medal winner in an appropriate category of the immediately previous Student Academy Awards is also eligible (Documentary category for that award, and Animation, Narrative, Alternative, or International for the other awards). The requirements for the qualifying theatrical run are also different from those for other awards. Only one screening per day is required. For the Documentary award, the screening must start between noon and 10 pm local time. For other awards, no specific start time is required, but the film must appear in regular theater listings with dates and screening times. In late December," +80,"to late February, to help disrupt and shorten the intense lobbying and ad campaigns associated with Oscar season in the film industry. Another reason was because of the growing television ratings success coinciding with the NCAA division I men's basketball tournament, which would cut into the Academy Awards audience. In 1976 and 1977, ABC's regained Oscars were moved from Tuesday to Monday and went directly opposite the national championship game on NBC. The earlier date is also to the advantage of ABC, as it now usually occurs during the highly profitable and important February sweeps period. Some years, the ceremony is moved into the first Sunday of March to avoid a clash with the Winter Olympic Games. Another reason for the move to late February and early March is to avoid the awards ceremony occurring so close to the religious holidays of Passover and Easter, which for decades had been a grievance from members and the general public. Advertising is somewhat restricted, as tradition" +81,"in the US, a box-office record that would remain unsurpassed for years. The 76th Academy Awards ceremony, in which The Lord of the Rings: The Return of the King (pre-telecast box office earnings of US$368 million) received 11 Awards, including Best Picture, drew 43.56 million viewers. The most-watched ceremony based on Nielsen ratings to date, was the 42nd Academy Awards (Best Picture Midnight Cowboy), which drew a 43.4% household rating on April 7, 1970. Hoping to reinvigorate the pre-show and ratings, the 2023 Oscars organizers hired members of the Met Gala creative team. By contrast, ceremonies honoring films that have not performed well at the box office tend to show weaker ratings, despite how much critical acclaim those films have received. The 78th Academy Awards, which awarded a low-budget independent film (Crash with a pre-Oscar gross of US$53.4 million) generated an audience of 38.64 million with a household rating of 22.91%. In 2008, the 80th Academy Awards telecast was watc" +82,"an annual basis. === Current special categories === * Academy Honorary Award: since 1929 * Academy Scientific and Technical Award (three different awards): since 1931 * Gordon E. Sawyer Award: since 1981 * Jean Hersholt Humanitarian Award: since 1957 * Irving G. Thalberg Memorial Award: since 1938 * Academy Special Achievement Award: from 1972 to 1995, and again for 2017 === Discontinued special categories === * Academy Juvenile Award: 1934 to 1960 == Criticism and controversies == === Accusations of commercialism === Due to the positive exposure and prestige of the Academy Awards, many studios spend around 25 million dollars and hire publicists specifically to promote their films during what is typically called the Oscar season . This has generated accusations of the Academy Awards being influenced more by marketing and lobbying than by quality. William Friedkin, an Academy Award–winning film director and former producer of the ceremony, expressed this sentiment at a conference in New" +83,"for typical Oscar-winning films and have much wider and earlier distribution than for other Best Picture-nominated films, while still being able to meet the minimal theatrical-run status to qualify for an Oscar. The United States Department of Justice, having heard of this potential rule change, wrote a letter to the Academy in March 2019, cautioning them that placing additional restrictions on films that originate from streaming media services without proper justification could raise anti-trust concerns against the Academy. Following its April 2019 board meeting, the Academy Board of Governors agreed to retain the current rules that allow for streaming media films to be eligible for Oscars as long as they enjoy limited theatrical runs. === 2022 Chris Rock and Will Smith slapping incident === During the 94th Academy Awards on March 27, 2022, Chris Rock joked about Jada Pinkett Smith's shaved head with a G.I. Jane reference. Will Smith walked onstage and slapped Rock across the face, th" +84,"image reading: Animation is cinema. A week later, Lord and his producing partner Christopher Miller wrote a guest column in Variety criticizing the Academy for the joke and how Hollywood has treated animation, writing that no one set out to diminish animated films, but it's high time we set out to elevate them. They also suggested to the Academy that the category should be presented by a filmmaker who respects the art of animation as cinema. Adding to the controversy was that the award for Best Animated Short Film (the nominees for which were mostly made up of shorts not aimed at children) was one of the eight categories that were not presented during the live broadcast. The winner for the Best Animated Short award was The Windshield Wiper, a multilingual Spanish-American film which is adult animated, while another nominee in three categories: Best Animated Feature, Best Documentary Feature Film, and Best International Feature Film, was Flee, a PG-13 rated animated documentary about an" +85,"1990 40.375 0.45 1989 42.619 0.375 1988 42.227 0.36 1987 37.190 0.335 1986 37.757 0.32 1985 38.855 0.315 1984 42.051 0.275 1983 53.235 0.245 1982 46.245 Not available Not available 1981 39.919 Not available Not available 1980 48.978 Not available Not available 1979 46.301 Not available Not available 1978 48.501 Not available Not available 1977 39.719 Not available Not available 1976 46.751 Not available Not available 1975 48.127 Not available Not available 1974 44.712 Not available Not available == Notable highest wins and nominees == === By films === The following nominees received at least 10 nominations: Nominations Title 14 All About Eve 14 Titanic 14 La La Land 13 Gone with the Wind 13 From Here to Eternity 13 Mary Poppins 13 Who's Afraid of Virginia Woolf? 13 Forrest Gump 13 Shakespeare in Love 13 The Lord of the Rings: The Fellowship of the Ring 13 Chicago 13 The Curious Case of Benjamin Button 13 The Shape of Water 13 Oppenheimer 12 Ben-Hur 12 Mrs. Miniver 12 The Song of Bernad" +86,"24 Woody Allen Filmmaker 24 Andy Nelson Sound engineer 23 Hans Dreier Art director 23 Hal Pereira Art director and production designer 22 Randy Newman Composer and songwriter 22 Samuel M. Comer Art director 22 Dimitri Tiomkin Composer 22 Victor Young Composer 22 Steven Spielberg Filmmaker 21 Kevin O'Connell Sound mixer 21 Meryl Streep Actress 21 Billy Wilder Filmmaker 20 Gary Rydstrom Sound designer and film director 19 Alan Menken Composer and songwriter 18 Henry Mancini Composer and songwriter 17 Gordon Hollingshead Film producer 17 Fred Quimby Animator 16 Greg P. Russell Sound engineer 16 Roger Deakins Cinematographer 16 Irene Sharaff Costume designer and art director 15 Thomas Newman Composer 15 Alex North Composer 15 Christopher Boyes Sound engineer 15 Sandy Powell Costume designer 15 William Wyler Filmmaker 15 Warren Beatty Actor and filmmaker 15 Diane Warren Songwriter 14 Ethan and Joel Coen Filmmakers 14 Francis Ford Coppola Filmmaker 14 Martin Scorsese Filmmaker 13 Richard Day" +87,"and songwriter 3 Peter Jackson Filmmaker 3 Maurice Jarre Composer 3 Michael Kahn Film editor 3 Paul Lambert Visual effects supervisor 3 Michel Legrand Composer and songwriter 3 Daniel Mandell Film editor 3 Jack Nicholson Actor 3 Thelma Schoonmaker Film editor 3 Stephen Schwartz Songwriter 3 Steven Spielberg Filmmaker 3 Max Steiner Composer 3 Meryl Streep Actress 3 Guillermo del Toro Director, producer, and writer 3 Fran Walsh Producer and writer 3 Ned Washington Songwriter 3 Paul Francis Webster Songwriter 3 Richard Williams Director and animator 3 3 3 == See also == * List of film awards * List of Academy Award–nominated films * List of actors with Academy Award nominations * List of superlative Academy Award winners and nominees == Footnotes == == References == == Further reading == * * * ** German-language book review of the book. * * * * == External links == * * Category:1929 establishments in California Category:1953 American television series debuts Category:Performing arts troph" +88,"Actresses (Catalan: Actrius) is a 1997 Catalan language Spanish drama film produced and directed by Ventura Pons and based on the award-winning stage play E.R. by Josep Maria Benet i Jornet. The film has no male actors, with all roles played by females. The film was produced in 1996. == Synopsis == In order to prepare herself to play a role commemorating the life of legendary actress Empar Ribera, young actress (Mercè Pons) interviews three established actresses who had been the Ribera's pupils: the international diva Glòria Marc (Núria Espert), the television star Assumpta Roca (Rosa Maria Sardà), and dubbing director Maria Caminal (Anna Lizaran). == Cast == * Núria Espert as Glòria Marc * Rosa Maria Sardà as Assumpta Roca * Anna Lizaran as Maria Caminal * Mercè Pons as Estudiant == Recognition == === Screenings === Actrius screened in 2001 at the Grauman's Egyptian Theatre in an American Cinematheque retrospective of the works of its director. The film had first screened at the same" +89,"Animalia is an illustrated children's book by Graeme Base. It was originally published in 1986, followed by a tenth anniversary edition in 1996, and a 25th anniversary edition in 2012. Over four million copies have been sold worldwide. A special numbered and signed anniversary edition was also published in 1996, with an embossed gold jacket. ==Synopsis== Animalia is an alliterative alphabet book and contains twenty-six illustrations, one for each letter of the alphabet. Each illustration features an animal from the animal kingdom (A is for alligator and armadillo, B is for butterfly, etc.) along with a short poem utilizing the letter of the page for many of the words. The illustrations contain many other objects beginning with that letter that the reader can try to identify (however, there are not necessarily a thousand things, or maybe more , as the author states). As an additional challenge, the author has hidden a picture of himself as a child in every picture. ==Related products==" +90,"International Atomic Time (abbreviated TAI, from its French name Temps atomique 1975) is a high-precision atomic coordinate time standard based on the notional passage of proper time on Earth's geoid. TAI is a weighted average of the time kept by over 450 atomic clocks in over 80 national laboratories worldwide. It is a continuous scale of time, without leap seconds, and it is the principal realisation of Terrestrial Time (with a fixed offset of epoch). It is the basis for Coordinated Universal Time (UTC), which is used for civil timekeeping all over the Earth's surface and which has leap seconds. UTC deviates from TAI by a number of whole seconds. , when another leap second was put into effect, UTC is currently exactly 37 seconds behind TAI. The 37 seconds result from the initial difference of 10 seconds at the start of 1972, plus 27 leap seconds in UTC since 1972. TAI may be reported using traditional means of specifying days, carried over from non-uniform time standards based on the" +91,"would remain parallel to the continuous UTC. == Relation to UTC == Contrary to TAI, UTC is a discontinuous time scale. It is occasionally adjusted by leap seconds. Between these adjustments, it is composed of segments that are mapped to atomic time by a constant offset. From its beginning in 1961 through December 1971, the adjustments were made regularly in fractional leap seconds so that UTC approximated UT2. Afterward, these adjustments were made only in whole seconds to approximate UT1. This was a compromise arrangement in order to enable a publicly broadcast time scale. The less frequent whole-second adjustments meant that the time scale would be more stable and easier to synchronize internationally. The fact that it continues to approximate UT1 means that tasks such as navigation which require a source of Universal Time continue to be well served by the public broadcast of UTC. == See also == * Clock synchronization * Time and frequency transfer == Notes == == References == * * ==" +92,"Altruism is the principle and practice of concern for the well-being and/or happiness of other humans or animals above oneself. While objects of altruistic concern vary, it is an important moral value in many cultures and religions. It may be considered a synonym of selflessness, the opposite of selfishness. The word altruism was popularized (and possibly coined) by the French philosopher Auguste Comte in French, as , for an antonym of egoism. He derived it from the Italian , which in turn was derived from Latin , meaning other people or somebody else . Altruism, as observed in populations of organisms, is when an individual performs an action at a cost to itself (in terms of e.g. pleasure and quality of life, time, probability of survival or reproduction) that benefits, directly or indirectly, another individual, without the expectation of reciprocity or compensation for that action. Altruism can be distinguished from feelings of loyalty or concern for the common good. The latter are" +93,"one another first. This may be due to better cooperativeness assessments or promises exchange. They are more cooperative if they can gradually build trust instead of being asked to give extensive help immediately. Direct reciprocity and cooperation in a group can be increased by changing the focus and incentives from intra-group competition to larger-scale competitions, such as between groups or against the general population. Thus, giving grades and promotions based only on an individual's performance relative to a small local group, as is common, may reduce cooperative behaviors in the group. ** Indirect reciprocity. Because people avoid poor reciprocators and cheaters, a person's reputation is important. A person esteemed for their reciprocity is more likely to receive assistance, even from individuals they have not directly interacted with before. ** Strong reciprocity. This form of reciprocity is expressed by people who invest more resources in cooperation and punishment than what" +94,"calculate how to increase their inclusive fitness when doing altruistic acts. Instead, evolution has shaped psychological mechanisms, such as emotions, that promote certain altruistic behaviors. The benefits for the altruist may be increased, and the costs reduced by being more altruistic towards certain groups. Research has found that people are more altruistic to kin than to no-kin, to friends than strangers, to those attractive than to those unattractive, to non-competitors than competitors, and to members in-groups than to members of out-groups. The study of altruism was the initial impetus behind George R. Price's development of the Price equation, a mathematical equation used to study genetic evolution. An interesting example of altruism is found in the cellular slime moulds, such as Dictyostelium mucoroides. These protists live as individual amoebae until starved, at which point they aggregate and form a multicellular fruiting body in which some cells sacrifice themselves to pro" +95,"when exposure to the situation could be easily avoided, whereas those lacking in empathic concern avoid allowing it unless it is difficult or impossible to avoid exposure to another's suffering. Helping behavior is seen in humans from about two years old when a toddler can understand subtle emotional cues. In psychological research on altruism, studies often observe altruism as demonstrated through prosocial behaviors such as helping, comforting, sharing, cooperation, philanthropy, and community service. People are most likely to help if they recognize that a person is in need and feel personal responsibility for reducing the person's distress. The number of bystanders witnessing pain or suffering affects the likelihood of helping (the Bystander effect). More significant numbers of bystanders decrease individual feelings of responsibility. However, a witness with a high level of empathic concern is likely to assume personal responsibility entirely regardless of the number of bystanders" +96,"views every soul as worthy of respect because it has the potential to become (God in Jainism). Because all living beings possess a soul, great care and awareness is essential in one's actions. Jainism emphasizes the equality of all life, advocating harmlessness towards all, whether the creatures are great or small. This policy extends even to microscopic organisms. Jainism acknowledges that every person has different capabilities and capacities to practice and therefore accepts different levels of compliance for ascetics and householders. ===Christianity=== Thomas Aquinas interprets You should love your neighbour as yourself and as meaning that love for ourselves is the exemplar of love for others.Thomas Aquinas, Summa Theologica, II:II Quaestio 25, Article 4 Considering that the love with which a man loves himself is the form and root of friendship he quotes Aristotle that the origin of friendly relations with others lies in our relations to ourselves ,.Aristotle, Nicomachean Ethics I" +97,"Bhai Kanhaiya. The Guru responded, Then you should also give them ointment to heal their wounds. You were practicing what you were coached in the house of the Guru. Under the tutelage of the Guru, Bhai Kanhaiya subsequently founded a volunteer corps for altruism, which is still engaged today in doing good to others and in training new recruits for this service. ===Hinduism=== In Hinduism Selflessness (), Love (), Kindness (), and Forgiveness () are considered as the highest acts of humanity or . Giving alms to the beggars or poor people is considered as a divine act or and Hindus believe it will free their souls from guilt or and will led them to heaven or in afterlife. Altruism is also the central act of various Hindu mythology and religious poems and songs. Mass donation of clothes to poor people (), or blood donation camp or mass food donation () for poor people is common in various Hindu religious ceremonies. The Bhagavad Gita supports the doctrine of karma yoga (achieving oneness" +98,"when Perform Acts of Extreme Altruism Rand and Epstein explored the behaviours of 51 Carnegie Hero Medal Recipients, demonstrating how extreme altruistic behaviours often stem from system I of the Dual Process Theory, which leads to rapid and intuitive behaviours. Additionally, a separate by Carlson et al. indicated that such prosocial behaviours are prevalent in emergencies where immediate actions are required. This discovery has led to ethical debates, particularly in the context of living organ donation, where laws regarding this issue differ by country. As observed in extreme altruists, these decisions are made intuitively, which may reflect insufficient consideration. Critics are concerned about whether this rapid decision encompasses a thorough cost-benefit analysis and question the appropriateness of exposing donors to such risk. * Social Discounting One finding suggests how extreme altruists exhibit lower levels of social discounting as compared to others. With that meaning ext" +99,"Alice O'Connor (born Alisa Zinovyevna Rosenbaum; , 1905 – March 6, 1982), better known by her pen name Ayn Rand ( ), was a Russian-born American author and philosopher. She is known for her fiction and for developing a philosophical system she named Objectivism. Born and educated in Russia, she moved to the United States in 1926. After two early novels that were initially unsuccessful and two Broadway plays, Rand achieved fame with her 1943 novel The Fountainhead. In 1957, she published her best-selling work, the novel Atlas Shrugged. Afterward, until her death in 1982, she turned to non-fiction to promote her philosophy, publishing her own periodicals and releasing several collections of essays. Rand advocated reason and rejected faith and religion. She supported rational and ethical egoism as opposed to altruism and hedonism. In politics, she condemned the initiation of force as immoral and supported laissez-faire capitalism, which she defined as the system based on recognizing indiv" +100,"put her in contact with other intellectuals sympathetic to free-market capitalism. She became friends with journalist Henry Hazlitt, who introduced her to the Austrian School economist Ludwig von Mises. Despite philosophical differences with them, Rand strongly endorsed the writings of both men, and they expressed admiration for her. Mises once called her the most courageous man in America , a compliment that particularly pleased her because he said man instead of woman . Rand became friends with libertarian writer Isabel Paterson. Rand questioned her about American history and politics during their many meetings, and gave Paterson ideas for her only non-fiction book, The God of the Machine. Rand's first major success as a writer came in 1943 with The Fountainhead, a novel about an uncompromising architect named Howard Roark and his struggle against what Rand described as second-handers who attempt to live through others, placing others above themselves. Twelve publishers rejected it b" +101,"she took controversial stances on political and social issues. These included supporting abortion rights, opposing the Vietnam War and the military draft (but condemning many draft dodgers as bums ), supporting Israel in the Yom Kippur War of 1973 against a coalition of Arab nations as civilized men fighting savages , claiming European colonists had the right to invade and take land inhabited by American Indians,Thompson, Stephen. Topographies of Liberal Thought: Rand and Arendt and Race . In . and calling homosexuality immoral and disgusting , despite advocating the repeal of all laws concerning it. She endorsed several Republican candidates for president of the United States, most strongly Barry Goldwater in 1964. In 1964, Nathaniel Branden began an affair with the young actress Patrecia Scott, whom he later married. Nathaniel and Barbara Branden kept the affair hidden from Rand. As her relationship with Nathaniel Branden deteriorated, Rand had her husband be present for difficult co" +102,"out of hate and showed remorseless hectoring and prolixity . Whittaker Chambers wrote what was later called the novel's most notorious review for the conservative magazine National Review. He accused Rand of supporting a godless system (which he related to that of the Soviets), claiming, From almost any page of Atlas Shrugged, a voice can be heard ... commanding: 'To a gas chamber—go!. Rand's nonfiction received far fewer reviews than her novels. The tenor of the criticism for her first nonfiction book, For the New Intellectual, was similar to that for Atlas Shrugged. Philosopher Sidney Hook likened her certainty to the way philosophy is written in the Soviet Union , and author Gore Vidal called her viewpoint nearly perfect in its immorality . These reviews set the pattern for reaction to her ideas among liberal critics. Her subsequent books got progressively less review attention. ===Academic assessments of Rand's fiction=== Academic consideration of Rand as a literary figure during h" +103,"term radical for capitalism . She worked with conservatives on political projects but disagreed with them over issues such as religion and ethics. Rand rejected anarchism as a naive theory based in subjectivism that would lead to collectivism in practice, and denounced libertarianism, which she associated with anarchism. Several critics, including Nozick, have said her attempt to justify individual rights based on egoism fails.Miller, Fred D., Jr. & Mossoff, Adam. Ayn Rand's Theory of Rights: An Exposition and Response to Critics . In Others, like libertarian philosopher Michael Huemer, have gone further, saying that her support of egoism and her support of individual rights are inconsistent positions.Miller, Fred D., Jr. & Mossoff, Adam. Ayn Rand's Theory of Rights: An Exposition and Response to Critics . In Some critics, like Roy Childs, have said that her opposition to the initiation of force should lead to support of anarchism, rather than limited government. ===Relationship to oth" +104,"year, few universities included Rand or Objectivism as a philosophical specialty or research area. From 2002 to 2012, over 60 colleges and universities accepted grants from the charitable foundation of BB&T that required teaching Rand's ideas or works; in some cases, the grants were controversial or even rejected because of the requirement to teach about Rand. In a 2010 essay for the Cato Institute, Huemer argued very few people find Rand's ideas convincing, especially her ethics. He attributed the attention she receives to her being a compelling writer , especially as a novelist. In 2012, the Pennsylvania State University Press agreed to take over publication of The Journal of Ayn Rand Studies, and the University of Pittsburgh Press launched an Ayn Rand Society Philosophical Studies series based on the Society's proceedings. The Fall 2012 update to the entry about Rand in the Stanford Encyclopedia of Philosophy said that only a few professional philosophers have taken her work serious" +105,"events with the novel's plot. Signs mentioning Rand and her fictional hero John Galt appeared at Tea Party protests. There was increased criticism of her ideas, especially from the political left. Critics blamed the Great Recession on her support of selfishness and free markets, particularly through her influence on Alan Greenspan. In 2015, Adam Weiner said that through Greenspan, Rand had effectively chucked a ticking time bomb into the boiler room of the US economy . Lisa Duggan said that Rand's novels had incalculable impact in encouraging the spread of neoliberal political ideas. In 2021, Cass Sunstein said Rand's ideas could be seen in the tax and regulatory policies of the Trump administration, which he attributed to the enduring influence of Rand's fiction. ===Objectivist movement=== After the closure of the Nathaniel Branden Institute, the Objectivist movement continued in other forms. In the 1970s, Peikoff began delivering courses on Objectivism. In 1979, Peter Schwartz starte" +106,"Alain Connes (; born 1 April 1947) is a French mathematician, known for his contributions to the study of operator algebras and noncommutative geometry. He is a professor at the , , Ohio State University and Vanderbilt University. He was awarded the Fields Medal in 1982. ==Career== Alain Connes attended high school at in Marseille, and was then a student of the classes préparatoires in . Between 1966 and 1970 he studied at École normale supérieure in Paris, and in 1973 he obtained a PhD from Pierre and Marie Curie University, under the supervision of Jacques Dixmier. From 1970 to 1974 he was research fellow at the French National Centre for Scientific Research and during 1975 he held a visiting position at Queen's University at Kingston in Canada. In 1976 he returned to France and worked as professor at Pierre and Marie Curie University until 1980 and at CNRS between 1981 and 1984. Moreover, since 1979 he holds the Léon Motchane Chair at IHES. From 1984 until his retirement in 2017 he" +107,"Allan Dwan (born Joseph Aloysius Dwan; April 3, 1885 – December 28, 1981) was a pioneering Canadian-born American motion picture director, producer, and screenwriter. ==Early life== Born Joseph Aloysius Dwan in Toronto, Ontario, Canada, Dwan was the younger son of commercial traveler of woolen clothing Joseph Michael Dwan (1857–1917) and his wife Mary Jane Dwan (née Hunt). The family moved to the United States when he was seven years old on December 4, 1892, by ferry from Windsor to Detroit, according to his naturalization petition of August 1939. His elder brother, Leo Garnet Dwan (1883–1964), became a physician. Allan Dwan studied engineering at the University of Notre Dame and then worked for a lighting company in Chicago. He had a strong interest in the fledgling motion picture industry, and when Essanay Studios offered him the opportunity to become a scriptwriter, he took the job. At that time, some of the East Coast movie makers began to spend winters in California where the clim" +108,"producer *Up in Mabel's Room (1944) *Abroad with Two Yanks (1944) *Getting Gertie's Garter (1945) also screenwriter *Brewster's Millions (1945) *Rendezvous with Annie (1946) *Driftwood (1947) *Calendar Girl (1947) *Northwest Outpost (1947) also associate producer *The Inside Story (1948) *Angel in Exile (1948) (with Philip Ford) *Sands of Iwo Jima (1949) *Surrender (1950) *Belle Le Grand (1951) *Wild Blue Yonder (1951) *I Dream of Jeanie (1952) *Montana Belle (1952) *Woman They Almost Lynched (1953) * Sweethearts on Parade (1953) *Silver Lode (1954) *Passion (1954) *Cattle Queen of Montana (1954) *Tennessee's Partner (1955) *Pearl of the South Pacific (1955) *Escape to Burma (1955) *Slightly Scarlet (1956) *Hold Back the Night (1956) *The Restless Breed (1957) *The River's Edge (1957) *Enchanted Island (1958) *Most Dangerous Man Alive (1961) ==See also== *Canadian pioneers in early Hollywood ==References== ==Further reading== *Brownlow, Kevin, The Parade's Gone By... (1968) *Bogdanovic" +109,"Algeria, officially the People's Democratic Republic of Algeria, is a country in the Maghreb region of North Africa. It is bordered to the northeast by Tunisia; to the east by Libya; to the southeast by Niger; to the southwest by Mali, Mauritania, and Western Sahara; to the west by Morocco; and to the north by the Mediterranean Sea. The capital and largest city is Algiers, located in the far north on the Mediterranean coast. Inhabited since prehistory, Algeria has been at the crossroads of numerous cultures and civilizations, including the Phoenicians, Romans, Vandals, Byzantine Greeks, and Turks. Its modern identity is rooted in centuries of Arab Muslim migration waves since the seventh century and the subsequent Arabization of the indigenous populations. Following a succession of Islamic Arab and Berber dynasties between the eighth and 15th centuries, the Regency of Algiers was established in 1516 as a largely independent tributary state of the Ottoman Empire. After nearly three cent" +110,"6000 and 2000 BC. This life, richly depicted in the Tassili n'Ajjer paintings, predominated in Algeria until the classical period. The mixture of peoples of North Africa coalesced eventually into a distinct native population that came to be called Berbers, who are the indigenous peoples of northern Africa. From their principal center of power at Carthage, the Carthaginians expanded and established small settlements along the North African coast; by 600 BC, a Phoenician presence existed at Tipasa, east of Cherchell, Hippo Regius (modern Annaba) and Rusicade (modern Skikda). These settlements served as market towns as well as anchorages. As Carthaginian power grew, its impact on the indigenous population increased dramatically. Berber civilisation was already at a stage in which agriculture, manufacturing, trade, and political organisation supported several states. Trade links between Carthage and the Berbers in the interior grew, but territorial expansion also resulted in the enslavemen" +111,"Tāghribāt. In Al-Tāghrībāt the Amazigh Zirid Hero Khālīfā Al-Zānatī asks daily, for duels, to defeat the Hilalan hero Ābu Zayd al-Hilalī and many other Arab knights in a string of victories. The Zirids, however, were ultimately defeated ushering in an adoption of Arab customs and culture. The indigenous Amazigh tribes, however, remained largely independent, and depending on tribe, location and time controlled varying parts of the Maghreb, at times unifying it (as under the Fatimids). The Fatimid Islamic state, also known as Fatimid Caliphate made an Islamic empire that included North Africa, Sicily, Palestine, Jordan, Lebanon, Syria, Egypt, the Red Sea coast of Africa, Tihamah, Hejaz and Yemen. Caliphates from Northern Africa traded with the other empires of their time, as well as forming part of a confederated support and trade network with other Islamic states during the Islamic Era. The Berber people historically consisted of several tribes. The two main branches were the Botr and B" +112,"Defeating the weakening Almoravid Empire and taking control over Morocco in 1147, they pushed into Algeria in 1152, taking control over Tlemcen, Oran, and Algiers,Jeff Huebner, Al Qal'a of Beni Hammad (M'sila, Algeria) in Middle East and Africa: International Dictionary of Historic Places (Vol. 4) (eds. K.A. Berney, Trudy Ring & Noelle Watson: Fitzroy Dearborn, 1996), pp. 36–39. wrestling control from the Hilian Arabs, and by the same year they defeated Hammadids who controlled Eastern Algeria. Following their decisive defeat in the Battle of Las Navas de Tolosa in 1212 the Almohads began collapsing, and in 1235 the governor of modern-day Western Algeria, Yaghmurasen Ibn Zyan declared his independence and established the Kingdom of Tlemcen and the Zayyanid dynasty. Warring with the Almohad forces attempting to restore control over Algeria for 13 years, they defeated the Almohads in 1248 after killing their Caliph in a successful ambush near Oujda.Jamil M. Abun-Nasr (20 August 1987). A" +113,"Algiers were later ransomed back to Iceland, but some chose to stay in Algeria. In 1629, pirate ships from Algeria raided the Faroe Islands. Vísindavefurinn: Voru Tyrkjarán framin í öðrum löndum? . Vísindavefurinn. In 1659, the Janissaries stationed in Algiers, also known commonly as the Odjak of Algiers; and the Reis or the company of corsair captains rebelled, they removed the Ottoman viceroy from power, and placed one of its own in power. The new leader received the title of Agha then Dey in 1671, and the right to select passed to the divan, a council of some sixty military senior officers. Thus Algiers became a sovereign military republic. It was at first dominated by the odjak; but by the 18th century, it had become the dey's instrument. Although Algiers remained nominally part of the Ottoman Empire, in reality they acted independently from the rest of the Empire, and often had wars with other Ottoman subjects and territories such as the Beylik of Tunis. The dey was in effect a co" +114,"heavily opposed this tendency, but in contrast to the other colonized countries' path in central Asia and Caucasus, Algeria kept its individual skills and a relatively human-capital intensive agriculture. During the Second World War, Algeria came under Vichy control before being liberated by the Allies in Operation Torch, which saw the first large-scale deployment of American troops in the North African campaign. Gradually, dissatisfaction among the Muslim population, which lacked political and economic status under the colonial system, gave rise to demands for greater political autonomy and eventually independence from France. In May 1945, the uprising against the occupying French forces was suppressed through what is now known as the Sétif and Guelma massacre. Tensions between the two population groups came to a head in 1954, when the first violent events of what was later called the Algerian War began after the publication of the Declaration of 1 November 1954. Historians have estim" +115,"similar protests across the Middle East and North Africa. On 24 February 2011, the government lifted Algeria's 19-year-old state of emergency. The government enacted legislation dealing with political parties, the electoral code, and the representation of women in elected bodies. In April 2011, Bouteflika promised further constitutional and political reform. However, elections are routinely criticised by opposition groups as unfair and international human rights groups say that media censorship and harassment of political opponents continue. On 2 April 2019, Bouteflika resigned from the presidency after mass protests against his candidacy for a fifth term in office. In December 2019, Abdelmadjid Tebboune became Algeria's president, after winning the first round of the presidential election with a record abstention rate – the highest of all presidential elections since Algeria's democracy in 1989. Tebboune is accused of being close to the military and being loyal to the deposed presiden" +116,"before he was brought down during the 2019 protests. In recent years, many of these generals have died, retired, or been imprisoned. After the death of General Larbi Belkheir, previous president Bouteflika put loyalists in key posts, notably at Sonatrach, and secured constitutional amendments that made him re-electable indefinitely, until he was brought down in 2019 during protests. The head of state is the President of Algeria, who is elected for a five-year term. The president is limited to two five-year terms. The most recent presidential election was planned to be in April 2019, but widespread protests erupted on 22 February against the president's decision to participate in the election, which resulted in President Bouteflika announcing his resignation on 3 April. Abdelmadjid Tebboune, an independent candidate, was elected as president after the election eventually took place on 12 December 2019. Protestors refused to recognise Tebboune as president, citing demands for comprehensi" +117,"When introducing new provinces, the numbers of old provinces are kept, hence the non-alphabetical order. With their official numbers, currently (since 1983) they are: ==Economy== Algeria's currency is the dinar (DZD). The economy remains dominated by the state, a legacy of the country's socialist post-independence development model. In June 2024 The World Bank's 2024 report marks a turning point for Algeria, which joins the select club of upper-middle-income countries. This economic rise, the result of an ambitious development strategy, places the country in the same category as emerging powers such as China, Brazil and Turkey In recent years, the Algerian government has halted the privatization of state-owned industries and imposed restrictions on imports and foreign involvement in its economy. These restrictions are just starting to be lifted off recently although questions about Algeria's slowly-diversifying economy remain. Algeria has struggled to develop industries outside hydroca" +118,"been implemented resulting in many hotels of a high modern standard being built. There are several UNESCO World Heritage Sites in Algeria including Al Qal'a of Beni Hammad, the first capital of the Hammadid empire; Tipasa, a Phoenician and later Roman town; and Djémila and Timgad, both Roman ruins; M'Zab Valley, a limestone valley containing a large urbanized oasis; and the Casbah of Algiers, an important citadel. The only natural World Heritage Site is the Tassili n'Ajjer, a mountain range. ===Transport=== thumb|The main highway connecting the Moroccan to the Tunisian border was a part of the Cairo–Dakar Highway project Two trans-African automobile routes pass through Algeria: * Cairo-Dakar Highway * Algiers-Lagos Highway The Algerian road network is the densest in Africa; its length is estimated at of highways, with more than 3,756 structures and a paving rate of 85%. This network will be complemented by the East-West Highway, a major infrastructure project currently under constructi" +119,"settlers left to France after the country's independence.De Azevedo, Raimondo Cagiano (1994) Migration and development co-operation. . Council of Europe. p. 25. . Today, estimates of the Christian population range from 20,000 to 200,000. Algerian citizens who are Christians predominantly belong to Protestant denominations, which have seen increased pressure from the government in recent years including many forced closures. According to the Arab Barometer in 2018–2019, the vast majority of Algerians (99.1%) continue to identify as Muslim. The June 2019 Arab Barometer-BBC News report found that the percentage of Algerians identifying as non-religious has grown from around 8% in 2013 to around 15% in 2018. The Arab Barometer December 2019, found that the growth in the percentage of Algerians identifying as non-religious is largely driven by young Algerians, with roughly 25% describing themselves as non-religious. However, the 2021 Arab Barometer report found that those who said they were" +120,"In this line, Mohamed Temam, Abdelkhader Houamel have also returned through this art, scenes from the history of the country, the habits and customs of the past and the country life. Other new artistic currents including the one of M'hamed Issiakhem, Mohammed Khadda and Bachir Yelles, appeared on the scene of Algerian painting, abandoning figurative classical painting to find new pictorial ways, to adapt Algerian paintings to the new realities of the country through its struggle and its aspirations. Mohammed Khadda and M'hamed Issiakhem have been notable in recent years. === Literature === The historic roots of Algerian literature go back to the Numidian and Roman African era, when Apuleius wrote The Golden Ass, the only Latin novel to survive in its entirety. This period had also known Augustine of Hippo, Nonius Marcellus and Martianus Capella, among many others. The Middle Ages have known many Arabic writers who revolutionised the Arab world literature, with authors like Ahmad al-Bun" +121,"Development, 1830–1987. Cambridge: Cambridge University Press. . * Christelow, Allan (1985) Muslim Law Courts and the French Colonial State in Algeria. Princeton University Press. * Fanon, Frantz (1966; 2005 paperback). The Wretched of the Earth. Grove Press. ASIN B0007FW4AW, . * * Horne, Alistair (1977). A Savage War of Peace: Algeria 1954–1962. Viking Adult. , (2006 reprint) * Laouisset, Djamel (2009). A Retrospective Study of the Algerian Iron and Steel Industry. New York City: Nova Publishers. . * Roberts, Hugh (2003). The Battlefield – Algeria, 1988–2002. Studies in a Broken Polity. London: Verso Books. . * Ruedy, John (1992). Modern Algeria – The Origins and Development of a Nation. Bloomington: Indiana University Press. . * Stora, Benjamin (2001). Algeria, 1830–2000 – A Short History. Ithaca, New York: Cornell University Press. . * Sidaoui, Riadh (2009). Islamic Politics and the Military – Algeria 1962–2008 . Religion and Politics – Islam and Muslim Civilisation. Farnham: Ashgat" +122,"This is a list of characters in Ayn Rand's 1957 novel Atlas Shrugged. ==Major characters== The following are major characters from the novel. ===Protagonists=== ====Dagny Taggart==== Dagny Taggart is the protagonist of the novel. She is vice president in charge of operations for Taggart Transcontinental, under her brother, James Taggart. Given James' incompetence, Dagny is responsible for all the workings of the railroad. ====Francisco d'Anconia==== Francisco d'Anconia is one of the central characters in Atlas Shrugged, an owner by inheritance of the world's largest copper mining operation. He is a childhood friend, and the first love, of Dagny Taggart. A child prodigy of exceptional talents, Francisco was dubbed the climax of the d'Anconia line, an already prestigious Argentine family of skilled industrialists. He was a classmate of John Galt and Ragnar Danneskjöld and student of both Hugh Akston and Robert Stadler. He began working while still in school to show that he could have bee" +123,"State Science Institute. He uses his position there to deride reason and productive achievement, and publishes a book entitled Why Do You Think You Think? He clashes on several occasions with Hank Rearden, and twice attempts to blackmail Rearden into giving up Rearden Metal. He is also one of the group of looters who tries to get Rearden to agree to the Steel Unification Plan. Ferris hosts the demonstration of the Project X weapon, and is the creator of the Ferris Persuader, a torture machine. When John Galt is captured by the looters, Ferris uses the device on Galt, but it breaks down before extracting the information Ferris wants from Galt. Ferris represents the group which uses brute force on the heroes to achieve the ends of the looters. ====Dr. Robert Stadler==== A former professor at Patrick Henry University, and along with colleague Hugh Akston, mentor to Francisco d'Anconia, John Galt and Ragnar Danneskjöld. He has since become a sell-out, one who had great promise but squander" +124,"Transcontinental train that is carrying him encounters a split rail, resulting in the destruction of its diesel engine. His demands lead to a coal-burning steam engine being attached to his train in its stead and used to pull it through an eight-mile tunnel. The result is the suffocation of all passengers and the destruction of the tunnel, caused when a train hauling military ordnance crashes into Chalmers' train and explodes. *Tom Colby is the head of the Rearden Steel Workers Union. *Dan Conway is the middle-aged president of the Phoenix-Durango railroad. Running a railroad is just about the only thing he knows. When the Anti-dog-eat-dog Rule is used to drive his business out of Colorado, he loses the will to fight, and resigns himself to a quiet life of books and fishing. He is not one of those who joined John Galt's strike, his resignation being a personal choice of his own. *Ken Danagger owns Danagger Coal in Pennsylvania. He helps Hank Rearden illegally make Rearden Metal, then l" +125,"Rearden's friends and a member of the cultural elite. *Clifton Locey is a friend of Jim Taggart who takes the position of vice-president of operation when Dagny Taggart quits. *Pat Logan is the engineer on the first run of the John Galt Line. He later strikes. *Kay Ludlow is a beautiful actress who quit Holywood because of the roles she was given and married secretly the pirate Ragnar Danneskjöld. *Roger Marsh is a producer of electrical equipment who joins the strike. Dagny finds him growing cabbage in Galt's Gulch. *Dick McNamara is a contractor who finished the San Sebastian Line. Dagny Taggart plans to hire him to lay the new Rearden Metal track for the Rio Norte Line, but before she does so, he mysteriously disappears. She later discovers that he has joined the strike and settled in Galt's Gulch. *Cuffy Meigs is the Director of Unification for the railroad business. He carries a pistol and a lucky rabbit's foot, dresses in a military uniform, and is described as impervious to thou" +126,"insist that the plan was perfect and that the failure of their father's company was entirely due to the workers. Eric was a weak, attention-seeking man with a pathological desire to be loved. He committed suicide after the woman he loved married another man. Gerald claims that he always acted for the good of the employees, but he was vain and incompetent and often threw lavish parties using company funds. Ivy, on the other hand, is described as a sadist who relishes seeing others in poverty, but who has no desire for wealth of her own. *Andrew Stockton runs the Stockton Foundry in Stockton, Colorado. When he joins the strike, he opens a foundry in Galt's Gulch. *Mr. Thompson is the Head of the State for the United States. He is not particularly intelligent and has a very undistinguished look. He knows politics, however, and is a master of public relations and back-room deals. Rand's notes indicate that she modeled him on President Harry S. Truman, and that she deliberately decided not" +127,"Anthropology is the scientific study of humanity, concerned with human behavior, human biology, cultures, societies, and linguistics, in both the present and past, including archaic humans. Social anthropology studies patterns of behavior, while cultural anthropology studies cultural meaning, including norms and values. The term sociocultural anthropology is commonly used today. Linguistic anthropology studies how language influences social life. Biological or physical anthropology studies the biological development of humans. Archaeology, often termed as anthropology of the past, studies human activity through investigation of physical evidence. It is considered a branch of anthropology in North America and Asia, while in Europe, archaeology is viewed as a discipline in its own right or grouped under other related disciplines, such as history and palaeontology. ==Etymology== The abstract noun anthropology is first attested in reference to history. Its present use first appeared in Ren" +128,"During the last three decades of the 19th century, a proliferation of anthropological societies and associations occurred, most independent, most publishing their own journals, and all international in membership and association. The major theorists belonged to these organizations. They supported the gradual osmosis of anthropology curricula into the major institutions of higher learning. By 1898, 48 educational institutions in 13 countries had some curriculum in anthropology. None of the 75 faculty members were under a department named anthropology. ===20th and 21st centuries=== Anthropology as a specialized field of academic study developed much through the end of the 19th century. Then it rapidly expanded beginning in the early 20th century to the point where many of the world's higher educational institutions typically included anthropology departments. Thousands of anthropology departments have come into existence, and anthropology has also diversified from a few major subdivision" +129,"of different cultures. The process of participant-observation can be especially helpful to understanding a culture from an emic (conceptual, vs. etic, or technical) point of view. The study of kinship and social organization is a central focus of sociocultural anthropology, as kinship is a human universal. Sociocultural anthropology also covers economic and political organization, law and conflict resolution, patterns of consumption and exchange, material culture, technology, infrastructure, gender relations, ethnicity, childrearing and socialization, religion, myth, symbols, values, etiquette, worldview, sports, music, nutrition, recreation, games, food, festivals, and language (which is also the object of study in linguistic anthropology). Comparison across cultures is a key element of method in sociocultural anthropology, including the industrialized (and de-industrialized) West. The Standard Cross-Cultural Sample (SCCS) includes 186 such cultures. ===Biological=== Biological anthro" +130,"and his French compatriot, Marcel Mauss, on the nature of gift-giving exchange (or reciprocity) as an alternative to market exchange. Economic Anthropology remains, for the most part, focused upon exchange. The school of thought derived from Marx and known as Political Economy focuses on production, in contrast. Economic anthropologists have abandoned the primitivist niche they were relegated to by economists, and have now turned to examine corporations, banks, and the global financial system from an anthropological perspective. ====Political economy==== Political economy in anthropology is the application of the theories and methods of historical materialism to the traditional concerns of anthropology, including, but not limited to, non-capitalist societies. Political economy introduced questions of history and colonialism to ahistorical anthropological theories of social structure and culture. Three main areas of interest rapidly developed. The first of these areas was concerned with" +131,"environmental and biological factors which influence health and illness both in the individual and the community as a whole * The critical analysis of interaction between psychiatric services and migrant populations ( critical ethnopsychiatry : Beneduce 2004, 2007) * The impact of biomedicine and biomedical technologies in non-Western settings Other subjects that have become central to medical anthropology worldwide are violence and social suffering (Farmer, 1999, 2003; Beneduce, 2010) as well as other issues that involve physical and psychological harm and suffering that are not a result of illness. On the other hand, there are fields that intersect with medical anthropology in terms of research methodology and theoretical production, such as cultural psychiatry and transcultural psychiatry or ethnopsychiatry. ==== Nutritional ==== Nutritional anthropology is a synthetic concept that deals with the interplay between economic systems, nutritional status and food security, and how chang" +132,"to prevent corporate exploitation and damage of land. Often, the observer has become an active part of the struggle either directly (organizing, participation) or indirectly (articles, documentaries, books, ethnographies). Such is the case with environmental justice advocate Melissa Checker and her relationship with the people of Hyde Park. ==== Environment ==== Social sciences, like anthropology, can provide interdisciplinary approaches to the environment. Professor Kay Milton, Director of the Anthropology research network in the School of History and Anthropology, describes anthropology as distinctive, with its most distinguishing feature being its interest in non-industrial indigenous and traditional societies. Anthropological theory is distinct because of the consistent presence of the concept of culture; not an exclusive topic but a central position in the study and a deep concern with the human condition. Milton describes three trends that are causing a fundamental shift in what" +133,"can assist in the identification of deceased individuals whose remains are decomposed, burned, mutilated or otherwise unrecognizable. The adjective forensic refers to the application of this subfield of science to a court of law. === Palaeoanthropology === Paleoanthropology combines the disciplines of paleontology and physical anthropology. It is the study of ancient humans, as found in fossil hominid evidence such as petrifacted bones and footprints. Genetics and morphology of specimens are crucially important to this field. Markers on specimens, such as enamel fractures and dental decay on teeth, can also give insight into the behaviour and diet of past populations. == Organizations == Contemporary anthropology is an established science with academic departments at most universities and colleges. The single largest organization of anthropologists is the American Anthropological Association (AAA), which was founded in 1903.AAAnet.org . AAAnet.org. Retrieved on 2 November 2016. Its mem" +134,"Association and amended through November 1986 stated that in relation with their own government and with host governments ... no secret research, no secret reports or debriefings of any kind should be agreed to or given. The current Principles of Professional Responsibility does not make explicit mention of ethics surrounding state interactions. Anthropologists, along with other social scientists, are working with the US military as part of the US Army's strategy in Afghanistan.US Army's strategy in Afghanistan: better anthropology . CSMonitor.com (7 September 2007). Retrieved on 2 November 2016. The Christian Science Monitor reports that Counterinsurgency efforts focus on better grasping and meeting local needs in Afghanistan, under the Human Terrain System (HTS) program; in addition, HTS teams are working with the US military in Iraq. In 2009, the American Anthropological Association's Commission on the Engagement of Anthropology with the US Security and Intelligence Communities (CEA" +135,"Atlantic region, frequently examining the connections between locations rather than limiting research to a single locale. There has also been a related shift toward broadening the focus beyond the daily life of ordinary people; increasingly, research is set in settings such as scientific laboratories, social movements, governmental and nongovernmental organizations and businesses.Fischer, Michael M. J. (2003) Emergent Forms of Life and the Anthropological Voice. Duke University Press. ==See also== * * Christian anthropology, a sub-field of theology * * * * * * * * * * * * Philosophical anthropology, a sub-field of philosophy * * === Lists === * * * ==Notes== ==References== ===Works cited=== * * * ==Further reading== ===Dictionaries and encyclopedias=== * * * * * ===Fieldnotes and memoirs=== * * * * * * * ===Histories=== * * * * * * * * * * * . * * * * ===Textbooks and key theoretical works=== * * * * * ==External links== * Open Encyclopedia of Anthropology. * (AIO) Category:Behavioural" +136,"Agricultural science (or agriscience for short) is a broad multidisciplinary field of biology that encompasses the parts of exact, natural, economic and social sciences that are used in the practice and understanding of agriculture. Professionals of the agricultural science are called agricultural scientists or agriculturists. ==History== In the 18th century, Johann Friedrich Mayer conducted experiments on the use of gypsum (hydrated calcium sulfate) as a fertilizer.John Armstrong, Jesse Buel. A Treatise on Agriculture, The Present Condition of the Art Abroad and at Home, and the Theory and Practice of Husbandry. To which is Added, a Dissertation on the Kitchen and Garden. 1840. p. 45. In 1843, John Bennet Lawes and Joseph Henry Gilbert began a set of long-term field experiments at Rothamsted Research in England, some of which are still running as of 2018. In the United States, a scientific revolution in agriculture began with the Hatch Act of 1887, which used the term agricultural sci" +137,"Alchemy (from the Arabic word , ) is an ancient branch of natural philosophy, a philosophical and protoscientific tradition that was historically practised in China, India, the Muslim world, and Europe. In its Western form, alchemy is first attested in a number of pseudepigraphical texts written in Greco-Roman Egypt during the first few centuries AD.Principe, Lawrence M. The secrets of alchemy . University of Chicago Press, 2012, pp. 9–14. Greek-speaking alchemists often referred to their craft as “the Art” (τέχνη) or “Knowledge” (ἐπιστήμη), and it was often characterised as mystic (μυστική), sacred (ἱɛρά), or divine (θɛíα). Alchemists attempted to purify, mature, and perfect certain materials. Common aims were chrysopoeia, the transmutation of base metals (e.g., lead) into noble metals (particularly gold); the creation of an elixir of immortality; and the creation of panaceas able to cure any disease. The perfection of the human body and soul was thought to result from the alchemical" +138,"systematic way. Zosimos of Panopolis wrote in the Final Abstinence (also known as the Final Count ).The title of the τελευταὶα ἀποχή is traditionally translated as the Final Count . Considering that the treatise does not mention any count nor counting and that it makes a case against the use of sacrifice in the practice of alchemy, a preferable translation would be the Final Abstinence . See Zosimos explains that the ancient practice of tinctures (the technical Greek name for the alchemical arts) had been taken over by certain demons who taught the art only to those who offered them sacrifices. Since Zosimos also called the demons the guardians of places (, ) and those who offered them sacrifices priests (, ), it is fairly clear that he was referring to the gods of Egypt and their priests. While critical of the kind of alchemy he associated with the Egyptian priests and their followers, Zosimos nonetheless saw the tradition's recent past as rooted in the rites of the Egyptian temples." +139,"(refined) Sugar. Buddhist texts from the 2nd to 5th centuries mention the transmutation of base metals to gold. According to some scholars Greek alchemy may have influenced Indian alchemy but there are no hard evidences to back this claim.Multhauf, Robert P. & Gilbert, Robert Andrew (2008). Alchemy. Encyclopædia Britannica (2008). The 11th-century Persian chemist and physician Abū Rayhān Bīrūnī, who visited Gujarat as part of the court of Mahmud of Ghazni, reported that they The goals of alchemy in India included the creation of a divine body (Sanskrit divya-deham) and immortality while still embodied (Sanskrit jīvan-mukti). Sanskrit alchemical texts include much material on the manipulation of mercury and sulphur, that are homologized with the semen of the god Śiva and the menstrual blood of the goddess Devī. Some early alchemical writings seem to have their origins in the Kaula tantric schools associated to the teachings of the personality of Matsyendranath. Other early writings are" +140,"transmutation of base metals into noble metals, Chinese alchemy had a more obvious connection to medicine.[Obed Simon Johnson, A Study of Chinese Alchemy, Shanghai, Commercial P, 1928. rpt. New York: Arno P, 1974.] The philosopher's stone of European alchemists can be compared to the Grand Elixir of Immortality sought by Chinese alchemists. In the hermetic view, these two goals were not unconnected, and the philosopher's stone was often equated with the universal panacea; therefore, the two traditions may have had more in common than initially appears. As early as 317 AD, Ge Hong documented the use of metals, minerals, and elixirs in early Chinese medicine. Hong identified three ancient Chinese documents, titled Scripture of Great Clarity, Scripture of the Nine Elixirs, and Scripture of the Golden Liquor, as texts containing fundamental alchemical information. He also described alchemy, along with meditation, as the sole spiritual practices that could allow one to gain immortality or t" +141,"after his death through to the 15th century, more than 28 alchemical tracts were misattributed to him, a common practice giving rise to his reputation as an accomplished alchemist.. Albertus Magnus and the Sciences: Commemorative Essays. PIMS. 1980. p.187-202 Likewise, alchemical texts have been attributed to Albert's student Thomas Aquinas. Roger Bacon, a Franciscan friar who wrote on a wide variety of topics including optics, comparative linguistics, and medicine, composed his Great Work () for as part of a project towards rebuilding the medieval university curriculum to include the new learning of his time. While alchemy was not more important to him than other sciences and he did not produce allegorical works on the topic, he did consider it and astrology to be important parts of both natural philosophy and theology and his contributions advanced alchemy's connections to soteriology and Christian theology. Bacon's writings integrated morality, salvation, alchemy, and the prolongati" +142,"spreading this new blend of Hermeticism outside the borders of Italy.Glenn Alexander Magee. Hegel and the Hermetic Tradition. Cornell University Press. 2008. p.30Nicholas Goodrick-Clarke. The Western Esoteric Traditions: A Historical Introduction. Oxford University Press. 2008 p.60 Paracelsus (Philippus Aureolus Theophrastus Bombastus von Hohenheim, 1493–1541) cast alchemy into a new form, rejecting some of Agrippa's occultism and moving away from chrysopoeia. Paracelsus pioneered the use of chemicals and minerals in medicine and wrote, Many have said of Alchemy, that it is for the making of gold and silver. For me such is not the aim, but to consider only what virtue and power may lie in medicines. His hermetical views were that sickness and health in the body relied on the harmony of man the microcosm and Nature the macrocosm. He took an approach different from those before him, using this analogy not in the manner of soul-purification but in the manner that humans must have certain" +143,"was now restricted to the realm of gold making, leading to the popular belief that alchemists were charlatans, and the tradition itself nothing more than a fraud. In order to protect the developing science of modern chemistry from the negative censure to which alchemy was being subjected, academic writers during the 18th-century scientific Enlightenment attempted to divorce and separate the new chemistry from the old practices of alchemy. This move was mostly successful, and the consequences of this continued into the 19th, 20th and 21st centuries. During the occult revival of the early 19th century, alchemy received new attention as an occult science. The esoteric or occultist school that arose during the 19th century held the view that the substances and operations mentioned in alchemical literature are to be interpreted in a spiritual sense, less than as a practical tradition or protoscience. This interpretation claimed that the obscure language of the alchemical texts, which 19th c" +144,"referred to experiments, and the most coveted ones were not those which were bizarre, but the ones which had been proven to yield the desired outcome. In this period, the only book of secrets ascribed to a woman was ('The Secrets of Signora Isabella Cortese'). This book contained information on how to turn base metals into gold, medicine, and cosmetics. However, it is rumoured that a man, Girolamo Ruscelli, was the real author and only used a female voice to attract female readers. In the nineteenth-century, Mary Anne Atwood's A Suggestive Inquiry into the Hermetic Mystery (1850) marked the return of women during the occult revival. === Modern historical research === The history of alchemy has become a recognized subject of academic study.Lawrence Principe. The Secrets of Alchemy. University of Chicago Press, 2015. As the language of the alchemists is analysed, historians are becoming more aware of the connections between that discipline and other facets of Western cultural history, su" +145,"the symbolic images in the alchemical drawings and the inner, symbolic images coming up in his patients' dreams, visions, or fantasies. He observed these alchemical images occurring during the psychic process of transformation, a process that Jung called individuation . Specifically, he regarded the conjuring up of images of gold or Lapis as symbolic expressions of the origin and goal of this process of individuation .Jung, C. G. (1944). Psychology and Alchemy (2nd ed. 1968 Collected Works Vol. 12 ). London: Routledge. E.g. § 41, § 116, § 427, § 431, § 448. Together with his alchemical mystica soror (mystical sister) Jungian Swiss analyst Marie-Louise von Franz, Jung began collecting old alchemical texts, compiled a lexicon of key phrases with cross-references,Anthony Stevens: On Jung. (A new and authoritiative introduction to Jung's life and thought), Penguin Books, London 1990, , p. 193. and pored over them. The volumes of work he wrote shed new light onto understanding the art of tr" +146,"d'Égypte, vol. 65, no 130, 1990, p. 219–242. * Korshi Dosoo, A History of the Theban Magical Library , Bulletin of the American Society of Papyrologists 53 (2016), p. 251–274. * Olivier Dufault, Early Greek Alchemy, Patronage and Innovation in Late Antiquity, California Classical Studies, 2019, Early Greek Alchemy, Patronage and Innovation in Late Antiquity. * Sergio Knipe, Sacrifice and self-transformation in the alchemical writings of Zosimus of Panopolis , in Christopher Kelly, Richard Flower, Michael Stuart Williams (eds.), Unclassical Traditions. Volume II: Perspectives from East and West in Late Antiquity, Cambridge University Press, 2011, p. 59–69. * André-Jean Festugière, La Révélation d'Hermès Trismégiste, Paris, Les Belles Lettres, 2014 , . * Kyle A. Fraser, Zosimos of Panopolis and the Book of Enoch: Alchemy as Forbidden Knowledge , Aries 4.2 (2004), p. 125–147. * Kyle A. Fraser, Baptized in Gnosis: The Spiritual Alchemy of Zosimos of Panopolis , Dionysius 25 (2007), p. 33–5" +147,"Alien primarily refers to: * Alien (law), a person in a country who is not a national of that country ** Enemy alien, the above in times of war * Extraterrestrial life, life which does not originate from Earth ** Specifically, a lifeform with extraterrestrial intelligence ** For fictional extraterrestrial life, see Extraterrestrials in fiction * Introduced species, a species not native to its environment Alien(s), or The Alien(s) may also refer to: == Science and technology == * AliEn (ALICE Environment), a grid framework * Alien (file converter), a Linux program * Alien Technology, a manufacturer of RFID technology == Arts and entertainment == * Alien (franchise), a media franchise ** Xenomorph, the titular alien in the franchise ===Films=== * Alien (film), a 1979 film by Ridley Scott ** Aliens (film), second film in the franchise from 1986 by James Cameron ** Alien 3, third film in the franchise from 1992 by David Fincher ** Alien Resurrection, fourth film in the franchise from 1997" +148,"An astronomer is a scientist in the field of astronomy who focuses on a specific question or field outside the scope of Earth. Astronomers observe astronomical objects, such as stars, planets, moons, comets and galaxies – in either observational (by analyzing the data) or theoretical astronomy. Examples of topics or fields astronomers study include planetary science, solar astronomy, the origin or evolution of stars, or the formation of galaxies. A related but distinct subject is physical cosmology, which studies the Universe as a whole. ==Types== Astronomers usually fall under either of two main types: observational and theoretical. Observational astronomers make direct observations of celestial objects and analyze the data. In contrast, theoretical astronomers create and investigate models of things that cannot be observed. Because it takes millions to billions of years for a system of stars or a galaxy to complete a life cycle, astronomers must observe snapshots of different systems" +149,"ASCII ( ), an acronym for American Standard Code for Information Interchange, is a character encoding standard for electronic communication. ASCII codes represent text in computers, telecommunications equipment, and other devices. ASCII has just 128 code points, of which only 95 are , which severely limit its scope. The set of available punctuation had significant impact on the syntax of computer languages and text markup. ASCII hugely influenced the design of character sets used by modern computers, including Unicode which has over a million code points, but the first 128 of these are the same as ASCII. The Internet Assigned Numbers Authority (IANA) prefers the name US-ASCII for this character encoding. ASCII is one of the IEEE milestones. ==Overview== ASCII was developed in part from telegraph code. Its first commercial use was in the Teletype Model 33 and the Teletype Model 35 as a seven-bit teleprinter code promoted by Bell data services. Work on the ASCII standard began in May 196" +150,"codes to be represented by a six-bit code. In a shifted code, some character codes determine choices between options for the following character codes. It allows compact encoding, but is less reliable for data transmission, as an error in transmitting the shift code typically makes a long part of the transmission unreadable. The standards committee decided against shifting, and so ASCII required at least a seven-bit code. The committee considered an eight-bit code, since eight bits (octets) would allow two four-bit patterns to efficiently encode two digits with binary-coded decimal. However, it would require all data transmission to send eight bits when seven could suffice. The committee voted to use a seven-bit code to minimize costs associated with data transmission. Since perforated tape at the time could record eight bits in one position, it also allowed for a parity bit for error checking if desired. Eight-bit machines (with octets as the native data type) that did not use parity" +151,"such as markup languages, address page and document layout and formatting. The original ASCII standard used only short descriptive phrases for each control character. The ambiguity this caused was sometimes intentional, for example where a character would be used slightly differently on a terminal link than on a data stream, and sometimes accidental, for example the standard is unclear about the meaning of delete . Probably the most influential single device affecting the interpretation of these characters was the Teletype Model 33 ASR, which was a printing terminal with an available paper tape reader/punch option. Paper tape was a very popular medium for long-term program storage until the 1980s, less costly and in some ways less fragile than magnetic tape. In particular, the Teletype Model 33 machine assignments for codes 17 (control-Q, DC1, also known as XON), 19 (control-S, DC3, also known as XOFF), and 127 (delete) became de facto standards. The Model 33 was also notable for takin" +152,"well established that backward compatibility necessitated continuing to follow it. When Gary Kildall created CP/M, he was inspired by some of the command line interface conventions used in DEC's RT-11 operating system. Until the introduction of PC DOS in 1981, IBM had no influence in this because their 1970s operating systems used EBCDIC encoding instead of ASCII, and they were oriented toward punch-card input and line printer output on which the concept of carriage return was meaningless. IBM's PC DOS (also marketed as MS-DOS by Microsoft) inherited the convention by virtue of being loosely based on CP/M, and Windows in turn inherited it from MS-DOS. Requiring two characters to mark the end of a line introduces unnecessary complexity and ambiguity as to how to interpret each character when encountered by itself. To simplify matters, plain text data streams, including files, on Multics used line feed (LF) alone as a line terminator. The tty driver would handle the LF to CRLF conversion" +153,"1111 037 31 1F S7 US US ␟ Unit Separator 111 1111 177 127 7F DEL DEL DEL ␡ Delete Other representations might be used by specialist equipment, for example ISO 2047 graphics or hexadecimal numbers. ===Printable characters=== Codes 20hex to 7Ehex, known as the printable characters, represent letters, digits, punctuation marks, and a few miscellaneous symbols. There are 95 printable characters in total. Code 20hex, the space character, denotes the space between words, as produced by the space bar of a keyboard. Since the space character is considered an invisible graphic (rather than a control character) it is listed in the table below instead of in the previous section. Code 7Fhex corresponds to the non-printable delete (DEL) control character and is therefore omitted from this chart; it is covered in the previous section's chart. Earlier versions of ASCII used the up arrow instead of the caret (5Ehex) and the left arrow instead of the underscore (5Fhex). Binary Oct Dec Hex Glyph Glyph G" +154,"support ASCII, stating: I have also approved recommendations of the Secretary of Commerce [Luther H. Hodges] regarding standards for recording the Standard Code for Information Interchange on magnetic tapes and paper tapes when they are used in computer operations. All computers and related equipment configurations brought into the Federal Government inventory on and after July 1, 1969, must have the capability to use the Standard Code for Information Interchange and the formats prescribed by the magnetic tape and paper tape standards when these media are used. ASCII was the most common character encoding on the World Wide Web until December 2007, when UTF-8 encoding surpassed it; UTF-8 is backward compatible with ASCII. ==Variants and derivations== As computer technology spread throughout the world, different standards bodies and corporations developed many variations of ASCII to facilitate the expression of non-English languages that used Roman-based alphabets. One could class some o" +155,"is based on ASCII-1963. Likewise, many Sharp MZ character sets are based on ASCII-1963. IBM defined code page 437 for the IBM PC, replacing the control characters with graphic symbols such as smiley faces, and mapping additional graphic characters to the upper 128 positions. Digital Equipment Corporation developed the Multinational Character Set (DEC-MCS) for use in the popular VT220 terminal as one of the first extensions designed more for international languages than for block graphics. Apple defined Mac OS Roman for the Macintosh and Adobe defined the PostScript Standard Encoding for PostScript; both sets contained international letters, typographic symbols and punctuation marks instead of graphics, more like modern character sets. The ISO/IEC 8859 standard (derived from the DEC-MCS) provided a standard that most systems copied (or at least were based on, when not copied exactly). A popular further extension designed by Microsoft, Windows-1252 (often mislabeled as ISO-8859-1), added" +156,"Austin refers to: ==Common meanings== * Austin, Texas, United States, a city * Austin (given name), a list of people and fictional characters * Austin (surname), a list of people and fictional characters * Austin Motor Company, a British car manufacturer ==Arts and entertainment== * Austin (album), by Post Malone, 2023 * Austin (Blake Shelton song), 2001 * Austin (Dasha song), 2023 * Austin (TV series), a 2024 Australian comedy series ==Businesses and organisations== ===Businesses=== * American Austin Car Company, short-lived American automobile maker * Austin Automobile Company, short-lived American automobile company * Austin Motor Company, British car manufacturer ** Austin magazine, produced for the Austin Motor Company by in-house Nuffield Press * Austin Airways, a former Canadian passenger airline and freight carrier * Austin cookies and crackers, a Keebler Company brand === Education === * Austin College, in Sherman, Texas, U.S. * Austin High School (disambiguation), several sch" +157,"Animation is a filmmaking technique by which still images are manipulated to create moving images. In traditional animation, images are drawn or painted by hand on transparent celluloid sheets (cels) to be photographed and exhibited on film. Animation has been recognized as an artistic medium, specifically within the entertainment industry. Many animations are either tradtional animations or computer animations made with computer-generated imagery (CGI). Stop motion animation, in particular claymation, has continued to exist alongside these other forms. Animation is contrasted with live-action film, although the two do not exist in isolation. Many moviemakers have produced films that are a hybrid of the two. As CGI increasingly approximates photographic imagery, filmmakers can easily composite 3D animations into their film rather than using practical effects for showy visual effects (VFX). == General overview == Computer animation can be very detailed 3D animation, while 2D computer an" +158,"Cristiani's Sin dejar rastros in 1918, but one day after its premiere, the film was confiscated by the government. After working on it for three years, Lotte Reiniger released the German feature-length silhouette animation Die Abenteuer des Prinzen Achmed in 1926, the oldest extant animated feature. In 1937, Walt Disney Studios premiered their first animated feature (using the Rotoscope technique invented by Max Fleischer in 1915) Snow White and the Seven Dwarfs, still one of the highest-grossing traditional animation features .Total prior to 50th anniversary reissue: 1987 and 1993 grosses from North America: The Fleischer studios followed this example in 1939 with Gulliver's Travels with some success. Partly due to foreign markets being cut off by the Second World War, Disney's next features Pinocchio, Fantasia (both 1940), Fleischer Studios' second animated feature Mr. Bug Goes to Town (1941–1942) and Disney's feature films Cinderella (1950), Alice in Wonderland (1951) and Lady and t" +159,"screen (which is often the case in Japan, where many manga are adapted into anime), original animated characters also commonly appear in comic books and magazines. Somewhat similarly, characters and plots for video games (an interactive form of animation that became its own medium) have been derived from films and vice versa. Some of the original content produced for the screen can be used and marketed in other media. Stories and images can easily be adapted into children's books and other printed media. Songs and music have appeared on records and as streaming media. While very many animation companies commercially exploit their creations outside moving image media, The Walt Disney Company is the best known and most extreme example. Since first being licensed for a children's writing tablet in 1929, their Mickey Mouse mascot has been depicted on an enormous amount of products, as have many other Disney characters. This may have influenced some pejorative use of Mickey's name, but lice" +160,"90 years. Some animation producers have used the term tradigital (a play on the words traditional and digital ) to describe cel animation that uses significant computer technology. Examples of traditionally animated feature films include Pinocchio (United States, 1940), Animal Farm (United Kingdom, 1954), Lucky and Zorba (Italy, 1998), and The Illusionist (British-French, 2010). Traditionally animated films produced with the aid of computer technology include The Lion King (US, 1994), Anastasia (US, 1997), The Prince of Egypt (US, 1998), Akira (Japan, 1988), Spirited Away (Japan, 2001), The Triplets of Belleville (France, 2003), and The Secret of Kells (Irish-French-Belgian, 2009). ====Full==== Full animation is the process of producing high-quality traditionally animated films that regularly use detailed drawings and plausible movement, having a smooth animation. Fully animated films can be made in a variety of styles, from more realistically animated works like those produced by the" +161,"interact with and exist as a part of a live-action world. Intercutting, matte effects and split screens are often employed to blend stop-motion characters or objects with live actors and settings. Examples include the work of Ray Harryhausen, as seen in films, Jason and the Argonauts (1963), and the work of Willis H. O'Brien on films, King Kong (1933). :;Go motion : A variant of model animation that uses various techniques to create motion blur between frames of film, which is not present in traditional stop motion. The technique was invented by Industrial Light & Magic and Phil Tippett to create special effect scenes for the film Star Wars: Episode V – The Empire Strikes Back (1980). Another example is the dragon named Vermithrax from the 1981 film Dragonslayer. ; Object animation : The use of regular inanimate objects in stop-motion animation, as opposed to specially created items. :; Graphic animation : Uses non-drawn flat visual graphic material (photographs, newspaper clippings, m" +162,"animation created by the makers of the television series Action League Now! in which characters/props are thrown, or chucked from off camera or wiggled around to simulate talking by unseen hands. * The Magic Lantern used mechanical slides to project moving images. Christiaan Huygens was thought to have invented the Magic Lantern in the mid-1600s. === Other === * Hydrotechnics: a technique that includes lights, water, fire, fog, and lasers, with high-definition projections on mist screens. * Drawn-on-film animation: a technique where footage is produced by creating the images directly on film stock; for example, by Norman McLaren, Len Lye and Stan Brakhage. * Paint-on-glass animation: a technique for making animated films by manipulating slow drying oil paints on sheets of glass, for example by Aleksandr Petrov. * Erasure animation: a technique using traditional 2D media, photographed over time as the artist manipulates the image. For example, William Kentridge is famous for his charcoa" +163,"Apollo is one of the Olympian deities in classical Greek and Roman religion and Greek and Roman mythology. Apollo has been recognized as a god of archery, music and dance, truth and prophecy, healing and diseases, the Sun and light, poetry, and more. One of the most important and complex of the Greek gods, he is the son of Zeus and Leto, and the twin brother of Artemis, goddess of the hunt. He is considered to be the most beautiful god and is represented as the ideal of the kouros (ephebe, or a beardless, athletic youth). Apollo is known in Greek-influenced Etruscan mythology as Apulu.Krauskopf, I. 2006. The Grave and Beyond. The Religion of the Etruscans. edited by N. de Grummond and E. Simon. Austin: University of Texas Press. p. vii, p. 73-75. As the patron deity of Delphi (Apollo Pythios), Apollo is an oracular god—the prophetic deity of the Delphic Oracle and also the deity of ritual purification. His oracles were often consulted for guidance in various matters. He was in general" +164,"would be the god of flocks and herds. In the ancient Macedonian language () means stone, R. S. P. Beekes, Etymological Dictionary of Greek, Brill, 2009, p. 1168. and some toponyms may be derived from this word: (Pella,. the capital of ancient Macedonia) and (Pellēnē/Pellene).Nilsson Vol I, p. 558 The Hittite form Apaliunas (d) is attested in the Manapa-Tarhunta letter.The reading of Apaliunas and the possible identification with Apollo is due to Emil Forrer (1931). It was doubted by Kretschmer, Glotta XXIV, p. 250. Martin Nilsson (1967), Vol I, p. 559 The Hittite testimony reflects an early form , which may also be surmised from the comparison of Cypriot with Doric . The name of the Lydian god Qλdãns /kʷʎðãns/ may reflect an earlier /kʷalyán-/ before palatalization, syncope, and the pre-Lydian sound change *y > d. Note the labiovelar in place of the labial /p/ found in pre-Doric Ἀπέλjων and Hittite Apaliunas. A Luwian etymology suggested for Apaliunas makes Apollo The One of Entrapment" +165,"his bow , the renowned archer.Homer, Odyssey 17.494 *Hecaërgus ( ; , Hekaergos), literally far-shooting *Hecebolus ( ; , Hekēbolos), far-shooting *Ismenius ( ; , Ismēnios), literally of Ismenus , after Ismenus, the son of Amphion and Niobe, whom he struck with an arrow ==== Appearance ==== * Acersecomes (, Akersekómēs), he who has unshorn hair , the eternal ephebe.See ἀκερσεκόμης * Chrysocomes ( ; , Khrusokómēs), literally he who has golden hair. ====Amazons==== *Amazonius (), Pausanias at the Description of Greece writes that near Pyrrhichus there was a sanctuary of Apollo, called Amazonius () with an image of the god said to have been dedicated by the Amazons.Pausanias, Description of Greece, § 3.25.3 ===Celtic epithets and cult titles=== Apollo was worshipped throughout the Roman Empire. In the traditionally Celtic lands, he was most often seen as a healing and sun god. He was often equated with Celtic gods of similar character.Miranda J. Green, Dictionary of Celtic Myth and Legend," +166,"had been rendered. It was in this way that Apollo had become recognized as the god of music. Apollo's role as the slayer of the Python led to his association with battle and victory; hence it became the Roman custom for a paean to be sung by an army on the march and before entering into battle, when a fleet left the harbour, and also after a victory had been won. In the Iliad, Apollo is the healer under the gods, but he is also the bringer of disease and death with his arrows, similar to the function of the Vedic god of disease Rudra. The conception that the diseases come from invisible shots sent by magicians or supernatural beings is common in primitive people and also in European folklore. In North-Europe they speak of the Elf-shots . In Sweden where the Lapps were called magicians, they speak of the Lappen-shots . Martin Nilsson (1967). Vol I, p. 541 He sends a plague () to the Achaeans. Knowing that Apollo can prevent a recurrence of the plague he sent, they purify themselves in a" +167,"3rd century CE, those at Didyma and Claros pronounced the so-called theological oracles , in which Apollo confirms that all deities are aspects or servants of an all-encompassing, highest deity. In the 3rd century, Apollo fell silent. Julian the Apostate (359–361) tried to revive the Delphic oracle, but failed. ===Oracular shrines=== Apollo had a famous oracle in Delphi, and other notable ones in Claros and Didyma. His oracular shrine in Abae in Phocis, where he bore the toponymic epithet Abaeus (, Apollon Abaios), was important enough to be consulted by Croesus.Herodotus, 1.46. His oracular shrines include: * Abae in Phocis. * Bassae in the Peloponnese. * At Clarus, on the west coast of Asia Minor; as at Delphi a holy spring which gave off a pneuma, from which the priests drank. * In Corinth, the Oracle of Corinth came from the town of Tenea, from prisoners supposedly taken in the Trojan War. * At Khyrse, in Troad, the temple was built for Apollo Smintheus. * In Delos, there was an or" +168,"(1967). Vol I, p. 529 The dimensions of the plan are 10.70 x 24.00 m and the building was not peripteral. It contains column-bases of the Minoan type, which may be considered as the predecessors of the Doric columns.Robertson pp. 56, 323. *Gortyn (Crete). A temple of Pythian Apollo, was built in the 7th century BC. The plan measured 19.00 x 16.70 m and it was not peripteral. The walls were solid, made from limestone, and there was a single door on the east side. *Thermon (West Greece): The Doric temple of Apollo Thermios, was built in the middle of the 7th century BC. It was built on an older curvilinear building dating perhaps from the 10th century, on which a peristyle was added. The temple was narrow, and the number of pteron columns (probably wooden) was 5 x 15. There was a single row of inner columns. It measures 12.13 x 38.23 m at the stylobate, which was made from stones.Spivey, p. 112 *Corinth: A Doric temple was built in the 6th century BC. The temple's stylobate measures 21.3" +169,"in the 3rd century BC., and it was peripteral with 6 x 11 columns. It was reconstructed at the end of the Hellenistic period, and later from the emperor Hadrian but Pausanias claims that it was still incomplete in the 2nd century BC.Prophecy centre of Apollo Clarius *Hamaxitus (Troad): In the Iliad, Chryses the priest of Apollo, addresses the god with the epithet Smintheus (Lord of Mice), related to the god's ancient role as bringer of the disease (plague). Recent excavations indicate that the Hellenistic temple of Apollo Smintheus was constructed in 150–125 BC, but the symbol of the mouse god was used on coinage probably from the 4th century .Bresson (2007) 154-5, citing the excavations reports of Özgünel (2001). The temple measures 40.00 x 23.00 m at the stylobate, and the number of pteron columns was 8 x 14.Robertson p. 333 *Pythion (), this was the name of a shrine of Apollo at Athens near the Ilisos river. It was created by Peisistratos, and tripods were placed there by those who" +170,"Apollo leaping forth from his mother's womb. The goddesses washed the newborn, covered him in a white garment and fastened golden bands around him. As Leto was unable to feed him, Themis, the goddess of divine law, fed him nectar and ambrosia. Upon tasting the divine food, the child broke free of the bands fastened onto him and declared that he would be the master of lyre and archery, and interpret the will of Zeus to humankind. He then started to walk, which caused the island to be filled with gold. Homeric hymn to Delian Apollo ====Callimachus' hymn to Delos==== The island Delos used to be Asteria, a goddess who jumped into the waters to escape the advances of Zeus and became a free-floating island of the same name. When Leto got pregnant, Hera was told that Leto's son would become more dear to Zeus than Ares. Enraged by this, Hera watched over the heavens and sent out Ares and Iris to prevent Leto from giving birth on the earth. Ares, stationed over the mainland, and Iris, over the" +171,"spent the winter months among the Hyperboreans,Apollonius Rhodius, Argonautica, 2. 674. leaving his shrine in Delphi under the care of Dionysus. His absence from the world caused coldness and this was marked as his annual death. No prophecies were issued during this time.Joseph Eddy Fontenrose, Python: A Study of Delphic Myth and Its Origins He returned to the world during the beginning of the spring. The Theophania festival was held in Delphi to celebrate his return.Plutarch, de his qui sero a num. pun. p. 557F However, Diodorus Silculus states that Apollo visited Hyperborea every nineteen years. This nineteen-year period was called by the Greeks as the 'year of Meton', the time period in which the stars returned to their initial positions. And that visiting Hyperborea at that time, Apollo played on the cithara and danced continuously from the vernal equinox until the rising of the Pleiades (constellations).Diodorus Siculus, Library of History 2. 47. 1 – 6 Hyperborea was also Leto's b" +172,"slain monster in a cauldron and deposited it in his temple.Pseudo-Hyginus, Fabulae 140 This legend is also narrated as the origin of the cry Hië paian . According to Athenaeus, Pyhton attacked Leto and her twins during their visit to Delphi. Taking Artemis into her arms, Leto climbed upon a rock and cried at Apollo to shoot the monster. The cry let out by her, ιε, παῖ ( Shoot, boy ) later got slightly altered as ἰὴ παιών (Hië paian), an exclamation to avert evils.Athenaeus, Deipnosophists 15.62 Callimachus attributes the origin of this phrase to the Delphians, who let out the cry to encourage Apollo when the young god battled with Python.Callimachus, Hymn to Apollo 97 Strabo has recorded a slightly different version where Python was actually a cruel and lawless man who was also known by the name Drakon . When Apollo was teaching the humans to cultivate fruits and civilise themselves, the residents of Parnassus complained to the god about Python. In response to their pleas, Apollo kille" +173,"funeral games to honor Python or to celebrate his own victory.Strabo, Geography 9. 3. 10Statius, Thebaid 6. 8 The Pythia was Apollo's high priestess and his mouthpiece through whom he gave prophecies. ====Tityus==== Tityus was another giant who tried to rape Leto, either on his own accord when she was on her way to DelphiHomer, Odyssey 11. 576Apollodorus, Bibliotheca 1.22 or at the order of Hera.Hyginus, Fabulae 55 Leto called upon on her children who instantly slew the giant. Apollo, still a young boy, shot him with his arrows.Apollonius Rhodius, Argonautica 1. 758Quintus Smyrnaeus, Fall of Troy 3. 390 In some accounts, Artemis also joined him in protecting their mother by attacking Tityos with her arrows.Scholia on Pindar, Pythian Odes 4.160 citing PherecydesPausanias, Description of Greece 10. 11. 1 For this act, he was banished to Tartarus and there he was pegged to the rock floor and stretched on an area of , while a pair of vultures feasted daily on his liver or his heart. Anothe" +174,"Chryses, and refused to return her. Angered by this, Apollo shot arrows infected with the plague into the Greek encampment. He demanded that they return the girl, and the Achaeans (Greeks) complied, indirectly causing the anger of Achilles, which is the theme of the Iliad. Receiving the aegis from Zeus, Apollo entered the battlefield as per his father's command, causing great terror to the enemy with his war cry. He pushed the Greeks back and destroyed many of the soldiers. He is described as the rouser of armies because he rallied the Trojan army when they were falling apart. When Zeus allowed the other gods to get involved in the war, Apollo was provoked by Poseidon to a duel. However, Apollo declined to fight him, saying that he would not fight his uncle for the sake of mortals. When the Greek hero Diomedes injured the Trojan hero Aeneas, Aphrodite tried to rescue him, but Diomedes injured her as well. Apollo then enveloped Aeneas in a cloud to protect him. He repelled the attacks D" +175,"Homo was replaced by A.The prefix A means without or not , and polloi means many , thus Apollo means not many or united , referring to his ability to create harmony.Plato, Cratylus Apollo's harmonious music delivered people from their pain, and hence, like Dionysus, he is also called the liberator. The swans, which were considered to be the most musical among the birds, were believed to be the singers of Apollo . They are Apollo's sacred birds and acted as his vehicle during his travel to Hyperborea. Aelian says that when the singers would sing hymns to Apollo, the swans would join the chant in unison.Aelian, On the nature of Animals 11. 1 Among the Pythagoreans, the study of mathematics and music were connected to the worship of Apollo, their principal deity.Aelian, Varia Historia, 2. 26Diogenes Laërtius, 8.13Iamblichus, Vit. Pyth. 8.91.141 Their belief was that music purifies the soul, just as medicine purifies the body. They also believed that music was delegated to the same mathema" +176,"punishment of being skinned for a wine sack.Philostratus the Younger, Imagines 2 (trans. Fairbanks) Another variation is that Apollo played his instrument upside down. Marsyas could not do this with his instrument. So the Muses who were the judges declared Apollo the winner. Apollo hung Marsyas from a tree to flay him.Man Myth and Magic by Richard Cavendish Apollo flayed the limbs of Marsyas alive in a cave near Celaenae in Phrygia for his hubris to challenge a god. He then gave the rest of his body for proper burialHyginus, Fabulae 165. and nailed Marsyas' flayed skin to a nearby pine-tree as a lesson to the others. Marsyas' blood turned into the river Marsyas. But Apollo soon repented and being distressed at what he had done, he tore the strings of his lyre and threw it away. The lyre was later discovered by the Muses and Apollo's sons Linus and Orpheus. The Muses fixed the middle string, Linus the string struck with the forefinger, and Orpheus the lowest string and the one next to i" +177,"Apollo and Heracles blinded the giant Ephialtes by shooting him in his eyes, Apollo shooting his left and Heracles his right.Apollodorus, 1.6.2. He also killed Porphyrion, the king of giants, using his bow and arrows.Pindar, Pythian 8.12–18. ====Aloadae==== The Aloadae, namely Otis and Ephialtes, were twin giants who decided to wage war upon the gods. They attempted to storm Mt. Olympus by piling up mountains, and threatened to fill the sea with mountains and inundate dry land.Grimal, s.v. Aloadae, p. 34. They even dared to seek the hand of Hera and Artemis in marriage. Angered by this, Apollo killed them by shooting them with arrows.Homer, Odyssey 11.305. According to another tale, Apollo killed them by sending a deer between them; as they tried to kill it with their javelins, they accidentally stabbed each other and died.Hyginus, Fabulae 28. ====Phorbas==== Phorbas was a savage giant king of Phlegyas who was described as having swine-like features. He wished to plunder Delphi for its" +178,"together founded the city of Gythion.Pausanias, Description of Greece 3. 21. 8 ==== Plato's concept of soulmates ==== A long time ago, there were three kinds of human beings: male, descended from the sun; female, descended from the earth; and androgynous, descended from the moon. Each human being was completely round, with four arms and four legs, two identical faces on opposite sides of a head with four ears, and all else to match. They were powerful and unruly. Otis and Ephialtes even dared to scale Mount Olympus. To check their insolence, Zeus devised a plan to humble them and improve their manners instead of completely destroying them. He cut them all in two and asked Apollo to make necessary repairs, giving humans the individual shape they still have now. Apollo turned their heads and necks around towards their wounds, he pulled together their skin at the abdomen, and sewed the skin together at the middle of it. This is what we call navel today. He smoothened the wrinkles and shap" +179,"by Apollo.Stesichorus, Fr.108 Coronis was daughter of Phlegyas, King of the Lapiths. While pregnant with Asclepius, Coronis fell in love with Ischys, son of Elatus and slept with him. When Apollo found out about her infidelity through his prophetic powers or thanks to his raven who informed him, he sent his sister, Artemis, to kill Coronis. Apollo rescued the baby by cutting open Coronis' belly and gave it to the centaur Chiron to raise. Dryope, the daughter of Dryops, was impregnated by Apollo in the form of a snake. She gave birth to a son named Amphissus.Antoninus Liberalis, Metamorphoses, 32; Ovid, Metamorphoses 9.330. In Euripides' play Ion, Apollo fathered Ion by Creusa, wife of Xuthus. He used his powers to conceal her pregnancy from her father. Later, when Creusa left Ion to die in the wild, Apollo asked Hermes to save the child and bring him to the oracle at Delphi, where he was raised by a priestess. Apollo loved and kidnapped an Oceanid nymph, Melia. Her father Oceanus sent" +180,"& Days 23) They were nicknamed Nete, Mese and Hypate after the highest, middle and lowest strings of his lyre. Phemonoe was a seer and poet who was the inventor of Hexameter. Apis, Idmon, Iamus, Tenerus, Mopsus, Galeus, Telmessus and others were gifted seers. Anius, Pythaeus and Ismenus lived as high priests. Most of them were trained by Apollo himself. Arabus, Delphos, Dryops, Miletos, Tenes, Epidaurus, Ceos, Lycoras, Syrus, Pisus, Marathus, Megarus, Patarus, Acraepheus, Cicon, Chaeron and many other sons of Apollo, under the guidance of his words, founded eponymous cities. He also had a son by Agathippe who was named Chrysorrhoas who was a mechanic artist.Plutarch, Of the Names of Rivers and Mountains, and Of Such Things as are to be Found Therein His other daughters include Eurynome, Chariclo wife of Chiron, Eurydice the wife of Orpheus, Eriopis, famous for her beautiful hair, Melite the heroine, Pamphile the silk weaver, Parthenos, and by some accounts, Phoebe, Hilyra and Scylla. A" +181,"nymph who scorned Apollo's advances and ran away from him. When Apollo chased her in order to persuade her, she changed herself into a laurel tree. According to other versions, she cried for help during the chase, and Gaia helped her by taking her in and placing a laurel tree in her place.Hyginus, Fabulae 203. According to Roman poet Ovid, the chase was brought about by Cupid, who hit Apollo with a golden arrow of love and Daphne with a leaden arrow of hatred.Ovid, Metamorphoses 1.452–567; Tripp, s.v. Daphne. The myth explains the origin of the laurel and the connection of Apollo with the laurel and its leaves, which his priestess employed at Delphi. The leaves became the symbol of victory and laurel wreaths were given to the victors of the Pythian games. Marpessa was kidnapped by Idas but was loved by Apollo as well. Zeus made her choose between them, and she chose Idas on the grounds that Apollo, being immortal, would tire of her when she grew old.Apollodorus, 1.7.8–9; cf. Homer, Ili" +182,"principal goddess of Athens, Apollo was the principal god of Sparta. As patrons of arts, Apollo and Athena were companions of the Muses, the former a much more frequent companion than the latter.Peter Dawkins, The Shakespeare Enigma Apollo was sometimes called the son of Athena and Hephaestus.Cicero, De Natura Deorum 3.22. In the Trojan War, as Zeus' executive, Apollo is seen holding the aegis like Athena usually does.Homer, Iliad 15.308. Apollo's decisions were usually approved by his sister Athena, and they both worked to establish the law and order set forth by Zeus.1.Homer, Iliad, Euripides, Ion, Aeschylus, Oresteia ===Apollo in the Oresteia=== In Aeschylus' Oresteia trilogy, Clytemnestra kills her husband, King Agamemnon because he had sacrificed their daughter Iphigenia to proceed forward with the Trojan war. Apollo gives an order through the Oracle at Delphi that Agamemnon's son, Orestes, is to kill Clytemnestra and Aegisthus, her lover. Orestes and Pylades carry out the revenge" +183,"Dionysus. This contrast appears to be shown on the two sides of the Borghese Vase. Apollo is often associated with the Golden Mean. This is the Greek ideal of moderation and a virtue that opposes gluttony. In antiquity, Apollo was associated with the planet Mercury. The ancient Greeks believed that Mercury as observed during the morning was a different planet than the one during the evening, because each twilight Mercury would appear farther from the Sun as it set than it had the night before. The morning planet was called Apollo, and the one at evening Hermes/Mercury before they realised they were the same, thereupon the name 'Mercury/Hermes' was kept, and 'Apollo' was dropped. == Apollo in the arts == Apollo is a common theme in Greek and Roman art and also in the art of the Renaissance. The earliest Greek word for a statue is delight (, agalma), and the sculptors tried to create forms which would inspire such guiding vision. Maurice Bowra notices that the Greek artist puts into a go" +184,"Keith David in the 1997 animated feature film Hercules, by Luke Evans in the 2010 action film Clash of the Titans, and by Dimitri Lekkos in the 2010 film Percy Jackson & the Olympians: The Lightning Thief. ==== Video games ==== Apollo has appeared in many modern video games. Apollo appears as a minor character in Santa Monica Studio's 2010 action-adventure game God of War III with his bow being used by Peirithous. He also appears in the 2014 Hi-Rez Studios Multiplayer Online Battle Arena game Smite as a playable character. === Psychology and philosophy === In the philosophical discussion of the arts, a distinction is sometimes made between the Apollonian and Dionysian impulses, where the former is concerned with imposing intellectual order and the latter with chaotic creativity. Friedrich Nietzsche argued that a fusion of the two was most desirable. Psychologist Carl Jung's Apollo archetype represents what he saw as the disposition in people to over-intellectualise and maintain emotion" +185,"Archive 1926 edition. 14. Hyacinthus (170–245 CE) * Pindar, Odes, Diane Arnson Svarlien. 1990. Online version at the Perseus Digital Library. * Plutarch. Lives, Volume I: Theseus and Romulus. Lycurgus and Numa. Solon and Publicola. Translated by Bernadotte Perrin. Loeb Classical Library No. 46. Cambridge, Massachusetts: Harvard University Press, 1914. . Online version at Harvard University Press. Numa at the Perseus Digital Library. * Pseudo-Plutarch, De fluviis, in Plutarch's morals, Volume V, edited and translated by William Watson Goodwin, Boston: Little, Brown & Co., 1874. Online version at the Perseus Digital Library. * Lucian, Dialogues of the Dead. Dialogues of the Sea-Gods. Dialogues of the Gods. Dialogues of the Courtesans, translated by M. D. MacLeod, Loeb Classical Library No. 431, Cambridge, Massachusetts, Harvard University Press, 1961. . Online version at Harvard University Press. Internet Archive. * First Vatican Mythographer, 197. Thamyris et Musae * Tzetzes, John, Chil" +186,"Andre Kirk Agassi ( ; born April 29, 1970) is an American former world No. 1 tennis player. He is an eight-time major champion, an Olympic gold medalist, and a runner-up in seven other majors. Agassi is the second of five men in the Open Era to achieve the Career Grand Slam. He is also the first man to complete both the Career Golden Slam and the Career Super Slam, achieving this feat in 1999. Agassi was the first man to win all four singles majors on three different surfaces (hard, clay and grass), and remains the most recent American man to win the French Open (in 1999) and the Australian Open (in 2003). He also won 17 Masters titles and was part of the winning Davis Cup teams in 1990, 1992 and 1995. Agassi reached the world No. 1 ranking for the first time in 1995, but was troubled by personal issues during the mid-to-late 1990s and sank to No. 141 in 1997, prompting many to believe that his career was over. Agassi returned to No. 1 in 1999 and enjoyed the most successful run of his" +187,"which time he told his coach that he felt bad for Sampras because he was never going to make it as a pro. Agassi lost the US Open final to Sampras in three sets. The Agassi-Sampras rivalry became the biggest one in tennis over the rest of the decade. Agassi ended 1990 on a high note as he helped the United States win its first Davis Cup in 8 years on home soil against Australia (3–2) and won his only Tennis Masters Cup, beating reigning Wimbledon champion Stefan Edberg in the final. In 1991, Agassi reached his second consecutive French Open final, where he faced fellow Bollettieri Academy alumnus Jim Courier. Courier emerged the victor in a five-set final. The Las Vegan was a set and 3–1 up when came the rain. The rain delay proved to be a confidence builder for Courier. Agassi decided to play at Wimbledon in 1991, leading to weeks of speculation in the media about the clothes he would wear. He eventually emerged for the first round in a completely white outfit. He reached the quarterf" +188,"prompting many to believe that his run as one of the sport's premier competitors was over and he would never again win any significant tournaments. ===1998–2003: Return to glory and Career Super Slam=== In 1998, Agassi began a rigorous conditioning program and worked his way back up the rankings by playing in Challenger Series tournaments, a circuit for pro players ranked outside the world's top 50. After returning to top physical and mental shape, Agassi recorded the most successful period of his tennis career and also played classic matches in that period against Pete Sampras and Patrick Rafter. In 1998, Agassi won five titles and leapt from No. 110 to No. 6, the highest jump into the top 10 made by any player during a calendar year. At Wimbledon, he had an early loss in the second round to Tommy Haas. He won five titles in ten finals and was runner-up at the Masters Series tournament in Key Biscayne, losing to Marcelo Ríos, who became No. 1 as a result. At the year end he was awarde" +189,"lost to Juan Carlos Ferrero, surrendering his No. 1 ranking to him. During his career, Agassi held the ranking for a total of 101 weeks. Agassi's ranking slipped when injuries forced him to withdraw from a number of events. At the year-end Tennis Masters Cup, Agassi lost in the final to Federer, his third time to finish as runner-up in the event after losses in 1999 and 2000, and finished the year ranked No. 4. At age 33, he had been one of the oldest players to rank in the top 5 since Connors, at age 35, was No. 4 in 1987. ===2004–2006: Final years=== In 2004, Agassi began the year with a five-set loss in the semi-finals of the Australian Open to Marat Safin; the loss ended Agassi's 26-match winning streak at the event. He won the Masters series event in Cincinnati to bring his career total to 59 top-level singles titles and a record 17 ATP Masters Series titles, having already won seven of the nine ATP Masters tournament—all except the tournaments in Monte Carlo and Hamburg. At 34, h" +190,"the top two spots in the rankings for the whole year. They met five times during the year, all in the title matches, including the Australian Open, the Newsweek Champions Cup (now Indian Wells), the Lipton International Players Championships (now Miami Open), the Canadian Open, and the US Open. Agassi won three of the finals, including the Australian Open; however, Sampras took the US Open title, ending Agassi's 26-match winning streak. After Agassi had taken most of the fall season off, Sampras took over the No. 1 ranking for the end of the season. Agassi admitted this loss, which gave Sampras a 9–8 lead in their head-to-head meetings, took two years for him to get over it mentally. In the following three years, while Sampras continued winning Grand Slam titles every season, Agassi slumped in the rankings and struggled in major competitions. The next time Sampras and Agassi met in a Grand Slam final was at Wimbledon in 1999, where Sampras won in straight sets. For both, it was conside" +191,"two in 1993 and 1999, while Rafter took their 2000 and 2001 encounters, the latter two matches being gruelling 5-setters often being presented on the lists of best matches ever played. Agassi also won both their meetings at the Australian Open, in 1995 and 2001, on his way to the title on both occasions. Rafter, however, took their only US Open encounter in 1997 and went on to win the title. ===Agassi vs. Federer=== Agassi and Roger Federer played 11 times, and Federer led their head-to-head series 8–3. With the retirement of Sampras, the rivalry against the 11-years-younger Federer, who was another great server like Sampras, became Agassi's main rivalry for the final years of his career. Agassi won their first three matches, but then went on to lose eight consecutive ones. They first met in just the third tournament of Federer's career at the 1998 Swiss Indoors in Federer's hometown, with Agassi prevailing over the 17-year-old. Agassi also defeated Federer at the 2001 US Open and the" +192,"2010. Sampras, Agassi Have Just Begun to Fight Los Angeles Times. Retrieved 15, 2010. As a result, he is credited for helping to revive the popularity of tennis during the 1990s. Among his numerous career accolades, Agassi was named the BBC Overseas Sports Personality of the Year in 1992, and the 7th greatest male player of all time by Sports Illustrated in 2010. On July 9, 2011, Agassi was inducted into the International Tennis Hall of Fame at a ceremony in Newport, Rhode Island. Agassi earned more than $30 million in prize-money during his career, sixth only to Djokovic, Federer, Nadal, Sampras and Murray to date (May 2018). He also earned more than $25 million a year through endorsements during his career, which was ranked fourth in all sports at the time. ==Playing style== Early in his career, Agassi would look to end points quickly by playing first-strike tennis, typically by inducing a weak return with a deep, hard shot, and then playing a winner at an extreme angle. On the rare" +193,"balls. On July 25, 2005, Agassi left Nike after 17 years and signed an endorsement deal with Adidas. A major reason for Agassi leaving Nike was because Nike refused to donate to Agassi's charities, and Adidas did. On May 13, 2013, Agassi rejoined Nike. Agassi was sponsored by DuPont, Ebel, Mountain Dew in 1993, Mazda in 1997, Kia Motors in 2002, American Express and Deutsche Bank in 2003. In 1990, he appeared in a television commercial for Canon Inc., promoting the Canon EOS Rebel camera. Between 1999 and 2000, he signed a multimillion-dollar, multiyear endorsement deal with Schick and became the worldwide spokesman for the company. Agassi signed a multiyear contract with Twinlab and promoted the company's nutritional supplements. In mid-2003, he was named the spokesman of Aramis Life, a fragrance by Aramis, and signed a five-year deal with the company. In March 2004, he signed a ten-year agreement worth $1.5 million a year with 24 Hour Fitness, which will open five Andre Agassi fitnes" +194,"Agassi's childhood friend and former business manager, Perry Rogers, sued Graf for $50,000 in management fees he claimed that she owed him.Alliance Sports Management v. Stephanie Graf Las Vegas Sun. Retrieved October 23, 2009. Ex-manager for Agassi sues Graf Las Vegas Review-Journal December 7, 2008. Retrieved October 23, 2009. ===Autobiography=== Agassi's autobiography, Open: An Autobiography, (written with assistance from J. R. Moehringer Agassi Basks in His Own Spotlight by Janet Malin New York Times November 8, 2009 . Retrieved December 11, 2009.), was published in November 2009. In it, Agassi talks about his childhood and his unconventional Armenian father, who came to the United States from Iran, where he was a professional boxer. Overly demanding and emotionally abusive to the whole family, his father groomed young Agassi for tennis greatness by building a tennis court in their backyard and sending Agassi to tennis boarding school under the supervision of Nick Bollettieri, who l" +195,"played Stands alone Time span Other selected records Players matched 1990–2003 6 Miami Masters titles Novak Djokovic 2001–2003 3 consecutive Miami Masters titles Novak Djokovic 1990–2003 8 Miami Masters finals Stands alone 1988–2005 61 match wins at Miami Masters Stands alone 2001–2004 19 consecutive wins at Miami Masters Stands alone 1990–1999 5 Washington Open titles Stands alone 1993–2002 4 Los Angeles Open titles ==Professional awards== * ITF World Champion: 1999. * ATP Player of the Year: 1999. * ATP Most Improved Player: 1988, 1998 ==Video== * Wimbledon 2000 Semi-final – Agassi vs. Rafter (2003) Starring: Andre Agassi, Patrick Rafter; Standing Room Only, DVD Release Date: August 16, 2005, Run Time: 213 minutes, . * Charlie Rose with Andre Agassi (May 7, 2001) Charlie Rose, Inc., DVD Release Date: August 15, 2006, Run Time: 57 minutes. * Wimbledon: The Record Breakers (2005) Starring: Andre Agassi, Boris Becker; Standing Room Only, DVD Release Date: August 16, 2005, Run Time: 52 m" +196,"The Austroasiatic languages ( ) are a large language family spoken throughout Mainland Southeast Asia, South Asia and East Asia. These languages are natively spoken by the majority of the population in Vietnam and Cambodia, and by minority populations scattered throughout parts of Thailand, Laos, India, Myanmar, Malaysia, Bangladesh, Nepal, and southern China. Approximately 117 million people speak an Austroasiatic language, of which more than two-thirds are Vietnamese speakers. Of the Austroasiatic languages, only Vietnamese, Khmer, and Mon have lengthy, established presences in the historical record. Only two are presently considered to be the national languages of sovereign states: Vietnamese in Vietnam, and Khmer in Cambodia. The Mon language is a recognized indigenous language in Myanmar and Thailand, while the Wa language is a recognized national language in the de facto autonomous Wa State within Myanmar. Santali is one of the 22 scheduled languages of India. The remainder of th" +197,"Northern MK) ***** Northern Mon–Khmer ****** Palaungic ****** Khmuic ***** Central Mon–Khmer ****** Khmer dialects ****** Pearic ****** Asli-Bahnaric ******* Aslian ******* Mon–Bahnaric ******** Monic ******** Katu–Bahnaric ********* Katuic ********* Bahnaric === Diffloth (2005) === Diffloth compares reconstructions of various clades, and attempts to classify them based on shared innovations, though like other classifications the evidence has not been published. As a schematic, we have: Or in more detail, * Austro‑Asiatic ** Munda languages (India) *** Koraput: 7 languages *** Core Munda languages **** Kharian–Juang: 2 languages **** North Munda languages ***** Korku ***** Kherwarian: 12 languages ** Khasi–Khmuic languages (Northern Mon–Khmer) *** Khasian: 3 languages of north eastern India and adjacent region of Bangladesh *** Palaungo-Khmuic languages **** Khmuic: 13 languages of Laos and Thailand **** Palaungo-Pakanic languages ***** Pakanic or Palyu: 4 or 5 languages of southern Ch" +198,"during the Iron Age. Paul Sidwell (2018)Sidwell, Paul. 2018. Austroasiatic deep chronology and the problem of cultural lexicon. Paper presented at the 28th Annual Meeting of the Southeast Asian Linguistics Society, held 17–19 May 2018 in Kaohsiung, Taiwan. considers the Austroasiatic language family to have rapidly diversified around 4,000 years B.P. during the arrival of rice agriculture in Indochina, but notes that the origin of Proto-Austroasiatic itself is older than that date. The lexicon of Proto-Austroasiatic can be divided into an early and late stratum. The early stratum consists of basic lexicon including body parts, animal names, natural features, and pronouns, while the names of cultural items (agriculture terms and words for cultural artifacts, which are reconstructible in Proto-Austroasiatic) form part of the later stratum. Roger Blench (2017)Blench, Roger. 2017. Waterworld: lexical evidence for aquatic subsistence strategies in Austroasiatic . Presented at ICAAL 7, Kiel," +199,"1992. The aberrancy of the Jiamao dialect of Hlai: speculation on its origins and history . In Ratliff, Martha S. and Schiller, E. (eds.), Papers from the First Annual Meeting of the Southeast Asian Linguistics Society, 417–433. Arizona State University, Program for Southeast Asian Studies. Jiamao is known for its highly aberrant vocabulary in relation to other Hlai languages. * Kerinci: van Reijn (1974)van Reijn, E. O. (1974). Some Remarks on the Dialects of North Kerintji: A link with Mon-Khmer Languages. Journal of the Malaysian Branch of the Royal Asiatic Society, 31, 2: 130–138. . notes that Kerinci, a Malayic language of central Sumatra, shares many phonological similarities with Austroasiatic languages, such as sesquisyllabic word structure and vowel inventory. John Peterson (2017)Peterson, John (2017). The prehistorical spread of Austro-Asiatic in South Asia . Presented at ICAAL 7, Kiel, Germany. suggests that pre-Munda (early languages related to Proto-Munda) languages may hav" +200,"in Laurent Sagart, Roger Blench and Alicia Sanchez-Mazas, eds. The Peopling of East Asia: Putting Together Archaeology, Linguistics and Genetics. 77–80. London: Routledge Curzon. * Filbeck, D. (1978). T'in: a historical study. Pacific linguistics, no. 49. Canberra: Dept. of Linguistics, Research School of Pacific Studies, Australian National University. * Hemeling, K. (1907). Die Nanking Kuanhua. (German language) * Jenny, Mathias and Paul Sidwell, eds (2015). The Handbook of Austroasiatic Languages . Leiden: Brill. * Peck, B. M., Comp. (1988). An Enumerative Bibliography of South Asian Language Dictionaries. * Peiros, Ilia. 1998. Comparative Linguistics in Southeast Asia. Pacific Linguistics Series C, No. 142. Canberra: Australian National University. * Shorto, Harry L. edited by Sidwell, Paul, Cooper, Doug and Bauer, Christian (2006). A Mon–Khmer comparative dictionary . Canberra: Australian National University. Pacific Linguistics. * Shorto, H. L. Bibliographies of Mon–Khmer and Tai" +201,"The Afroasiatic languages (or Afro-Asiatic, sometimes Afrasian), also known as Hamito-Semitic or Semito-Hamitic, are a language family (or phylum ) of about 400 languages spoken predominantly in West Asia, North Africa, the Horn of Africa, and parts of the Sahara and Sahel. Over 500 million people are native speakers of an Afroasiatic language, constituting the fourth-largest language family after Indo-European, Sino-Tibetan, and Niger–Congo. Most linguists divide the family into six branches: Berber, Chadic, Cushitic, Egyptian, Semitic, and Omotic. The vast majority of Afroasiatic languages are considered indigenous to the African continent, including all those not belonging to the Semitic branch. Arabic, if counted as a single language, is by far the most widely spoken within the family, with around 300 million native speakers concentrated primarily in the Middle East and North Africa. Other major Afroasiatic languages include the Cushitic Oromo language with 45 million native speake" +202,"Afrasian () was proposed by Igor Diakonoff in 1980. At present it predominantly sees use among Russian scholars. The names Lisramic—based on the Afroasiastic root *lis- ( tongue ) and the Egyptian word rmṯ ( person )—and Erythraean—referring to the core area around which the languages are spoken, the Red Sea—have also been proposed. ==Distribution and branches== Scholars generally consider Afroasiatic to have between five and eight branches. The five that are universally agreed upon are Berber (also called Libyco-Berber ), Chadic, Cushitic, Egyptian, and Semitic. Most specialists consider the Omotic languages to constitute a sixth branch. Due to the presumed distance of relationship between the various branches, many scholars prefer to refer to Afroasiatic as a linguistic phylum rather than a language family . G.W. Tsereteli goes even further and outright doubts that the Afro-Asiatic languages are a genetic language family altogether, but are rather a sprachbund. However, this is not t" +203,"Semitic is the largest branch of Afroasiatic by number of current speakers. Most authorities divide Semitic into two branches: East Semitic, which includes the extinct Akkadian language, and West Semitic, which includes Arabic, Aramaic, the Canaanite languages (including Hebrew), as well as the Ethiopian Semitic languages such as Ge'ez and Amharic. The classification within West Semitic remains contested. The only group with an African origin is Ethiopian Semitic. The oldest written attestations of Semitic languages come from Mesopotamia, Northern Syria, and Egypt and date as early as c. 3000 BCE. ===Other proposed branches=== There are also other proposed branches, but none has so far convinced a majority of scholars: * Linguist H. Fleming proposed that the near-extinct Ongota language is a separate branch of Afroasiatic; however, this is only one of several competing theories. About half of current scholarly hypotheses on Ongota's origins align it with Afroasiatic in some way. * Robe" +204,"scholars refused to admit that the largely unwritten, Negroid Chadic languages were in the same family as the Caucasian ancient civilizations of the Egyptians and Semites. An important development in the history of Afroasiatic scholarship – and the history of African linguistics – was the creation of the Hamitic theory or Hamitic hypothesis by Lepsius, fellow Egyptologist Christian Bunsen, and linguist Christian Bleek. This theory connected the Hamites , the originators of Hamitic languages, with (supposedly culturally superior) Caucasians , who were assumed to have migrated into Africa and intermixed with indigenous Negroid Africans in ancient times. The Hamitic theory would serve as the basis for Carl Meinhof's highly influential classification of African languages in his 1912 book . On one hand, the Hamitic classification was justified partially based on linguistic features: for example, Meinhof split the presently-understood Chadic family into Hamito-Chadic , and an unrelated non-H" +205,"linguistic macrofamilies (such as Indo-European, Bantu, and Austro-Asiatic) can be associated with the development of agriculture; they argue that there is clear archaeological support for farming spreading from the Levant into Africa via the Nile valley. ==Phonological characteristics== Afroasiatic languages share a number of phonetic and phonological features. ===Syllable structure=== Egyptian, Cushitic, Berber, Omotic, and most languages in the Semitic branch all require a syllable to begin with a consonant (with the exception of some grammatical prefixes). Igor Diakonoff argues that this constraint goes back to Proto-Afroasiatic. Some Chadic languages allow a syllable to begin with a vowel, however in many Chadic languages verbs must begin with a consonant. In Cushitic and Chadic languages, a glottal stop or glottal fricative may be inserted to prevent a word from beginning with a vowel. Typically, syllables only begin with a single consonant. Diakonoff argues that proto-Afroasiati" +206,"in most Cushitic languages this is not the case. Some scholars postulate that Proto-Afroasiatic may have had tone, while others believe it arose later from a pitch accent. Examples of tones marking lexical and morphological changes in some AA languages, after Language Examples Examples Examples Somali (Cushitic) díbi bull, absolutive case dibi bull, nominative case dibí bull, genitive case Somali (Cushitic) ínan, boy inán girl inán girl Bench (Omotic) k'áyts' work! do it! (active imperative) k'àyts' be done! (passive imperative) k'àyts' be done! (passive imperative) Hausa (Chadic) màatáa woman, wife máatáa women, wives máatáa women, wives Hausa (Chadic) dáfàa to cook (infinitive) dàfáa cook! (imperative) dàfáa cook! (imperative) ==Similarities in grammar, syntax, and morphology== At present, there is no generally accepted reconstruction of Proto-Afroasiatic grammar, syntax, or morphology, nor one for any of the sub-branches besides Egyptian. This means that it is difficult to know whic" +207,"there is no evidence for cases in Egyptian. A common pattern in AA languages with case is for the nominative to be marked by -u or -i, and the accusative to be marked by -a. However, the number and types of cases varies across AA and also within the individual branches. Some languages in AA have a marked nominative alignment, a feature which may date back to Proto-Afroasiatic. Zygmont Frajzyngier states that a general characteristic of case marking in AA languages is that it tends to mark roles such as genitive, dative, locative, etc. rather than the subject and object. Subject-Object case marking in some AA branches, using data in , and Case Oromo (Cushitic) Oromo (Cushitic) Berber Berber Akkadian (Semitic) Akkadian (Semitic) Wolaitta (Omotic) Wolaitta (Omotic) Case Masculine Feminine Masculine Feminine Masculine Feminine Masculine Feminine Nominative/bound nam-(n)i boy intal-t-i girl u-frux boy t-frux-t girl šarr-u-m king šarr-at-u-m queen keett-i house macci-yo woman Accusative/abso" +208,"Plural 1 1 ni-prus ni-parras n-ăknəf n-əkănnăf n-i:-dbíl n-i-dbíl n-e:-dbíl Plural 2 m ta-prus-a: a-parras t-ăknəf-ăm t-əkănnăf-ăm t-i:-dbíl-na t-i-dbíl-na t-e:-dbíl-na Plural 2 f ta-prus-a: ta-parras t-ăknəf-măt t-əkănnăf-măt t-i:-dbíl-na t-i-dbíl-na t-e:-dbíl-na Plural 3 m i-prus-u: ta-parras-i: ăknəf-ăn əkănnăf-ăn ʔ-i:-dbíl ʔ-i-dbíl ʔ-e:-dbíl-na Plural 3 f i-prus-a: i-parras ăknəf-năt əkănnăf-năt ʔ-i:-dbíl ʔ-i-dbíl ʔ-e:-dbíl-na ==== Suffix conjugation ==== Some AA branches have what is called a suffix conjugation , formed by adding pronominal suffixes to indicate person, gender, and number to a verbal adjective. In Akkadian, Egyptian, Berber, and Cushitic this forms a stative conjugation , used to express the state or result of an action; the same endings as in Akkadian and Egyptian are also present in the West Semitic perfective verb form. In Akkadian and Egyptian, the suffixes appear to be reduced forms of the independent pronouns (see Pronouns); the obvious correspondence between" +209,"Egyptian East Semitic (Akkadian) West Semitic (Arabic) Berber (Tashelhiyt) 'I' (ind.) tá aní aní-ga ni: ndé jnk ana:ku ʔana nkki 'me, my' (dep.) -ná- -tá- -u: -ʔe na ní -j wj -i: -ya -i: -ni: -i 'we' (ind.) ìnno hinín anná-ga inná-ga mu: ána éné jnn ni:nu: naħnu nkkwni 'you' (masc. sing. ind.) né barú:k adí-ga kai kám nt-k at-ta ʔan-ta kiji 'you' (fem. sing. ind.) né batú:k adí-ga ke: kín nt-ṯ at-ti ʔan-ti kmmi (f) 'you' (masc. sing., dep.) -né- -ú:k(a) ku ka ká -k -ka -ka -k 'you' (fem. sing., dep.) -né- -ú:k(i) ku ki kí -ṯ -ki -ki -m 'you' (plural, dep.) -nitì- -ú:kna idin ku ká(n) -ṯn -kunu (m) -kina (f) -kum (m) -kunna (f) -un (m) -un-t (f) 'he' (ind.) bár barú:s isá-ga ši: ár nt-f šu – ntta (m) 'she' (ind.) bár batú:s ijá-ga ita tír nt-s ši hiya ntta-t 'he' (dep.) -bá- -ūs – ši à -f sw -šu -hu -s 'she' (dep.) -bá- -ūs – ta dì -s sy -ša -ha: -s ===Numerals=== Unlike in the Indo-European or Austronesian language families, numerals in AA languages cannot be traced to a proto-system." +210,"Akkadian liša:nu 'tongue' Kabyle iləs 'tongue' to die *maaw- *mawut- – Rendille amut 'to die, to be ill' Hausa mutu 'to die', Mubi ma:t 'to die' mwt 'to die' Hebrew mwt, 'to die' Ge'ez mo:ta 'to die' Kabyle ammat 'to die' to fly, to soar *pîr- *pir- (cf. Yemsa fill- 'to jump'; Dime far 'to jump') Beja fir 'to fly' Hausa fi:ra 'to soar'; Mafa parr, perr 'bird's flight' pꜣ 'to fly'; prj 'to soar, rise' Ugaritic pr 'to flee'; Arabic frr 'to flee' Ahogar fərə-t 'to fly' name *sǔm / *sǐm- *süm- – – Hausa su:na: 'name'; Sura sun 'name'; Ga'anda ɬim 'name' – Akkadian šumu 'name' – to sour *s'ăm- – Mocha č'àm- 'to be bitter' PEC *cam- 'to rot' *s'am 'sour'; Hausa (t)sʼáː.mí 'sour' smj 'curds' Arabic sumūț 'to begin to turn sour' – to spit *tuf- *tuf- – Beja tuf 'to spit'; Kemant təff y- 'to spit'; Somali tuf 'to spit' Hausa tu:fa 'to spit' tf 'to spit' Aramaic tpp 'to spit'; Arabic tff 'to spit' – to rend, tear *zaaʕ- – Gamo zaʔ 'to rend, split' Dahalo ḏaaʕ- 'to rend, to tear (of an animal tea" +211,"Andorra, officially the Principality of Andorra, is a sovereign landlocked country on the Iberian Peninsula, in the eastern Pyrenees in Western Europe, bordered by France to the north and Spain to the south. Believed to have been created by Charlemagne, Andorra was ruled by the count of Urgell until 988, when it was transferred to the Roman Catholic Diocese of Urgell. The present principality was formed by a charter in 1278. It is currently headed by two co-princes: the Bishop of Urgell in Catalonia, Spain, and the president of France. Its capital and largest city is Andorra la Vella. Andorra is the sixth-smallest state in Europe, with an area of and a population of approximately . The Andorran people are a Romance ethnic group closely related to Catalans. Andorra is the world's 16th-smallest country by land and 11th-smallest by population. Its capital, Andorra la Vella, is the highest capital city in Europe, at an elevation of above sea level. The official language is Catalan, but Spa" +212,"was guaranteed by the son of Charlemagne, Louis the Pious, writing the Carta de Poblament or a local municipal charter . In 988, Count Borrell II of Urgell gave the Andorran valleys to the Diocese of Urgell in exchange for land in Cerdanya. Since then, the bishop of Urgell, based in Seu d'Urgell, has been co-prince of Andorra. The first document that mentions Andorra as a territory is the Acta de Consagració i Dotació de la Catedral de la Seu d'Urgell (Deed of Consecration and Endowment of the Cathedral of La Seu d'Urgell). The document, dated 839, depicts the six old parishes of the Andorran valleys that made up the country's administrative division. ===Middle Ages: The Paréages and the founding of the Co-Principality=== Before 1095, Andorra had no military protection, and the bishop of Urgell, who knew that the count of Urgell wanted to reclaim the Andorran valleys, asked the Lord of Caboet for help and protection. In 1095, the lord of Caboet and the bishop of Urgell signed under oat" +213,"laws were later kept. In both the Reapers' War and the War of the Spanish Succession, the Andorran people (while professing to be a neutral country) supported the Catalans who saw their rights reduced in 1716. The reaction was the promotion of Catalan writings in Andorra, with cultural works such as the Book of Privileges (Llibre de Privilegis de 1674), Manual Digest (1748) by Antoni Fiter i Rossell or the Polità andorrà (1763) by Antoni Puig. ===19th century: the New Reform and the Andorran Question=== After the French Revolution, Napoleon I reestablished the Co-Principate in 1809 and removed the French medieval title. In 1812–1813, the First French Empire annexed Catalonia during the Peninsular War () and divided the region into four départements, with Andorra as a part of the district of Puigcerdà. In 1814, an imperial decree reestablished the independence and economy of Andorra. During this period, Andorra's late medieval institutions and rural culture remained largely unchanged. I" +214,"in such places as the Hotel Mirador or the Casino Hotel, as a meeting place for Free French forces and a route for escorting crashed Allied pilots out of Europe. The network was maintained after the war, when film societies were formed, where movies, music and books censored in Franco's Spain were imported, becoming an anti-censorship attraction for the Catalan or foreign public even within Andorra. Andorran Group (Agrupament Andorrà), an anti-fascist organization linked to the Occitanie's French Resistance, accused the French representative (veguer) of collaboration with Nazism. The Andorran opening to the capitalist economy resulted in two axes: mass tourism and the country's tax exemption. The first steps toward the capitalist boom date from the 1930s, with the construction of FHASA and the creation of professional banking with Banc Agrícol (1930) and Crèdit Andorrà (1949), later with Banca Mora (1952), Banca Cassany (1958) and SOBANCA (1960). Shortly after, activities such as skiin" +215,"submit the proposed budget for parliamentary approval at least two months before the previous budget expires. If the budget is not approved by the first day of the next year, the previous budget is extended until a new one is approved. Once any bill is approved, the Syndic General is responsible for presenting it to the co-princes so that they may sign and enact it. thumb|The centre of government in Andorra la Vella If the prime minister is not satisfied with the council, he may request that the co-princes dissolve the council and order new elections. In turn, the councillors have the power to remove the prime minister from office. After a motion of censure is approved by at least one-fifth of the councillors, the council will vote and if it receives the absolute majority of votes, the prime minister is removed. ==Law and criminal justice== The judiciary is composed of the Magistrates Court, the Criminal Law Court, the High Court of Andorra, and the Constitutional Court. The High Court" +216,"police dogs, mountain rescue, and a bomb disposal team. ====GIPA==== The ' (GIPA) is a small special forces squad trained in counter-terrorism, and hostage recovery tasks. Although it is the closest in style to an active military force, it is part of the Police Corps, and not the army. As terrorist and hostage situations are a rare threat to the country, the GIPA is commonly assigned to prisoner escort duties, and at other times to routine policing. ===Fire brigade=== The Andorran Fire Brigade, with headquarters at Santa Coloma, operates from four modern fire stations, and has a staff of around 120 firefighters. The service is equipped with 16 heavy appliances (fire tenders, turntable ladders, and specialist four-wheel drive vehicles), four light support vehicles (cars and vans) and four ambulances. Historically, the families of the six ancient parishes of Andorra maintained local arrangements to assist each other in fighting fires. The first fire pump purchased by the government was a" +217,"had one of the world's lowest unemployment rates. In 2023 it stood at 1.5%. On 31 May 2013, it was announced that Andorra intended to legislate for the introduction of an income tax by the end of June, against a background of increasing dissatisfaction with the existence of tax havens among EU members. The announcement was made following a meeting in Paris between the Prime Minister Antoni Martí and the French President and Prince of Andorra François Hollande. Hollande welcomed the move as part of a process of Andorra bringing its taxation in line with international standards . By the mid-2010s, the financial system comprised five banking groups, one specialised credit entity, eight investment undertaking management entities, three asset management companies, and 29 insurance companies, 14 of which are branches of foreign insurance companies authorised to operate in the principality. The last mergers between banks took place in 2022, bringing the Andorran financial sector to currently" +218,"southeast of Andorra, to Toulouse and on to Paris by the French high-speed trains. This line is operated by the SNCF. Latour-de-Carol has a scenic trainline to Villefranche-de-Conflent, as well as the SNCF's gauge line connecting to Perpignan, and the Renfe's -gauge line to Barcelona. There are also direct Intercités de Nuit trains between L'Hospitalet-près-l'Andorre and Paris on certain dates. ==Media and telecommunications== In Andorra, mobile and fixed telephone and internet services are operated exclusively by the Andorran national telecommunications company, SOM, also known as Andorra Telecom (STA). The same company also manages the technical infrastructure for national broadcasting of digital television and radio. In 2010 Andorra became the first country to provide a direct optical fiber link to all homes (FTTH) and businesses. The first commercial radio station to broadcast was Radio Andorra, which was active from 1939 to 1981. On 12 October 1989, the General Council established" +219,"league system. Rugby is a traditional sport in Andorra, mainly influenced by its popularity in southern France. The Andorra national rugby union team, nicknamed Els Isards, plays on the international stage in rugby union and rugby sevens. VPC Andorra XV is a rugby team based in Andorra la Vella, which actually plays in the French championship. Basketball's popularity has increased in the country since the 1990s, when the Andorran team BC Andorra played in the top league of Spain (Liga ACB). After 18 years the club returned to the top league in 2014. Other sports practised in Andorra include cycling, volleyball, judo, Australian Rules football, handball, swimming, gymnastics, tennis, and motorsports. In 2012, Andorra raised its first national cricket team and played a home match against the Dutch Fellowship of Fairly Odd Places Cricket Club, the first match played in the history of Andorra at an altitude of . Andorra first participated at the Olympic Games in 1976. The country has appea" +220,"In mathematics and statistics, the arithmetic mean ( ), arithmetic average, or just the mean or average (when the context is clear) is the sum of a collection of numbers divided by the count of numbers in the collection. The collection is often a set of results from an experiment, an observational study, or a survey. The term arithmetic mean is preferred in some mathematics and statistics contexts because it helps distinguish it from other types of means, such as geometric and harmonic. In addition to mathematics and statistics, the arithmetic mean is frequently used in economics, anthropology, history, and almost every academic field to some extent. For example, per capita income is the arithmetic average income of a nation's population. While the arithmetic mean is often used to report central tendencies, it is not a robust statistic: it is greatly influenced by outliers (values much larger or smaller than most others). For skewed distributions, such as the distribution of income for" +221,"distributions=== If a numerical property, and any sample of data from it, can take on any value from a continuous range instead of, for example, just integers, then the probability of a number falling into some range of possible values can be described by integrating a continuous probability distribution across this range, even when the naive probability for a sample number taking one certain value from infinitely many is zero. In this context, the analog of a weighted average, in which there are infinitely many possibilities for the precise value of the variable in each range, is called the mean of the probability distribution. The most widely encountered probability distribution is called the normal distribution; it has the property that all measures of its central tendency, including not just the mean but also the median mentioned above and the mode (the three Ms), are equal. This equality does not hold for other probability distributions, as illustrated for the log-normal distribut" +222,"The American Football Conference (AFC) is one of the two conferences of the National Football League (NFL), the highest professional level of American football in the United States. The AFC and its counterpart, the National Football Conference (NFC), each contain 16 teams with 4 divisions. Both conferences were created as part of the 1970 merger between the National Football League, and the American Football League (AFL). All ten of the AFL teams, and three NFL teams, became members of the new AFC, with the remaining thirteen NFL teams forming the NFC. A series of league expansions and division realignments have occurred since the merger, thus making the current total of 16 teams in each conference. The current AFC champions are the Kansas City Chiefs, who defeated the Baltimore Ravens in the 2023 season's AFC Championship Game for their fourth conference championship and went on to win Super Bowl LVIII against the San Francisco 49ers. ==Teams== Like the NFC, the conference has 16 team" +223,"1982, back to Oakland in 1995, and then to Las Vegas in 2020, while the San Diego Chargers returned to Los Angeles in 2017 after 56 years in San Diego. The Houston Oilers moved to Tennessee in 1997, where they were renamed the Tennessee Oilers. The team would change its name again, two years later, to the Tennessee Titans. The NFL would again expand in 2002, adding the Houston Texans to the AFC. With the exception of the aforementioned relocations since that time, the divisional setup has remained static ever since. Between 1995 and 2022, the AFC has sent only 9 of its 16 teams to the Super Bowl: New England Patriots (10 times), Pittsburgh Steelers (4 times), Denver Broncos (4 times), Baltimore Ravens (2 times), Indianapolis Colts (2 times), Kansas City Chiefs (3 times), Cincinnati Bengals (1 time), Las Vegas Raiders (1 time), Tennessee Titans (1 time). By contrast, the NFC has sent 13 of the 16 NFC teams during that same time frame with only the Detroit Lions, Minnesota Vikings, and W" +224,"Animal Farm is a satirical allegorical novella, in the form of a beast fable, by George Orwell, first published in England on 17 August 1945. It tells the story of a group of anthropomorphic farm animals who rebel against their human farmer, hoping to create a society where the animals can be equal, free, and happy. Ultimately, the rebellion is betrayed, and under the dictatorship of a pig named Napoleon, the farm ends up in a state far worse than before. According to Orwell, Animal Farm reflects events leading up to the Russian Revolution of 1917 and then on into the Stalinist era of the Soviet Union, a period when Russia lived under the communist ideology of Joseph Stalin. Orwell, a democratic socialist, was a critic of Stalin and hostile to Moscow-directed Stalinism, an attitude that was critically shaped by his experiences during the Barcelona May Days conflicts between the POUM and Stalinist forces, during the Spanish Civil War. In a letter to Yvonne Davet, Orwell described Animal" +225,"He is taken away in a knacker's van and a donkey called Benjamin alerts the animals of this, but Squealer quickly waves off their alarm by persuading the animals that the van had been purchased from the knacker by an animal hospital and that the previous owner's signboard had not been repainted. Squealer subsequently reports Boxer's death and honours him with a festival the following day. In truth, Napoleon had engineered the sale of Boxer to the knacker, allowing him and his inner circle to acquire money to buy whisky for themselves. Years pass, the windmill is rebuilt and another windmill is constructed, which makes the farm a good amount of income. However, the ideals that Snowball discussed, including stalls with electric lighting, heating, and running water, are forgotten, with Napoleon advocating that the happiest animals live simple lives. Snowball has been forgotten, alongside Boxer, with the exception of the few who knew him . Many of the animals who participated in the rebell" +226,"killing many animals and destroying the windmill. The brief alliance and subsequent invasion may allude to the Molotov–Ribbentrop Pact and Operation Barbarossa. * Mr. Pilkington – The easy-going but crafty and well-to-do owner of Foxwood Farm, a large neighbouring farm overgrown with weeds. Pilkington is wealthier than Frederick and owns more land, but his farm needs care as opposed to Frederick's smaller but more efficiently run farm. Although on bad terms with Frederick, Pilkington is also concerned about the animal revolution that deposed Jones and is worried that this could also happen to him. * Mr. Whymper – A man hired by Napoleon to act as the liaison between Animal Farm and human society. At first, he acquires necessities that cannot be produced on the farm, such as dog biscuits and paraffin wax, but later he procures luxuries like alcohol for the pigs. ===Equines=== * Boxer – A loyal, kind, dedicated, extremely strong, hard-working, and respectable cart-shire horse, although q" +227,"the only time she is recorded as having participated in an election, she is found to have actually voted on both sides . * The ducks – Unnamed. * The roosters – One arranges to wake Boxer early, and a black one acts as a trumpeter for Napoleon. * The geese – Unnamed. One gander commits suicide by eating nightshade berries. * The rats – Unnamed. Classed among the wild animals, unsuccessful attempts were made to civilise them and teach them the principles of Animalism. == Genre and style == George Orwell's Animal Farm is an example of a political satire and an allegory that was intended to have a wider application , according to Orwell himself, in terms of its relevance. Stylistically, the work shares many similarities with some of Orwell's other works, most notably Nineteen Eighty-Four, as both have been considered works of Swiftian satire. Furthermore, these two prominent works seem to suggest Orwell's bleak view of the future for humanity; he seems to stress the potential/current thre" +228,"was thought to be especially offensive. It may reasonably be assumed that the important official was a man named Peter Smollett, who was later unmasked as a Soviet agent. Orwell was suspicious of Smollett/Smolka, and he would be one of the names Orwell included in his list of Crypto-Communists and Fellow-Travellers sent to the Information Research Department in 1949. The publisher wrote to Orwell, saying: Frederic Warburg also faced pressures against publication, even from people in his own office and from his wife Pamela, who felt that it was not the moment for ingratitude towards Stalin and the Red Army, which had played a major part in defeating Adolf Hitler. A Russian translation was printed in the paper Posev, and in permitting a Russian translation of Animal Farm, Orwell refused in advance all royalties. A translation in Ukrainian, which was produced in Germany, was confiscated in large part by the American wartime authorities and handed over to the Soviet repatriation commission" +229,"faced similar forms of resistance in other countries. The ALA also mentions the way that the book was prevented from being featured at the International Book Fair in Moscow, Russia, in 1977 and banned from schools in the United Arab Emirates for references to practices or actions that defy Arab or Islamic beliefs, such as pigs or alcohol. In the same manner, Animal Farm has also faced relatively recent issues in China. In 2018, the government decided to censor all online posts about or referring to Animal Farm. However, the book itself, as of 2019, remains sold in stores. Amy Hawkins and Jeffrey Wasserstrom of The Atlantic stated in 2019 that the book is widely available in mainland China for several reasons: censors believe the general public is unlikely to read a highbrow book, because the elites who do read books feel connected to the ruling party anyway, and because the Communist Party sees being too aggressive in blocking cultural products as a liability. The authors stated: It wa" +230,"Orwell's telescoping of Russian history from 1917 to 1943, including the wave of rebelliousness that ran through the countryside after the Rebellion, which stands for the abortive revolutions in Hungary and Germany (Ch. IV); the conflict between Napoleon and Snowball (Ch. V), parallelling the two rival and quasi-Messianic beliefs that seemed pitted against one another: Trotskyism, with its faith in the revolutionary vocation of the proletariat of the West; and Stalinism with its glorification of Russia's socialist destiny ; Napoleon's dealings with Whymper and the Willingdon markets (Ch. VI), paralleling the Treaty of Rapallo; and Frederick's forged bank notes, parallelling the Molotov–Ribbentrop Pact of August 1939, after which Frederick attacks Animal Farm without warning and destroys the windmill. The book's close, with the pigs and men in a kind of rapprochement, reflected Orwell's view of the 1943 Tehran Conference that seemed to display the establishment of the best possible rela" +231,"Category:British novellas Category:British novels adapted into films Category:British novels adapted into plays Category:British novels adapted into television shows Category:British political novels Category:British satirical novels Category:Novels about cats Category:Cattle in literature Category:Censored books Category:Novels about dogs Category:Dystopian novels Category:English novels Category:Hugo Award for Best Novella winning works Category:Novels about animals Category:Novels about propaganda Category:Novels about revolutionaries Category:Novels about totalitarianism Category:Novels adapted into comics Category:Novels adapted into radio programs Category:Novels by George Orwell Category:Novels set on farms Category:Novels about pigs Category:Novels set in East Sussex Category:Political literature Category:Roman à clef novels Category:Secker & Warburg books Category:Cultural depictions of Karl Marx Category:Cultural depictions of Vladimir Lenin Category:Cultural depictions of Jo" +232,"Amphibians are ectothermic, anamniotic, four-limbed vertebrate animals that constitute the class Amphibia. In its broadest sense, it is a paraphyletic group encompassing all tetrapods excluding the amniotes (tetrapods with an amniotic membrane, such as modern reptiles, birds and mammals). All extant (living) amphibians belong to the monophyletic subclass Lissamphibia, with three living orders: Anura (frogs and toads), Urodela (salamanders), and Gymnophiona (caecilians). Evolved to be mostly semiaquatic, amphibians have adapted to inhabit a wide variety of habitats, with most species living in freshwater, wetland or terrestrial ecosystems (such as riparian woodland, fossorial and even arboreal habitats). Their life cycle typically starts out as aquatic larvae with gills known as tadpoles, but some species have developed behavioural adaptations to bypass this. Young amphibians generally undergo metamorphosis from an aquatic larval form with gills to an air-breathing adult form with lungs" +233,"been suggested that salamanders arose separately from a temnospondyl-like ancestor, and even that caecilians are the sister group of the advanced reptiliomorph amphibians, and thus of amniotes. Although the fossils of several older proto-frogs with primitive characteristics are known, the oldest true frog , with hopping adaptations is Prosalirus bitis, from the Early Jurassic Kayenta Formation of Arizona. It is anatomically very similar to modern frogs. The oldest known caecilians are Funcusvermis gilmorei (from the Late Triassic) and Eocaecilia micropodia (from the Early Jurassic), both from Arizona. The earliest salamander is Beiyanerpeton jianpingensis from the Late Jurassic of northeastern China. Authorities disagree as to whether Salientia is a superorder that includes the order Anura, or whether Anura is a sub-order of the order Salientia. The Lissamphibia are traditionally divided into three orders, but an extinct salamander-like family, the Albanerpetontidae, is now considered" +234,"Molecular analysis suggests that the frog–salamander divergence took place considerably earlier than the palaeontological evidence indicates. One study suggested that the last common ancestor of all modern amphibians lived about 315 million years ago, and that stereospondyl temnospondyls are the closest relatives to the caecilians. However, most studies support a single monophyletic origin of all modern amphibians within the dissorophoid temnospondyls. As they evolved from lunged fish, amphibians had to make certain adaptations for living on land, including the need to develop new means of locomotion. In the water, the sideways thrusts of their tails had propelled them forward, but on land, quite different mechanisms were required. Their vertebral columns, limbs, limb girdles and musculature needed to be strong enough to raise them off the ground for locomotion and feeding. Terrestrial adults discarded their lateral line systems and adapted their sensory systems to receive stimuli via" +235,"have few derived features and are probably paraphyletic with regard to other frog lineages. The six families in the more evolutionarily advanced suborder Mesobatrachia are the fossorial Megophryidae, Pelobatidae, Pelodytidae, Scaphiopodidae and Rhinophrynidae and the obligatorily aquatic Pipidae. These have certain characteristics that are intermediate between the two other suborders. Neobatrachia is by far the largest suborder and includes the remaining families of modern frogs, including most common species. Approximately 96% of the over 5,000 extant species of frog are neobatrachians. === Caudata === The order Caudata (from the Latin cauda meaning tail ) consists of the salamanders—elongated, low-slung animals that mostly resemble lizards in form. This is a symplesiomorphic trait and they are no more closely related to lizards than they are to mammals. Salamanders lack claws, have scale-free skins, either smooth or covered with tubercles, and tails that are usually flattened from si" +236,"To compensate for their thin and delicate skin, amphibians have evolved mucous glands, principally on their heads, backs and tails. The secretions produced by these help keep the skin moist. In addition, most species of amphibian have granular glands that secrete distasteful or poisonous substances. Some amphibian toxins can be lethal to humans while others have little effect. The main poison-producing glands, the parotoids, produce the neurotoxin bufotoxin and are located behind the ears of toads, along the backs of frogs, behind the eyes of salamanders and on the upper surface of caecilians. The skin colour of amphibians is produced by three layers of pigment cells called chromatophores. These three cell layers consist of the melanophores (occupying the deepest layer), the guanophores (forming an intermediate layer and containing many granules, producing a blue-green colour) and the lipophores (yellow, the most superficial layer). The colour change displayed by many species is initia" +237,"sounds like mating calls are heard in this way, but low-frequency noises can be detected through another mechanism. There is a patch of specialized haircells, called papilla amphibiorum, in the inner ear capable of detecting deeper sounds. Another feature, unique to frogs and salamanders, is the columella-operculum complex adjoining the auditory capsule which is involved in the transmission of both airborne and seismic signals. The ears of salamanders and caecilians are less highly developed than those of frogs as they do not normally communicate with each other through the medium of sound. The eyes of tadpoles lack lids, but at metamorphosis, the cornea becomes more dome-shaped, the lens becomes flatter, and eyelids and associated glands and ducts develop. The adult eyes are an improvement on invertebrate eyes and were a first step in the development of more advanced vertebrate eyes. They allow colour vision and depth of focus. In the retinas are green rods, which are receptive to a w" +238,"are not yet reproductively active. In caecilians, fertilisation is internal, the male extruding an intromittent organ, the , and inserting it into the female cloaca. The paired Müllerian glands inside the male cloaca secrete a fluid which resembles that produced by mammalian prostate glands and which may transport and nourish the sperm. Fertilisation probably takes place in the oviduct. The majority of salamanders also engage in internal fertilisation. In most of these, the male deposits a spermatophore, a small packet of sperm on top of a gelatinous cone, on the substrate either on land or in the water. The female takes up the sperm packet by grasping it with the lips of the cloaca and pushing it into the vent. The spermatozoa move to the spermatheca in the roof of the cloaca where they remain until ovulation which may be many months later. Courtship rituals and methods of transfer of the spermatophore vary between species. In some, the spermatophore may be placed directly into the fe" +239,"singly, in cluster or in long strands. Sites for laying eggs include water, mud, burrows, debris and on plants or under logs or stones. The greenhouse frog (Eleutherodactylus planirostris) lays eggs in small groups in the soil where they develop in about two weeks directly into juvenile frogs without an intervening larval stage. The tungara frog (Physalaemus pustulosus) builds a floating nest from foam to protect its eggs. First a raft is built, then eggs are laid in the centre, and finally a foam cap is overlaid. The foam has anti-microbial properties. It contains no detergents but is created by whipping up proteins and lectins secreted by the female. === Larvae === The eggs of amphibians are typically laid in water and hatch into free-living larvae that complete their development in water and later transform into either aquatic or terrestrial adults. In many species of frog and in most lungless salamanders (Plethodontidae), direct development takes place, the larvae growing within th" +240,"including species of Necturus, Proteus and Amphiuma, and many examples of facultative ones, such as the northwestern salamander (Ambystoma gracile) and the tiger salamander (A. tigrinum) that adopt this strategy under appropriate environmental circumstances. Lungless salamanders in the family Plethodontidae are terrestrial and lay a small number of unpigmented eggs in a cluster among damp leaf litter. Each egg has a large yolk sac and the larva feeds on this while it develops inside the egg, emerging fully formed as a juvenile salamander. The female salamander often broods the eggs. In the genus Ensatinas, the female has been observed to coil around them and press her throat area against them, effectively massaging them with a mucous secretion. In newts and salamanders, metamorphosis is less dramatic than in frogs. This is because the larvae are already carnivorous and continue to feed as predators when they are adults so few changes are needed to their digestive systems. Their lungs a" +241,"up to eight weeks. He keeps them moist and when they are ready to hatch, he visits a pond or ditch and releases the tadpoles. The female gastric-brooding frog (Rheobatrachus spp.) reared larvae in her stomach after swallowing either the eggs or hatchlings; however, this stage was never observed before the species became extinct. The tadpoles secrete a hormone that inhibits digestion in the mother whilst they develop by consuming their very large yolk supply. The pouched frog (Assa darlingtoni) lays eggs on the ground. When they hatch, the male carries the tadpoles around in brood pouches on his hind legs. The aquatic Surinam toad (Pipa pipa) raises its young in pores on its back where they remain until metamorphosis. The granular poison frog (Oophaga granulifera) is typical of a number of tree frogs in the poison dart frog family Dendrobatidae. Its eggs are laid on the forest floor and when they hatch, the tadpoles are carried one by one on the back of an adult to a suitable water-fill" +242,"Most salamanders are considered voiceless, but the California giant salamander (Dicamptodon ensatus) has vocal cords and can produce a rattling or barking sound. Some species of salamander emit a quiet squeak or yelp if attacked. Frogs are much more vocal, especially during the breeding season when they use their voices to attract mates. The presence of a particular species in an area may be more easily discerned by its characteristic call than by a fleeting glimpse of the animal itself. In most species, the sound is produced by expelling air from the lungs over the vocal cords into an air sac or sacs in the throat or at the corner of the mouth. This may distend like a balloon and acts as a resonator, helping to transfer the sound to the atmosphere, or the water at times when the animal is submerged. The main vocalisation is the male's loud advertisement call which seeks to both encourage a female to approach and discourage other males from intruding on its territory. This call is modi" +243,"or neurological symptoms. Altogether, over 200 toxins have been isolated from the limited number of amphibian species that have been investigated. Poisonous species often use bright colouring to warn potential predators of their toxicity. These warning colours tend to be red or yellow combined with black, with the fire salamander (Salamandra salamandra) being an example. Once a predator has sampled one of these, it is likely to remember the colouration next time it encounters a similar animal. In some species, such as the fire-bellied toad (Bombina spp.), the warning colouration is on the belly and these animals adopt a defensive pose when attacked, exhibiting their bright colours to the predator. The frog Allobates zaparo is not poisonous, but mimics the appearance of other toxic species in its locality, a strategy that may deceive predators. Many amphibians are nocturnal and hide during the day, thereby avoiding diurnal predators that hunt by sight. Other amphibians use camouflage to" +244,"reptiles. The buildup of these inhibitory effects on motor performance, such as food consumption and other activities. === Conservation and protection strategies === The Amphibian Specialist Group of the IUCN is spearheading efforts to implement a comprehensive global strategy for amphibian conservation. Amphibian Ark is an organization that was formed to implement the ex-situ conservation recommendations of this plan, and they have been working with zoos and aquaria around the world, encouraging them to create assurance colonies of threatened amphibians. One such project is the Panama Amphibian Rescue and Conservation Project that built on existing conservation efforts in Panama to create a country-wide response to the threat of chytridiomycosis. Another measure would be to stop exploitation of frogs for human consumption. In the Middle East, a growing appetite for eating frog legs and the consequent gathering of them for food was already linked to an increase in mosquitoes and thus h" +245,"Alaska ( ) is a non-contiguous U.S. state on the northwest extremity of North America. It is in the Western United States region. The only other non-contiguous U.S. state is Hawaii. Alaska is also considered to be the northernmost, westernmost, and easternmost (the Aleutian Islands cross the 180th meridian into the eastern hemisphere) state in the United States. It borders the Canadian territory of Yukon and the province of British Columbia to the east. It shares a western maritime border, in the Bering Strait, with Russia's Chukotka Autonomous Okrug. The Chukchi and Beaufort Seas of the Arctic Ocean lie to the north, and the Pacific Ocean lies to the south. Technically, it is a semi-exclave of the U.S., and is the largest exclave in the world. Alaska is the largest U.S. state by area, comprising more total area than the following three largest states of Texas, California, and Montana combined, and is the sixth-largest subnational division in the world. It is the third-least populous a" +246,"Russian Navy aboard the St. Peter. After his crew returned to Russia with sea otter pelts judged to be the finest fur in the world, small associations of fur traders began to sail from the shores of Siberia toward the Aleutian Islands. The first permanent European settlement was founded in 1784. Between 1774 and 1800, Spain sent several expeditions to Alaska to assert its claim over the Pacific Northwest. In 1789, a Spanish settlement and fort were built in Nootka Sound. These expeditions gave names to places such as Valdez, Bucareli Sound, and Cordova. Later, the Russian-American Company carried out an expanded colonization program during the early-to-mid-19th century. Sitka, renamed New Archangel from 1804 to 1867, on Baranof Island in the Alexander Archipelago in what is now Southeast Alaska, became the capital of Russian America. It remained the capital after the colony was transferred to the United States. The Russians never fully colonized Alaska, and the colony was never very pr" +247,"Sound, Port Valdez suffered a massive underwater landslide, resulting in the deaths of 32 people between the collapse of the Valdez city harbor and docks, and inside the ship that was docked there at the time. Nearby, a tsunami destroyed the village of Chenega, killing 23 of the 68 people who lived there; survivors out-ran the wave, climbing to high ground. Post-quake tsunamis severely affected Whittier, Seward, Kodiak, and other Alaskan communities, as well as people and property in British Columbia, Washington, Oregon, and California. Tsunamis also caused damage in Hawaii and Japan. Evidence of motion directly related to the earthquake was also reported from Florida and Texas. Alaska had never experienced a major disaster in a highly populated area before and had very limited resources for dealing with the effects of such an event. In Anchorage, at the urging of geologist Lidia Selkregg, the City of Anchorage and the Alaska State Housing Authority appointed a team of 40 scientists, i" +248,"the only non-contiguous U.S. state on continental North America; about of Canadian territory consisting of British Columbia (in Canada) separates Alaska from Washington. It is technically part of the continental U.S., but is not usually included in the colloquial use of the term; Alaska is not part of the contiguous U.S., often called the Lower 48 . The capital city, Juneau, is situated on the mainland of the North American continent but is not connected by road to the rest of the North American highway system. The largest lake in Alaska is Lake Illiamna. The state is bordered by Canada's Yukon and British Columbia to the east (making it the only state to border only a Canadian territory); the Gulf of Alaska and the Pacific Ocean to the south and southwest; the Bering Sea, Bering Strait, and Chukchi Sea to the west; and the Arctic Ocean to the north. Alaska's territorial waters touch Russia's territorial waters in the Bering Strait, as the Russian Big Diomede Island and Alaskan Little" +249,". Of the remaining land area, the state of Alaska owns , its entitlement under the Alaska Statehood Act. A portion of that acreage is occasionally ceded to the organized boroughs presented above, under the statutory provisions pertaining to newly formed boroughs. Smaller portions are set aside for rural subdivisions and other homesteading-related opportunities. These are not very popular due to the often remote and roadless locations. The University of Alaska, as a land grant university, also owns substantial acreage which it manages independently. Another are owned by 12 regional, and scores of local, Native corporations created under the Alaska Native Claims Settlement Act (ANCSA) of 1971. Regional Native corporation Doyon, Limited often promotes itself as the largest private landowner in Alaska in advertisements and other communications. Provisions of ANCSA allowing the corporations' land holdings to be sold on the open market starting in 1991 were repealed before they could take ef" +250,"Farm Loop CDP 2,747 39 Seward City 2,717 40 Eielson AFB CDP 2,610 41 Cordova City 2,609 42 Ester CDP 2,416 43 Deltana CDP 2,359 44 Dillingham City 2,249 45 Fritz Creek CDP 2,248 46 North Pole City 2,243 47 Willow CDP 2,196 48 Ridgeway CDP 2,136 49 Bear Creek CDP 2,129 50 Wrangell City 2,127 ===Climate=== Alaska is the coldest state in the United States. The climate in the south and southeastern Alaska is a mid-latitude oceanic climate (Köppen climate classification: Cfb), and a subarctic oceanic climate (Köppen Cfc) in the northern parts, with cool summers and relatively mild winters. On an annual basis, the southeast is both the wettest and warmest part of Alaska with milder temperatures in the winter and high precipitation throughout the year. Juneau averages over of precipitation a year, and Ketchikan averages over . This is also the only region in Alaska in which the average daytime high temperature is above freezing during the winter months.The climate of Anchorage and south centr" +251,"and Japanese and Chinese Americans. The state was 66.7% white (64.1% non-Hispanic white), 14.8% American Indian and Alaska Native, 5.4% Asian, 3.3% black or African American, 1.0% Native Hawaiian and other Pacific Islander, 1.6% from some other race, and 7.3% from two or more races in 2010. Hispanics or Latin Americans of any race made up 5.5% of the population in 2010. , 50.7% of Alaska's population younger than one year of age belonged to minority groups (i.e., did not have two parents of non-Hispanic white ancestry). In 1960, the United States Census Bureau reported Alaska's population as 77.2% white, 3% black, and 18.8% American Indian and Alaska Native. In 2018, The top countries of origin for Alaska's immigrants were the Philippines, Mexico, Canada, Thailand and Korea. ===Languages=== According to the 2011 American Community Survey, 83.4% of people over the age of five spoke only English at home. About 3.5% spoke Spanish at home, 2.2% spoke another Indo-European language, about 4" +252,"estimated there were 400 Muslims in the state. The Islamic Community Center of Anchorage began efforts in the late 1990s to construct a mosque in Anchorage. They broke ground on a building in south Anchorage in 2010 and were nearing completion in late 2014. When completed, the mosque was the first in the state and one of the northernmost mosques in the world. There is also a Baháʼí center, and there were 690 adherents in 2020. Additionally, there were 469 adherents of Hinduism and Yoga altogether in 2020, and a small number of Buddhists were present. ==Economy== As of October 2022, Alaska had a total employment of 316,900. The number of employer establishments was 21,077. The 2018 gross state product was $55 billion, 48th in the U.S. Its per capita personal income for 2018 was $73,000, ranking 7th in the nation. According to a 2013 study by Phoenix Marketing International, Alaska had the fifth-largest number of millionaires per capita in the United States, with a ratio of 6.75 percent." +253,"or criminal convictions which fall under various disqualifying classifications or may subject the payment amount to civil garnishment. The Permanent Fund is often considered to be one of the leading examples of a basic income policy in the world. ===Cost of living=== The cost of goods in Alaska has long been higher than in the contiguous 48 states. Federal government employees, particularly United States Postal Service (USPS) workers and active-duty military members, receive a Cost of Living Allowance usually set at 25% of base pay because, while the cost of living has gone down, it is still one of the highest in the country. Rural Alaska suffers from extremely high prices for food and consumer goods compared to the rest of the country, due to the relatively limited transportation infrastructure. ===Agriculture and fishing=== thumb|right|upright|Halibut, both as a sport fish and commercially, is important to the state's economy. Due to the northern climate and short growing season, rel" +254,"and television=== The 1983 Disney movie Never Cry Wolf was at least partially shot in Alaska. The 1991 film White Fang, based on Jack London's 1906 novel and starring Ethan Hawke, was filmed in and around Haines. Steven Seagal's 1994 On Deadly Ground, starring Michael Caine, was filmed in part at the Worthington Glacier near Valdez. Many reality television shows are filmed in Alaska. In 2011, the Anchorage Daily News found ten set in the state. ===Sports=== The following is a list of sporting venues, events, and teams based in Alaska. ==== Anchorage ==== ===== Venues ===== * Alyeska Resort * Chugach State Park, a high alpine park. * Anchorage has many groomed cross-country skiing trails within the urban core. There are of maintained ski trails in the city, some of which reach downtown. * Mulcahy Stadium * Sullivan Arena * Alaska Airlines Center ===== Teams ===== * Anchorage Wolverines * Anchorage Bucs * Anchorage Glacier Pilots * Alaska-Anchorage Seawolves men's ice hockey * Rage City" +255,"state through Canada. The state capital, Juneau, is not accessible by road, with access only being through ferry or flight; this has spurred debate over decades about moving the capital to a city on the road system, or building a road connection from Haines. The western part of Alaska has no road system connecting the communities with the rest of Alaska. The Interstate Highways in Alaska consists of a total of . One unique feature of the Alaska Highway system is the Anton Anderson Memorial Tunnel, an active Alaska Railroad tunnel recently upgraded to provide a paved roadway link with the isolated community of Whittier on Prince William Sound to the Seward Highway about southeast of Anchorage at Portage. At , the tunnel was the longest road tunnel in North America until 2007.completion of the Interstate 93 tunnel as part of the Big Dig project in Boston, Massachusetts. The tunnel is the longest combination road and rail tunnel in North America. Southwest Alaska is largely coastal, spars" +256,"larger communities as well as to major Southeast and Alaska Peninsula communities. The bulk of remaining commercial flight offerings come from small regional commuter airlines such as Ravn Alaska, PenAir, and Frontier Flying Service. The smallest towns and villages must rely on scheduled or chartered bush flying services using general aviation aircraft such as the Cessna Caravan, the most popular aircraft in use in the state. Much of this service can be attributed to the Alaska bypass mail program which subsidizes bulk mail delivery to Alaskan rural communities. The program requires 70% of that subsidy to go to carriers who offer passenger service to the communities. Many communities have small air taxi services. These operations originated from the demand for customized transport to remote areas. Perhaps the most quintessentially Alaskan plane is the bush seaplane. The world's busiest seaplane base is Lake Hood, located next to Ted Stevens Anchorage International Airport, where flight" +257,"in 1994. Alaska's voter initiative making marijuana legal took effect on February 24, 2015, placing Alaska alongside Colorado and Washington, as well as Washington D.C., as the first three U.S. states where recreational marijuana is legal. The new law means people over 21 can consume small amounts of cannabis. The first legal marijuana store opened in Valdez in October 2016.Andrews, Laurel,Marijuana milestone: Alaska's first pot shop opens to the public in Valdez Alaska Dispatch News, October 29, 2016 ===Voter registration=== Party registration as of June 3, 2024 Party registration as of June 3, 2024 Party registration as of June 3, 2024 Party registration as of June 3, 2024 Party registration as of June 3, 2024 Party registration as of June 3, 2024 Party Party Total voters Percentage Unaffiliated 346,751 58.35% Republican 143,401 24.13% Democratic 73,598 12.38% Alaskan Independence 18,768 3.16% Minor parties 11,758 1.98% Total Total 594,276 100.00% ===Taxes=== To finance state governm" +258,"===Alaska state government=== * State of Alaska website * Alaska State Databases * Alaska Department of Natural Resources, Recorder's Office Category:Arctic Ocean Category:Former Russian colonies Category:States and territories established in 1959 Category:States of the United States Category:States of the West Coast of the United States Category:1959 establishments in the United States Category:Western United States Category:Northern America Category:Enclaves and exclaves Category:Russia–United States relations Category:Exclaves in the United States" +259,"Agriculture encompasses crop and livestock production, aquaculture, and forestry for food and non-food products. Agriculture was a key factor in the rise of sedentary human civilization, whereby farming of domesticated species created food surpluses that enabled people to live in cities. While humans started gathering grains at least 105,000 years ago, nascent farmers only began planting them around 11,500 years ago. Sheep, goats, pigs, and cattle were domesticated around 10,000 years ago. Plants were independently cultivated in at least 11 regions of the world. In the 20th century, industrial agriculture based on large-scale monocultures came to dominate agricultural output. , small farms produce about one-third of the world's food, but large farms are prevalent. The largest 1% of farms in the world are greater than and operate more than 70% of the world's farmland. Nearly 40% of agricultural land is found on farms larger than . However, five of every six farms in the world consist of" +260,"and goats. Cattle, sheep and goats were domesticated in Mehrgarh culture by 8,000–6,000 BC.Baber, Zaheer (1996). The Science of Empire: Scientific Knowledge, Civilization, and Colonial Rule in India. State University of New York Press. 19. .Harris, David R. and Gosden, C. (1996). The Origins and Spread of Agriculture and Pastoralism in Eurasia: Crops, Fields, Flocks And Herds. Routledge. p. 385. .Possehl, Gregory L. (1996). Mehrgarh in Oxford Companion to Archaeology, Ed. Brian Fagan. Oxford University Press. Cotton was cultivated by the 5th–4th millennium BC.Stein, Burton (1998). A History of India. Blackwell Publishing. p. 47. . Archeological evidence indicates an animal-drawn plough from 2,500 BC in the Indus Valley civilization. In China, from the 5th century BC, there was a nationwide granary system and widespread silk farming.Needham, Vol. 6, Part 2, pp. 55–57. Water-powered grain mills were in use by the 1st century BC,Needham, Vol. 4, Part 2, pp. 89, 110, 184. followed by irrig" +261,"the area is abandoned. Another patch of land is selected and the process is repeated. This type of farming is practiced mainly in areas with abundant rainfall where the forest regenerates quickly. This practice is used in Northeast India, Southeast Asia, and the Amazon Basin. Subsistence farming is practiced to satisfy family or local needs alone, with little left over for transport elsewhere. It is intensively practiced in Monsoon Asia and South-East Asia. An estimated 2.5 billion subsistence farmers worked in 2018, cultivating about 60% of the earth's arable land. Intensive farming is cultivation to maximize productivity, with a low fallow ratio and a high use of inputs (water, fertilizer, pesticide and automation). It is practiced mainly in developed countries. == Contemporary agriculture == === Status === From the twentieth century onwards, intensive agriculture increased crop productivity. It substituted synthetic fertilizers and pesticides for labour, but caused increased water p" +262,"independent farmers and are engaged in the production of less lucrative crops. The gender gap in land productivity between female- and male managed farms of the same size is 24 percent. On average, women earn 18.4 percent less than men in wage employment in agriculture; this means that women receive 82 cents for every dollar earned by men. Progress has been slow in closing gaps in women's access to irrigation and in ownership of livestock, too. Women in agriculture still have significantly less access than men to inputs, including improved seeds, fertilizers and mechanized equipment. On a positive note, the gender gap in access to mobile internet in low- and middle-income countries fell from 25 percent to 16 percent between 2017 and 2021, and the gender gap in access to bank accounts narrowed from 9 to 6 percentage points. Women are as likely as men to adopt new technologies when the necessary enabling factors are put in place and they have equal access to complementary resources. ===" +263,"Barley 134 Source: Food and Agriculture Organization Source: Food and Agriculture Organization === Livestock production systems === Animal husbandry is the breeding and raising of animals for meat, milk, eggs, or wool, and for work and transport. Working animals, including horses, mules, oxen, water buffalo, camels, llamas, alpacas, donkeys, and dogs, have for centuries been used to help cultivate fields, harvest crops, wrangle other animals, and transport farm products to buyers. Livestock production systems can be defined based on feed source, as grassland-based, mixed, and landless. , 30% of Earth's ice- and water-free area was used for producing livestock, with the sector employing approximately 1.3 billion people. Between the 1960s and the 2000s, there was a significant increase in livestock production, both by numbers and by carcass weight, especially among beef, pigs and chickens, the latter of which had production increased by almost a factor of 10. Non-meat animals, such as mi" +264,"encourage farmers to conserve some aspects of the environment. Measures might include paying for reforestation upstream of a city, to improve the supply of fresh water. === Agricultural automation === Different definitions exist for agricultural automation and for the variety of tools and technologies that are used to automate production. One view is that agricultural automation refers to autonomous navigation by robots without human intervention. Alternatively it is defined as the accomplishment of production tasks through mobile, autonomous, decision-making, mechatronic devices. However, FAO finds that these definitions do not capture all the aspects and forms of automation, such as robotic milking machines that are static, most motorized machinery that automates the performing of agricultural operations, and digital tools (e.g., sensors) that automate only diagnosis. FAO defines agricultural automation as the use of machinery and equipment in agricultural operations to improve their" +265,"to more than 2.5 t/ha in 1990. South American average wheat yields are around 2 t/ha, African under 1 t/ha, and Egypt and Arabia up to 3.5 to 4 t/ha with irrigation. In contrast, the average wheat yield in countries such as France is over 8 t/ha. Variations in yields are due mainly to variation in climate, genetics, and the level of intensive farming techniques (use of fertilizers, chemical pest control, and growth control to avoid lodging).Conversion note: 1 bushel of wheat=60 pounds (lb) ≈ 27.215 kg. 1 bushel of maize=56 pounds ≈ 25.401 kg Investments into innovation for agriculture are long term. This is because it takes time for research to become commercialized and for technology to be adapted to meet multiple regions’ needs, as well as meet national guidelines before being adopted and planted in a farmer’s fields. For instance, it took at least 60 years from the introduction of hybrid corn technology before its adoption became widespread. Agricultural innovation developed for the" +266,"land surface of the planet. It is one of the largest sources of greenhouse gases, responsible for 18% of the world's greenhouse gas emissions as measured in CO2 equivalents. By comparison, all transportation emits 13.5% of the CO2. It produces 65% of human-related nitrous oxide (which has 296 times the global warming potential of CO2) and 37% of all human-induced methane (which is 23 times as warming as CO2.) It also generates 64% of the ammonia emission. Livestock expansion is cited as a key factor driving deforestation; in the Amazon basin 70% of previously forested area is now occupied by pastures and the remainder used for feed crops. Through deforestation and land degradation, livestock is also driving reductions in biodiversity. A well documented phenomenon is woody plant encroachment, caused by overgrazing in rangelands. Furthermore, the United Nations Environment Programme (UNEP) states that methane emissions from global livestock are projected to increase by 60 per cent by 203" +267,"There is not enough water to continue farming using current practices; therefore how water, land, and ecosystem resources are used to boost crop yields must be reconsidered. A solution would be to give value to ecosystems, recognizing environmental and livelihood tradeoffs, and balancing the rights of a variety of users and interests. Inequities that result when such measures are adopted would need to be addressed, such as the reallocation of water from poor to rich, the clearing of land to make way for more productive farmland, or the preservation of a wetland system that limits fishing rights. Technological advancements help provide farmers with tools and resources to make farming more sustainable. Technology permits innovations like conservation tillage, a farming process which helps prevent land loss to erosion, reduces water pollution, and enhances carbon sequestration. Agricultural automation can help address some of the challenges associated with climate change and thus facilita" +268,"processed products) provided by the supply chain. Market concentration has increased in the sector as well, and although the total effect of the increased market concentration is likely increased efficiency, the changes redistribute economic surplus from producers (farmers) and consumers, and may have negative implications for rural communities. National government policies, such as taxation, subsidies, tariffs and others, can significantly change the economic marketplace for agricultural products. Since at least the 1960s, a combination of trade restrictions, exchange rate policies and subsidies have affected farmers in both the developing and the developed world. In the 1980s, non-subsidized farmers in developing countries experienced adverse effects from national policies that created artificially low global prices for farm products. Between the mid-1980s and the early 2000s, several international agreements limited agricultural tariffs, subsidies and other trade restrictions. Howev" +269,"* Farmworker * Food loss and waste * Food security * Hill farming * List of documentary films about agriculture * Pharming (genetics) * Remote sensing * Rural Development * Soil biodiversity * Subsistence economy * Sustainable agriculture * Urban agriculture * Vertical farming * Vegetable farming == References == == Cited sources == * * * == External links == * Food and Agriculture Organization * United States Department of Agriculture * Agriculture material from the World Bank Group * * Category:Agronomy Category:Food industry" +270,"Aldous Leonard Huxley ( ; 26 July 1894 – 22 November 1963) was an English writer and philosopher.: He was also a philosopher, mystic, social prophet, political thinker, and world traveler who had a detailed knowledge of music, medicine, science, technology, history, literature and Eastern religions. : Huxley was a philosopher but his viewpoint was not determined by the intellect alone. He believed the rational mind could only speculate about truth and never find it directly. His bibliography spans nearly 50 books,Dana Sawyer in M. Keith Booker (ed.), Encyclopedia of Literature and Politics: H–R, Greenwood Publishing Group (2005), p. 359 including non-fiction works, as well as essays, narratives, and poems. Born into the prominent Huxley family, he graduated from Balliol College, Oxford, with a degree in English literature. Early in his career, he published short stories and poetry and edited the literary magazine Oxford Poetry, before going on to publish travel writing, satire, and scr" +271,"of profound ideas, subtle interconnections, and various emerging spiritual and psychotherapy methods.Murray, Nicholas (4 June 2009). Aldous Huxley: An English Intellectual. Little, Brown Book Group. p. 220. ISBN 978-0-7481-1231-9. Works of this period included novels about the dehumanising aspects of scientific progress, (his magnum opus Brave New World), and on pacifist themes (Eyeless in Gaza). In Brave New World, set in a dystopian London, Huxley portrays a society operating on the principles of mass production and Pavlovian conditioning. Huxley was strongly influenced by F. Matthias Alexander, on whom he based a character in Eyeless in Gaza. During this period, Huxley began to write and edit non-fiction works on pacifist issues, including Ends and Means (1937), An Encyclopedia of Pacifism, and Pacifism and Philosophy, and was an active member of the Peace Pledge Union (PPU). === Life in the United States === In 1937, Huxley moved to Hollywood with his wife Maria, son Matthew Huxley" +272,"December 1962 letter to brother Julian, summarizing a paper he had presented in Santa Barbara, he wrote, What I said was that if we didn't pretty quickly start thinking of human problems in ecological terms rather than in terms of power politics we should very soon be in a bad way. Huxley's engagement with Eastern wisdom traditions was entirely compatible with a strong appreciation of modern science. Biographer Milton Birnbaum wrote that Huxley ended by embracing both science and Eastern religion . In his last book, Literature and Science, Huxley wrote that The ethical and philosophical implications of modern science are more Buddhist than Christian.... In A Philosopher's Visionary Prediction , published one month before he died, Huxley endorsed training in general semantics and the nonverbal world of culturally uncontaminated consciousness , writing that We must learn how to be mentally silent, we must cultivate the art of pure receptivity.... [T]he individual must learn to deconditio" +273,"on CD: Knowledge and Understanding and Who Are We? from 1955. Many of Huxley's contemporaries and critics were disappointed by Huxley's turn to mysticism; Isherwood describes in his diary how he had to explain the criticism to Huxley's widow, Laura: == Psychedelic drug use and mystical experiences== In early 1953, Huxley had his first experience with the psychedelic drug mescaline. Huxley had initiated a correspondence with Doctor Humphry Osmond, a British psychiatrist then employed in a Canadian institution, and eventually asked him to supply a dose of mescaline; Osmond obliged and supervised Huxley's session in southern California. After the publication of The Doors of Perception, in which he recounted this experience, Huxley and Swami Prabhavananda disagreed about the meaning and importance of the psychedelic drug experience, which may have caused the relationship to cool, but Huxley continued to write articles for the society's journal, lecture at the temple, and attend social func" +274,"via letter on 28 April 1962.Peter Edgerly Firchow, Hermann Josef Real (2005). The Perennial Satirist: Essays in Honour of Bernfried Nugel, Presented on the Occasion of His 65th Birthday, p. 1. LIT Verlag Münster The correspondence between Huxley and the society is kept at the Cambridge University Library. The society invited Huxley to appear at a banquet and give a lecture at Somerset House, London, in June 1963. Huxley wrote a draft of the speech he intended to give at the society; however, his deteriorating health meant he was not able to attend. == Death == In 1960, Huxley was diagnosed with oral cancer and for the next three years his health steadily declined. On November 4, 1963, less than three weeks before Huxley's death, author Christopher Isherwood, a friend of 25 years, visited in Cedars Sinai Hospital and wrote his impressions: At home on his deathbed, unable to speak owing to cancer that had metastasized, Huxley made a written request to his wife Laura for LSD, 100 μg, intr" +275,"Ada may refer to: ==Arts and entertainment== * Ada or Ardor: A Family Chronicle, a novel by Vladimir Nabokov ===Film and television=== * Ada TV, a television channel in Northern Cyprus * Ada (1961 film), a 1961 film by Daniel Mann * Ada (2019 film), a short biopic about Ada Lovelace * Ada... A Way of Life, a 2008 Bollywood musical by Tanvir Ahmed * Ada (dog actor), a dog that played Colin on the sitcom Spaced * Ada, a character in 1991 movie Armour of God II: Operation Condor ==Places== ===Africa=== * Ada Foah, a town in Ghana * Ada (Ghana parliament constituency) * Ada, Osun, a town in Nigeria ===Asia=== * Ada, Urmia, a village in West Azerbaijan Province, Iran * Ada, Karaman, a village in Karaman Province, Turkey ===Europe=== * Ada, Bosnia and Herzegovina, a village * Ada, Croatia, a village * Ada, Serbia, a town and municipality * Ada Ciganlija or Ada, a river island artificially turned into a peninsula in Belgrade, Serbia ===United States=== * Ada, Alabama, an unincorporated commun" +276,"Aberdeen is a city in Scotland. Aberdeen may also refer to: == Places == === Africa === * Aberdeen, Sierra Leone * Aberdeen, Eastern Cape, South Africa === Asia === ==== Hong Kong ==== * Aberdeen, Hong Kong, an area and town on southwest Hong Kong Island * Aberdeen Channel, a channel between Ap Lei Chau (Aberdeen Island) and Nam Long Shan on the Hong Kong Island in Hong Kong * Aberdeen Country Park, a country park in Hong Kong Island * Aberdeen floating village, at Aberdeen Harbour, containing approximately 600 junks, which house an estimated 6,000 people * Aberdeen Harbour, a harbour between Aberdeen, Hong Kong and Ap Lei Chau (Aberdeen Island) * Aberdeen Tunnel, a tunnel in Hong Kong Island ** Aberdeen Tunnel Underground Laboratory, an underground particle physics laboratory in Hong Kong Island * Ap Lei Chau or Aberdeen Island, an island of Hong Kong * Aberdeen (Hong Kong constituency), a constituency of Southern District Council ==== India ==== * Aberdeen Bazaar, a shopping centre i" +277,"Algae ( , ; : alga ) are any of a large and diverse group of photosynthetic, eukaryotic organisms. The name is an informal term for a polyphyletic grouping that includes species from multiple distinct clades. Included organisms range from unicellular microalgae, such as Chlorella, Prototheca and the diatoms, to multicellular forms, such as the giant kelp, a large brown alga which may grow up to in length. Most are aquatic and lack many of the distinct cell and tissue types, such as stomata, xylem and phloem that are found in land plants. The largest and most complex marine algae are called seaweeds. In contrast, the most complex freshwater forms are the Charophyta, a division of green algae which includes, for example, Spirogyra and stoneworts. Algae that are carried by water are plankton, specifically phytoplankton. Algae constitute a polyphyletic group since they do not include a common ancestor, and although their plastids seem to have a single origin, from cyanobacteria, they were" +278,"its original endosymbiont of red algal origin with one of green algal origin. A nucleomorph is present, and the host genome still have several red algal genes acquired through endosymbiotic gene transfer. Also the euglenid and chlorarachniophyte genome contain genes of apparent red algal ancestry) Halvaria and Hacrobia * Heterokonts * Dinoflagellates * Haptophyta * Cryptomonads Red algae These groups have chloroplasts containing chlorophylls a and c, and phycobilins. The shape can vary; they may be of discoid, plate-like, reticulate, cup-shaped, spiral, or ribbon shaped. They have one or more pyrenoids to preserve protein and starch. The latter chlorophyll type is not known from any prokaryotes or primary chloroplasts, but genetic similarities with red algae suggest a relationship there. In the first three of these groups (Chromista), the chloroplast has four membranes, retaining a nucleomorph in cryptomonads, and they likely share a common pigmented ancestor, although other evidence c" +279,"from sessile shallow freshwater charophyte algae much like Chara, which likely got stranded ashore when riverine/lacustrine water levels dropped during dry seasons. These charophyte algae probably already developed filamentous thalli and holdfasts that superficially resembled plant stems and roots, and probably had an isomorphic alternation of generations. They perhaps evolved some 850 mya and might even be as early as 1 Gya during the late phase of the Boring Billion. ==Morphology== A range of algal morphologies is exhibited, and convergence of features in unrelated groups is common. The only groups to exhibit three-dimensional multicellular thalli are the reds and browns, and some chlorophytes. Apical growth is constrained to subsets of these groups: the florideophyte reds, various browns, and the charophytes. The form of charophytes is quite different from those of reds and browns, because they have distinct nodes, separated by internode 'stems'; whorls of branches reminiscent of th" +280,"example, breadcrumb sponges (Halichondria panicea). The alga is thus protected from predators; the sponge is provided with oxygen and sugars which can account for 50 to 80% of sponge growth in some species. ==Life cycle== Rhodophyta, Chlorophyta, and Heterokontophyta, the three main algal divisions, have life cycles which show considerable variation and complexity. In general, an asexual phase exists where the seaweed's cells are diploid, a sexual phase where the cells are haploid, followed by fusion of the male and female gametes. Asexual reproduction permits efficient population increases, but less variation is possible. Commonly, in sexual reproduction of unicellular and colonial algae, two specialized, sexually compatible, haploid gametes make physical contact and fuse to form a zygote. To ensure a successful mating, the development and release of gametes is highly synchronized and regulated; pheromones may play a key role in these processes. Sexual reproduction allows for more var" +281,"is used both for algae and (in the modest tradition of the imperial scholars) for literary talent . The third island in Kunming Lake beside the Summer Palace in Beijing is known as the Zaojian Tang Dao (藻鑒堂島), which thus simultaneously means Island of the Algae-Viewing Hall and Island of the Hall for Reflecting on Literary Talent . == Cultivation == === Seaweed farming === === Bioreactors === ==Uses== ===Agar=== Agar, a gelatinous substance derived from red algae, has a number of commercial uses. It is a good medium on which to grow bacteria and fungi, as most microorganisms cannot digest agar. ===Alginates=== Alginic acid, or alginate, is extracted from brown algae. Its uses range from gelling agents in food, to medical dressings. Alginic acid also has been used in the field of biotechnology as a biocompatible medium for cell encapsulation and cell immobilization. Molecular cuisine is also a user of the substance for its gelling properties, by which it becomes a delivery vehicle for f" +282,"and coloring agents. The presence of some individual algal pigments, together with specific pigment concentration ratios, are taxon-specific: analysis of their concentrations with various analytical methods, particularly high-performance liquid chromatography, can therefore offer deep insight into the taxonomic composition and relative abundance of natural algae populations in sea water samples. ===Stabilizing substances=== Carrageenan, from the red alga Chondrus crispus, is used as a stabilizer in milk products. ==Additional images== File:Algae bladder 4290.jpg|Algae bladder ==See also== * AlgaeBase * AlgaePARC * Eutrophication * Iron fertilization * Marimo algae * Microbiofuels * Microphyte * Photobioreactor * Phycotechnology * Plant * Toxoid – anatoxin ==References== ==Bibliography== ===General=== * * * * * * . * * * ===Regional=== ====Britain and Ireland==== * * * * * * * * ====Australia==== * ====New Zealand==== * ====Europe==== * * * * * * ====Arctic==== * ====Greenland==== * ===" +283,"Analysis of variance (ANOVA) is a collection of statistical models and their associated estimation procedures (such as the variation among and between groups) used to analyze the differences among means. ANOVA was developed by the statistician Ronald Fisher. ANOVA is based on the law of total variance, where the observed variance in a particular variable is partitioned into components attributable to different sources of variation. In its simplest form, ANOVA provides a statistical test of whether two or more population means are equal, and therefore generalizes the t-test beyond two means. In other words, the ANOVA is used to test the difference between two or more means. ==History== While the analysis of variance reached fruition in the 20th century, antecedents extend centuries into the past according to Stigler.Stigler (1986) These include hypothesis testing, the partitioning of sums of squares, experimental techniques and the additive model. Laplace was performing hypothesis testi" +284,"of the method is a judgment in the confidence in an explanatory relationship. == Classes of models == There are three classes of models used in the analysis of variance, and these are outlined here. ===Fixed-effects models=== The fixed-effects model (class I) of analysis of variance applies to situations in which the experimenter applies one or more treatments to the subjects of the experiment to see whether the response variable values change. This allows the experimenter to estimate the ranges of response variable values that the treatment would generate in the population as a whole. ===Random-effects models=== Random-effects model (class II) is used when the treatments are not fixed. This occurs when the various factor levels are sampled from a larger population. Because the levels themselves are random variables, some assumptions and the method of contrasting the treatments (a multi-variable generalization of simple differences) differ from the fixed-effects model.Montgomery (2001," +285,"must control by the device of randomization. [O]nly when the treatments in the experiment are applied by the experimenter using the full randomization procedure is the chain of inductive inference sound. It is only under these circumstances that the experimenter can attribute whatever effects he observes to the treatment and the treatment only. Under these circumstances his conclusions are reliable in the statistical sense. ) For observational data, the derivation of confidence intervals must use subjective models, as emphasized by Ronald Fisher and his followers. In practice, the estimates of treatment-effects from observational studies generally are often inconsistent. In practice, statistical models and observational data are useful for suggesting hypotheses that should be treated very cautiously by the public.Freedman ===Summary of assumptions=== The normal-model based ANOVA analysis assumes the independence, normality, and homogeneity of variances of the residuals. The randomizati" +286,"compare the observed value of F with the critical value of F determined from tables. The critical value of F is a function of the degrees of freedom of the numerator and the denominator and the significance level (α). If F ≥ FCritical, the null hypothesis is rejected. * The computer method calculates the probability (p-value) of a value of F greater than or equal to the observed value. The null hypothesis is rejected if this probability is less than or equal to the significance level (α). The ANOVA F-test is known to be nearly optimal in the sense of minimizing false negative errors for a fixed rate of false positive errors (i.e. maximizing power for a fixed significance level). For example, to test the hypothesis that various medical treatments have exactly the same effect, the F-test's p-values closely approximate the permutation test's p-values: The approximation is particularly close when the design is balanced.Hinkelmann and Kempthorne (2008, Volume 1, Section 6.7: Completely rand" +287,"(2001, Section 5-1: Introduction to factorial designs; Basic definitions and principles) Graphical methods are recommended to enhance understanding. Regression is often useful. A lengthy discussion of interactions is available in Cox (1958).Cox (1958, Chapter 6: Basic ideas about factorial experiments) Some interactions can be removed (by transformations) while others cannot. A variety of techniques are used with multiple factor ANOVA to reduce expense. One technique used in factorial designs is to minimize replication (possibly no replication with support of analytical trickery) and to combine groups when effects are found to be statistically (or practically) insignificant. An experiment with many insignificant factors may collapse into one with a few factors supported by many replications.Montgomery (2001, Section 5-3.7: Introduction to factorial designs; The two-factor factorial design; One observation per cell) ==Associated analysis== Some analysis is required in support of the des" +288,"a linear trend estimation should be used. Typically, however, the one-way ANOVA is used to test for differences among at least three groups, since the two-group case can be covered by a t-test. When there are only two means to compare, the t-test and the ANOVA F-test are equivalent; the relation between ANOVA and t is given by . *Factorial ANOVA is used when there is more than one factor. *Repeated measures ANOVA is used when the same subjects are used for each factor (e.g., in a longitudinal study). *Multivariate analysis of variance (MANOVA) is used when there is more than one response variable. ==Cautions== Balanced experiments (those with an equal sample size for each treatment) are relatively easy to interpret; unbalanced experiments offer more complexity. For single-factor (one-way) ANOVA, the adjustment for unbalanced data is easy, but the unbalanced analysis lacks both robustness and power.Montgomery (2001, Section 3-3.4: Unbalanced data) For more complex designs the lack of ba" +289,"In organic chemistry, an alkane, or paraffin (a historical trivial name that also has other meanings), is an acyclic saturated hydrocarbon. In other words, an alkane consists of hydrogen and carbon atoms arranged in a tree structure in which all the carbon–carbon bonds are single. Alkanes have the general chemical formula . The alkanes range in complexity from the simplest case of methane (), where n = 1 (sometimes called the parent molecule), to arbitrarily large and complex molecules, like pentacontane () or 6-ethyl-2-methyl-5-(1-methylethyl) octane, an isomer of tetradecane (). The International Union of Pure and Applied Chemistry (IUPAC) defines alkanes as acyclic branched or unbranched hydrocarbons having the general formula , and therefore consisting entirely of hydrogen atoms and saturated carbon atoms . However, some sources use the term to denote any saturated hydrocarbon, including those that are either monocyclic (i.e. the cycloalkanes) or polycyclic, despite them having a d" +290,"carbon and 8 hydrogen ; butane : C4H10 – four carbon and 10 hydrogen ; pentane: C5H12 – five carbon and 12 hydrogen ; hexane : C6H14 – six carbon and 14 hydrogen ; heptane: C7H16 – seven carbons and 16 hydrogen ; octane: C8H18 – eight carbons and 18 hydrogen The first four names were derived from methanol, ether, propionic acid and butyric acid. Alkanes with five or more carbon atoms are named by adding the suffix -ane to the appropriate numerical multiplier prefix with elision of any terminal vowel (-a or -o) from the basic numerical term. Hence, pentane, C5H12; hexane, C6H14; heptane, C7H16; octane, C8H18; etc. The numeral prefix is generally Greek; however, alkanes with a carbon atom count ending in nine, for example nonane, use the Latin prefix non-. ===Branched alkanes=== Simple branched alkanes often have a common name using a prefix to distinguish them from linear alkanes, for example n-pentane, isopentane, and neopentane. IUPAC naming conventions can be used to produce a system" +291,"plane of intermolecular contact. ===Melting points=== The melting points of the alkanes follow a similar trend to boiling points for the same reason as outlined above. That is, (all other things being equal) the larger the molecule the higher the melting point. There is one significant difference between boiling points and melting points. Solids have a more rigid and fixed structure than liquids. This rigid structure requires energy to break down. Thus the better put together solid structures will require more energy to break apart. For alkanes, this can be seen from the graph above (i.e., the blue line). The odd-numbered alkanes have a lower trend in melting points than even-numbered alkanes. This is because even-numbered alkanes pack well in the solid phase, forming a well-organized structure which requires more energy to break apart. The odd-numbered alkanes pack less well and so the looser -organized solid packing structure requires less energy to break apart. For a visualization o" +292,"C–H bonds (~100 kcal/mol) and C–C bonds (~90 kcal/mol). They are also relatively unreactive toward free radicals. This inertness is the source of the term paraffins (with the meaning here of lacking affinity ). In crude oil the alkane molecules have remained chemically unchanged for millions of years. ===Acid-base behavior=== The acid dissociation constant (pKa) values of all alkanes are estimated to range from 50 to 70, depending on the extrapolation method, hence they are extremely weak acids that are practically inert to bases (see: carbon acids). They are also extremely weak bases, undergoing no observable protonation in pure sulfuric acid (H0 ~ −12), although superacids that are at least millions of times stronger have been known to protonate them to give hypercoordinate alkanium ions (see: methanium ion). Thus, a mixture of antimony pentafluoride (SbF5) and fluorosulfonic acid (HSO3F), called magic acid, can protonate alkanes. ===Reactions with oxygen (combustion reaction)=== All" +293,"gut of ruminants. The most important commercial sources for alkanes are natural gas and oil. Natural gas contains primarily methane and ethane, with some propane and butane: oil is a mixture of liquid alkanes and other hydrocarbons. These hydrocarbons were formed when marine animals and plants (zooplankton and phytoplankton) died and sank to the bottom of ancient seas and were covered with sediments in an anoxic environment and converted over many millions of years at high temperatures and high pressure to their current form. Natural gas resulted thereby for example from the following reaction: :C6H12O6 → 3 CH4 + 3 CO2 These hydrocarbon deposits, collected in porous rocks trapped beneath impermeable cap rocks, comprise commercial oil fields. They have formed over millions of years and once exhausted cannot be readily replaced. The depletion of these hydrocarbons reserves is the basis for what is known as the energy crisis. Alkanes have a low solubility in water, so the content in the o" +294,"United States appellate procedure involves the rules and regulations for filing appeals in state courts and federal courts. The nature of an appeal can vary greatly depending on the type of case and the rules of the court in the jurisdiction where the case was prosecuted. There are many types of standard of review for appeals, such as de novo and abuse of discretion. However, most appeals begin when a party files a petition for review to a higher court for the purpose of overturning the lower court's decision. An appellate court is a court that hears cases on appeal from another court. Depending on the particular legal rules that apply to each circumstance, a party to a court case who is unhappy with the result might be able to challenge that result in an appellate court on specific grounds. These grounds typically could include errors of law, fact, procedure or due process. In different jurisdictions, appellate courts are also called appeals courts, courts of appeals, superior courts," +295,"is distinguished from judicial review, which refers to the court's overriding constitutional or statutory right to determine if a legislative act or administrative decision is defective for jurisdictional or other reasons (which may vary by jurisdiction). In most jurisdictions the normal and preferred way of seeking appellate review is by filing an appeal of the final judgment. Generally, an appeal of the judgment will also allow appeal of all other orders or rulings made by the trial court in the course of the case. This is because such orders cannot be appealed as of right . However, certain critical interlocutory court orders, such as the denial of a request for an interim injunction, or an order holding a person in contempt of court, can be appealed immediately although the case may otherwise not have been fully disposed of. There are two distinct forms of appellate review, direct and collateral . For example, a criminal defendant may be convicted in state court, and lose on direct" +296,"the appellate court does find a legal defect in the decision below (i.e., in the lower court), it may modify the ruling to correct the defect, or it may nullify ( reverse or vacate ) the whole decision or any part of it. It may, in addition, send the case back ( remand or remit ) to the lower court for further proceedings to remedy the defect. In some cases, an appellate court may review a lower court decision de novo (or completely), challenging even the lower court's findings of fact. This might be the proper standard of review, for example, if the lower court resolved the case by granting a pre-trial motion to dismiss or motion for summary judgment which is usually based only upon written submissions to the trial court and not on any trial testimony. Another situation is where appeal is by way of re-hearing . Certain jurisdictions permit certain appeals to cause the trial to be heard afresh in the appellate court. Sometimes, the appellate court finds a defect in the procedure the pa" +297,"result in the affirmance of the lower court's judgment on the grounds that one did not preserve the issue for appeal by objecting. In cases where a judge rather than a jury decided issues of fact, an appellate court will apply an abuse of discretion standard of review. Under this standard, the appellate court gives deference to the lower court's view of the evidence, and reverses its decision only if it were a clear abuse of discretion. This is usually defined as a decision outside the bounds of reasonableness. On the other hand, the appellate court normally gives less deference to a lower court's decision on issues of law, and may reverse if it finds that the lower court applied the wrong legal standard. In some cases, an appellant may successfully argue that the law under which the lower decision was rendered was unconstitutional or otherwise invalid, or may convince the higher court to order a new trial on the basis that evidence earlier sought was concealed or only recently discove" +298,"In law, an answer was originally a solemn assertion in opposition to someone or something, and thus generally any counter-statement or defense, a reply to a question or response, or objection, or a correct solution of a problem. In the common law, an answer is the first pleading by a defendant, usually filed and served upon the plaintiff within a certain strict time limit after a civil complaint or criminal information or indictment has been served upon the defendant. It may have been preceded by an optional pre-answer motion to dismiss or demurrer; if such a motion is unsuccessful, the defendant must file an answer to the complaint or risk an adverse default judgment. In a criminal case, there is usually an arraignment or some other kind of appearance before the defendant comes to court. The pleading in the criminal case, which is entered on the record in open court, is usually either guilty or not guilty. Generally, speaking in private, civil cases there is no plea entered of guilt o" +299,"An appellate court, commonly called a court of appeal(s), appeal court, court of second instance or second instance court, is any court of law that is empowered to hear a case upon appeal from a trial court or other lower tribunal. In much of the world, court systems are divided into at least three levels: the trial court, which initially hears cases and considers factual evidence and testimony relevant to the case; at least one intermediate appellate court; and a supreme court (or court of last resort) which primarily reviews the decisions of the intermediate courts, often on a discretionary basis. A particular court system's supreme court is its highest appellate court. Appellate courts nationwide can operate under varying rules. Under its standard of review, an appellate court decides the extent of the deference it would give to the lower court's decision, based on whether the appeal were one of fact or of law. In reviewing an issue of fact, an appellate court ordinarily gives defer" +300,"theory that is raised for the first time in the appeal. thumb|225px|The Fifth District Court of Appeal for Florida, an intermediate appellate court for the Florida judicial system In most U.S. states, and in U.S. federal courts, parties before the court are allowed one appeal as of right. This means that a party who is unsatisfied with the outcome of a trial may bring an appeal to contest that outcome. However, appeals may be costly, and the appellate court must find an error on the part of the court below that justifies upsetting the verdict. Therefore, only a small proportion of trial court decisions result in appeals. Some courts, particularly supreme courts, have the power of discretionary review, meaning that they can decide whether they will hear an appeal brought in a particular case. ==== Nomenclature ==== Many U.S. jurisdictions title their appellate court a court of appeal or court of appeals. Both terms are used in the United States, but the plural form is more common in Ame" +301,"Arraignment is a formal reading of a criminal charging document in the presence of the defendant, to inform them of the criminal charges against them. In response to arraignment, in some jurisdictions, the accused is expected to enter a plea; in other jurisdictions, no plea is required. Acceptable pleas vary among jurisdictions, but they generally include guilty, not guilty, and the peremptory pleas (pleas in bar) setting out reasons why a trial cannot proceed. Pleas of nolo contendere ('no contest') and the Alford plea are allowed in some circumstances. == By country == === Australia === In the Australian legal system, arraignment is the first stage in a criminal trial. The indictment is read to the defendant, who is asked to plead guilty or not guilty. Arraignment procedures vary somewhat among jurisdictions. In New South Wales, the arraignment takes place before the judge only. In South Australian practice, the jury hears the arraignment. In Queensland the indictment is read to the" +302,"Use of video arraignment system allows the court to conduct the requisite arraignment process without the need to transport the defendant to the courtroom by using an audio-visual link between the location where the defendant is being held and the courtroom. Use of the video arraignment process addresses the problems associated with having to transport defendants. The transportation of defendants requires time, puts additional demands on the public safety organizations to provide for the safety of the public, court personnel and for the security of the population held in detention. It also addresses the rising costs of transportation. ==Guilty and not-guilty pleas== If the defendant pleads guilty, an evidentiary hearing usually follows. The court is not required to accept a guilty plea. During the hearing, the judge assesses the offense, the mitigating factors, and the defendant's character, and passes sentence. If the defendant pleads not guilty, a date is set for a preliminary hearin" +303,"America the Beautiful is a patriotic American song. Its lyrics were written by Katharine Lee Bates and its music was composed by church organist and choirmaster Samuel A. Ward at Grace Episcopal Church in Newark, New Jersey. The two never met.Andy Pease, 'America the Beautiful' by Katharine Lee Bates and Samuel Augustus Ward, arr. Carmen Dragon , Wind Band Literature, July 1, 2014; accessed 2019-08-17. Bates wrote the words as a poem, originally titled Pikes Peak . It was first published in the Fourth of July 1895 edition of the church periodical, The Congregationalist. At that time, the poem was titled America . Ward had initially composed the song's melody in 1882 to accompany lyrics to Materna , basis of the hymn, O Mother dear, Jerusalem , though the hymn was not first published until 1892. (McKim notes that Ward once mentioned in a postcard that the hymn had been composed in 1882, however). The combination of Ward's melody and Bates's poem was first entitled America the Beautiful" +304,"God shed His grace on thee And crown thy good with brotherhood From sea to shining sea! O beautiful for pilgrim feet, Whose stern, impassioned stress A thoroughfare for freedom beat Across the wilderness! America! America! God mend thine every flaw, Confirm thy soul in self-control, Thy liberty in law! O beautiful for heroes proved In liberating strife, Who more than self their country loved And mercy more than life! America! America! May God thy gold refine, Till all success be nobleness, And every gain divine! O beautiful for patriot dream That sees beyond the years Thine alabaster cities gleam Undimmed by human tears! America! America! God shed His grace on thee And crown thy good with brotherhood From sea to shining sea! ==Notable performances== Elvis Presley performed it many times in concerts starting in 1976. Bing Crosby included the song in a medley on his album 101 Gang Songs (1961). Frank Sinatra recorded the song with Nelson Riddle during the sessions for The Concert Sinatra" +305,"close the 2024 Republican Party's national convention in Milwaukee, Wisconsin and former President’s Trump campaign rally in Butler, Pennsylvania. ==Idioms== From sea to shining sea , originally used in the charters of some of the English colonies in North America, is an American idiom meaning from the Atlantic Ocean to the Pacific Ocean (or vice versa). Other songs that have used this phrase include the American patriotic song God Bless the U.S.A. and Schoolhouse Rock's Elbow Room . The phrase and the song are also the namesake of the Shining Sea Bikeway, a bike path in Bates's hometown of Falmouth, Massachusetts. The phrase is similar to the Latin phrase ( From sea to sea ), which is the official motto of Canada. Purple mountain majesties refers to the shade of Pikes Peak in Colorado Springs, Colorado, which inspired Bates to write the poem. The idiom inspired the Colorado Rockies to have purple as one of its team colors.Harding, Thomas. Rockies outfitted with one shade of purple, ML" +306,"Assistive technology (AT) is a term for assistive, adaptive, and rehabilitative devices for people with disabilities and the elderly. Disabled people often have difficulty performing activities of daily living (ADLs) independently, or even with assistance. ADLs are self-care activities that include toileting, mobility (ambulation), eating, bathing, dressing, grooming, and personal device care. Assistive technology can ameliorate the effects of disabilities that limit the ability to perform ADLs. Assistive technology promotes greater independence by enabling people to perform tasks they were formerly unable to accomplish, or had great difficulty accomplishing, by providing enhancements to, or changing methods of interacting with, the technology needed to accomplish such tasks. For example, wheelchairs provide independent mobility for those who cannot walk, while assistive eating devices can enable people who cannot feed themselves to do so. Due to assistive technology, disabled people h" +307,"A walker or walking frame or Rollator is a tool for disabled people who need additional support to maintain balance or stability while walking. It consists of a frame that is about waist high, approximately twelve inches deep and slightly wider than the user. Walkers are also available in other sizes, such as for children, or for heavy people. Modern walkers are height-adjustable. The front two legs of the walker may or may not have wheels attached depending on the strength and abilities of the person using it. It is also common to see caster wheels or glides on the back legs of a walker with wheels on the front.C. Barrué. Personalization and Shared Autonomy in Assistive Technologies. Ph. Thesis. Universitat Politècnica de Catalunya. 2012 ===Treadmills=== Bodyweight-supported treadmill training (BWSTT) is used to enhance walking ability of people with neurological injury. These machines are therapist-assisted devices that are used in the clinical setting, but is limited by the personne" +308,"screen magnifiers, Braille embossers, desktop video magnifiers, and voice recorders. === Screen readers === Screen readers are used to help the visually impaired to easily access electronic information. These software programs run on a computer to convey the displayed information through voice (text-to-speech) or braille (refreshable braille displays) in combination with magnification for low vision users in some cases. There are a variety of platforms and applications available for a variety of costs with differing feature sets. Some example of screen readers are Apple VoiceOver, CheckMeister browser, Google TalkBack and Microsoft Narrator. Screen readers may rely on the assistance of text-to-speech tools. To use the text-to-speech tools, the documents must be in an electronic form, which is uploaded as the digital format. However, people usually will use the hard copy documents scanned into the computer, which cannot be recognized by the text-to-speech software. To solve this issue," +309,"development of this standard has been supported in Europe by SNAPI and has been successfully incorporated into the Lasseo specifications, but with limited success due to the lack of interest from public computer terminal suppliers. ==Hearing impairments== People in the deaf and hard of hearing community have a more difficult time receiving auditory information as compared to hearing individuals. These individuals often rely on visual and tactile mediums for receiving and communicating information. The use of assistive technology and devices provides this community with various solutions to auditory communication needs by providing higher sound (for those who are hard of hearing), tactile feedback, visual cues and improved technology access. Individuals who are deaf or hard of hearing use a variety of assistive technologies that provide them with different access to information in numerous environments. Most devices either provide amplified sound or alternate ways to access information" +310,"is being scooped. More sophisticated equipment for eating includes manual and powered feeding devices. These devices support those who have little or no hand and arm function and enable them to eat independently. == In sports == Assistive technology in sports is an area of technology design that is growing. Assistive technology is the array of new devices created to enable sports enthusiasts who have disabilities to play. Assistive technology may be used in adaptive sports, where an existing sport is modified to enable players with a disability to participate; or, assistive technology may be used to invent completely new sports with athletes with disabilities exclusively in mind. An increasing number of people with disabilities are participating in sports, leading to the development of new assistive technology. Assistive technology devices can be simple, or low-technology , or they may use highly advanced technology. Low-tech devices can include velcro gloves and adaptive bands and tub" +311,"turn off the oven, and one by the front door may remind the resident to lock the door. == Assistive technology and innovation == Innovation is happening in assistive technology either through improvements to existing devices or the creation of new products. In the WIPO published 2021 report on Technology Trends, assistive products are grouped into either conventional or emerging technologies. Conventional assisting technology tracks innovation within well-established assistive products, whereas emerging assistive technology refers to more advanced products. These identified advanced assistive products are distinguished from the conventional ones by the use of one or more enabling technologies (for instance, artificial intelligence, Internet of things, advanced sensors, new material, Additive Manufacturing, advanced robotics, augmented and virtual reality) or by the inclusion of implantable products/components. Such emerging assistive products are either more sophisticated or more funct" +312,"machine learning to identify the appropriate assistive device to suggest to patients, making assistive devices more accessible. ==History== In 1988 the National institute on disability and rehabilitation research, NIDRR, awarded Gaulladet University a grant for the project Robotic finger spelling hand for communication and access to text by deaf-blind persons . Researchers at the university developed and tested a robotic hand. Although it was never commercialized the concept is relevant for current and future research. Since this grant, many others have been written. NIDRR funded research appears to be moving from the fabrication of robotic arms that can be used by disabled persons to perform daily activities, to developing robotics that assist with therapy in the hopes of achieving long-term performance gains. If there is success in development of robotics, these mass-marketed products could assist tomorrow's longer-living elderly individuals enough to postpone nursing home stays. Jim" +313,"An abacus (: abaci or abacuses), also called a counting frame, is a hand-operated calculating tool which was used from ancient times in the ancient Near East, Europe, China, and Russia, until the adoption of the Arabic numeral system. An abacus consists of a two-dimensional array of slidable beads (or similar objects). In their earliest designs, the beads could be loose on a flat surface or sliding in grooves. Later the beads were made to slide on rods and built into a frame, allowing faster manipulation. Each rod typically represents one digit of a multi-digit number laid out using a positional numeral system such as base ten (though some cultures used different numerical bases). Roman and East Asian abacuses use a system resembling bi-quinary coded decimal, with a top deck (containing one or two beads) representing fives and a bottom deck (containing four or five beads) representing ones. Natural numbers are normally used, but some allow simple fractional components (e.g. , , and in" +314,"Rome === The normal method of calculation in ancient Rome, as in Greece, was by moving counters on a smooth table. Originally pebbles () were used. Marked lines indicated units, fives, tens, etc. as in the Roman numeral system. Writing in the 1st century BC, Horace refers to the wax abacus, a board covered with a thin layer of black wax on which columns and figures were inscribed using a stylus. One example of archaeological evidence of the Roman abacus, shown nearby in reconstruction, dates to the 1st century AD. It has eight long grooves containing up to five beads in each and eight shorter grooves having either one or no beads in each. The groove marked I indicates units, X tens, and so on up to millions. The beads in the shorter grooves denote fives (five units, five tens, etc.) resembling a bi-quinary coded decimal system related to the Roman numerals. The short grooves on the right may have been used for marking Roman ounces (i.e. fractions). === Medieval Europe === The Roman sys" +315,"is generally used instead of the division method; at the same time, in order to make the multiplication and division digits consistently use the division multiplication. Later, Japan had a 3:5 abacus called 天三算盤, which is now in the Ize Rongji collection of Shansi Village in Yamagata City. Japan also used a 2:5 type abacus. The four-bead abacus spread, and became common around the world. Improvements to the Japanese abacus arose in various places. In China, an abacus with an aluminium frame and plastic beads has been used. The file is next to the four beads, and pressing the clearing button puts the upper bead in the upper position, and the lower bead in the lower position. The abacus is still manufactured in Japan, despite the proliferation, practicality, and affordability of pocket electronic calculators. The use of the soroban is still taught in Japanese primary schools as part of mathematics, primarily as an aid to faster mental calculation. Using visual imagery, one can complete a" +316,"by mathematician Jean-Victor Poncelet, who had served in Napoleon's army and had been a prisoner of war in Russia. The abacus had fallen out of use in western Europe in the 16th century with the rise of decimal notation and algorismic methods. To Poncelet's French contemporaries, it was something new. Poncelet used it, not for any applied purpose, but as a teaching and demonstration aid. The Turks and the Armenian people used abacuses similar to the Russian schoty. It was named a coulba by the Turks and a choreb by the Armenians. ==School abacus== thumb|A twenty bead rekenrek Around the world, abacuses have been used in pre-schools and elementary schools as an aid in teaching the numeral system and arithmetic. In Western countries, a bead frame similar to the Russian abacus but with straight wires and a vertical frame is common (see image). The wireframe may be used either with positional notation like other abacuses (thus the 10-wire version may represent numbers up to 9,999,999,999)," +317,"thumb|Zinc, a typical metal, reacting with hydrochloric acid, a typical acid An acid is a molecule or ion capable of either donating a proton (i.e. hydrogen ion, H+), known as a Brønsted–Lowry acid, or forming a covalent bond with an electron pair, known as a Lewis acid.IUPAC Gold Book - acid The first category of acids are the proton donors, or Brønsted–Lowry acids. In the special case of aqueous solutions, proton donors form the hydronium ion H3O+ and are known as Arrhenius acids. Brønsted and Lowry generalized the Arrhenius theory to include non-aqueous solvents. A Brønsted or Arrhenius acid usually contains a hydrogen atom bonded to a chemical structure that is still energetically favorable after loss of H+. Aqueous Arrhenius acids have characteristic properties that provide a practical description of an acid. Acids form aqueous solutions with a sour taste, can turn blue litmus red, and react with bases and certain metals (like calcium) to form salts. The word acid is derived from" +318,"or the gas phase. Hydrogen chloride (HCl) and ammonia combine under several different conditions to form ammonium chloride, NH4Cl. In aqueous solution HCl behaves as hydrochloric acid and exists as hydronium and chloride ions. The following reactions illustrate the limitations of Arrhenius's definition: # H3O + Cl + NH3 → Cl + NH(aq) + H2O # HCl(benzene) + NH3(benzene) → NH4Cl(s) # HCl(g) + NH3(g) → NH4Cl(s) As with the acetic acid reactions, both definitions work for the first example, where water is the solvent and hydronium ion is formed by the HCl solute. The next two reactions do not involve the formation of ions but are still proton-transfer reactions. In the second reaction hydrogen chloride and ammonia (dissolved in benzene) react to form solid ammonium chloride in a benzene solvent and in the third gaseous HCl and NH3 combine to form the solid. ===Lewis acids=== A third, only marginally related concept was proposed in 1923 by Gilbert N. Lewis, which includes reactions with aci" +319,"acid (HBr), perchloric acid (HClO4), nitric acid (HNO3) and sulfuric acid (H2SO4). In water each of these essentially ionizes 100%. The stronger an acid is, the more easily it loses a proton, H+. Two key factors that contribute to the ease of deprotonation are the polarity of the H—A bond and the size of atom A, which determines the strength of the H—A bond. Acid strengths are also often discussed in terms of the stability of the conjugate base. Stronger acids have a larger acid dissociation constant, Ka and a lower pKa than weaker acids. Sulfonic acids, which are organic oxyacids, are a class of strong acids. A common example is toluenesulfonic acid (tosylic acid). Unlike sulfuric acid itself, sulfonic acids can be solids. In fact, polystyrene functionalized into polystyrene sulfonate is a solid strongly acidic plastic that is filterable. Superacids are acids stronger than 100% sulfuric acid. Examples of superacids are fluoroantimonic acid, magic acid and perchloric acid. The stronges" +320,"hydrochloric acid and sodium hydroxide form sodium chloride and water: :HCl(aq) + NaOH(aq) → H2O(l) + NaCl(aq) Neutralization is the basis of titration, where a pH indicator shows equivalence point when the equivalent number of moles of a base have been added to an acid. It is often wrongly assumed that neutralization should result in a solution with pH 7.0, which is only the case with similar acid and base strengths during a reaction. Neutralization with a base weaker than the acid results in a weakly acidic salt. An example is the weakly acidic ammonium chloride, which is produced from the strong acid hydrogen chloride and the weak base ammonia. Conversely, neutralizing a weak acid with a strong base gives a weakly basic salt (e.g., sodium fluoride from hydrogen fluoride and sodium hydroxide). ===Weak acid–weak base equilibrium=== In order for a protonated acid to lose a proton, the pH of the system must rise above the pKa of the acid. The decreased concentration of H+ in that basic" +321,"of proteins required for growth and repair of body tissues. Fatty acids are also required for growth and repair of body tissues. Nucleic acids are important for the manufacturing of DNA and RNA and transmitting of traits to offspring through genes. Carbonic acid is important for maintenance of pH equilibrium in the body. Human bodies contain a variety of organic and inorganic compounds, among those dicarboxylic acids play an essential role in many biological behaviors. Many of those acids are amino acids, which mainly serve as materials for the synthesis of proteins. Other weak acids serve as buffers with their conjugate bases to keep the body's pH from undergoing large scale changes that would be harmful to cells. The rest of the dicarboxylic acids also participate in the synthesis of various biologically important compounds in human bodies. ===Acid catalysis=== Acids are used as catalysts in industrial and organic chemistry; for example, sulfuric acid is used in very large quantities" +322,"A carboxylic acid has the general formula R-C(O)OH, where R is an organic radical. The carboxyl group -C(O)OH contains a carbonyl group, C=O, and a hydroxyl group, O-H. * Acetic acid (CH3COOH) * Citric acid (C6H8O7) * Formic acid (HCOOH) * Gluconic acid HOCH2-(CHOH)4-COOH * Lactic acid (CH3-CHOH-COOH) * Oxalic acid (HOOC-COOH) * Tartaric acid (HOOC-CHOH-CHOH-COOH) ===Halogenated carboxylic acids=== Halogenation at alpha position increases acid strength, so that the following acids are all stronger than acetic acid. * Fluoroacetic acid * Trifluoroacetic acid * Chloroacetic acid * Dichloroacetic acid * Trichloroacetic acid ===Vinylogous carboxylic acids=== Normal carboxylic acids are the direct union of a carbonyl group and a hydroxyl group. In vinylogous carboxylic acids, a carbon-carbon double bond separates the carbonyl and hydroxyl groups. * Ascorbic acid ===Nucleic acids=== * Deoxyribonucleic acid (DNA) * Ribonucleic acid (RNA) ==References== * Listing of strengths of common acids a" +323,"thumb|Refined bitumen Bitumen ( , ) is an immensely viscous constituent of petroleum. Depending on its exact composition it can be a sticky, black liquid or an apparently solid mass that behaves as a liquid over very large time scales. In American English, the material is commonly referred to as asphalt. Whether found in natural deposits or refined from petroleum, the substance is classed as a pitch. Prior to the 20th century, the term asphaltum was in general use. The word derives from the Ancient Greek word (), which referred to natural bitumen or pitch. The largest natural deposit of bitumen in the world is the Pitch Lake of southwest Trinidad, which is estimated to contain 10 million tons. About 70% of annual bitumen production is destined for road construction, its primary use. In this application, bitumen is used to bind aggregate particles like gravel and forms a substance referred to as asphalt concrete, which is colloquially termed asphalt. Its other main uses lie in bituminou" +324,"(naphthalene), consisting of partially hydrogenated polycyclic aromatic compounds * Polar aromatics, consisting of high molecular weight phenols and carboxylic acids produced by partial oxidation of the material * Saturated hydrocarbons; the percentage of saturated compounds in asphalt correlates with its softening point * Asphaltenes, consisting of high molecular weight phenols and heterocyclic compounds Bitumen typically contains, elementally 80% by weight of carbon; 10% hydrogen; up to 6% sulfur; and molecularly, between 5 and 25% by weight of asphaltenes dispersed in 90% to 65% maltenes. Most natural bitumens also contain organosulfur compounds, Nickel and vanadium are found at <10 parts per million, as is typical of some petroleum. The substance is soluble in carbon disulfide. It is commonly modelled as a colloid, with asphaltenes as the dispersed phase and maltenes as the continuous phase. It is almost impossible to separate and identify all the different molecules of bitumen, be" +325,"time, and gently cooked into oil by geothermal heat at a temperature of . Due to pressure from the rising of the Rocky Mountains in southwestern Alberta, 80 to 55 million years ago, the oil was driven northeast hundreds of kilometres and trapped into underground sand deposits left behind by ancient river beds and ocean beaches, thus forming the oil sands. ==History== ===Paleolithic times=== Bitumen use goes back to the Middle Paleolithic, where it was shaped into tool handles or used as an adhesive for attaching stone tools to hafts. The earliest evidence of bitumen use was discovered when archeologists identified bitumen material on Levallois flint artefacts that date to about 71,000 years BP at the Umm el Tlel open-air site, located on the northern slope of the Qdeir Plateau in el Kowm Basin in Central Syria. Microscopic analyses found bituminous residue on two-thirds of the stone artefacts, suggesting that bitumen was an important and frequently-used component of tool making for peo" +326,"2000 One of the earliest uses in France was the laying of about 24,000 square yards of Seyssel asphalt at the Place de la Concorde in 1835. ===United Kingdom=== Among the earlier uses of bitumen in the United Kingdom was for etching. William Salmon's Polygraphice (1673) provides a recipe for varnish used in etching, consisting of three ounces of virgin wax, two ounces of mastic, and one ounce of asphaltum. By the fifth edition in 1685, he had included more asphaltum recipes from other sources. The first British patent for the use of asphalt was Cassell's patent asphalte or bitumen in 1834. Then on 25 November 1837, Richard Tappin Claridge patented the use of Seyssel asphalt (patent #7849), for use in asphalte pavement, Writer is replying to note or query from previous publication, cited as 9th S. xi. 30 having seen it employed in France and Belgium when visiting with Frederick Walter Simms, who worked with him on the introduction of asphalt to Britain. Dr T. Lamb Phipson writes that hi" +327,"or other stone blocks, or bricks. Unpaved roads produced uneven wear and hazards for pedestrians. In the late 19th century with the rise of the popular bicycle, bicycle clubs were important in pushing for more general pavement of streets. Advocacy for pavement increased in the early 20th century with the rise of the automobile. Asphalt gradually became an ever more common method of paving. St. Charles Avenue in New Orleans was paved its whole length with asphalt by 1889. In 1900, Manhattan alone had 130,000 horses, pulling streetcars, wagons, and carriages, and leaving their waste behind. They were not fast, and pedestrians could dodge and scramble their way across the crowded streets. Small towns continued to rely on dirt and gravel, but larger cities wanted much better streets. They looked to wood or granite blocks by the 1850s.David O. Whitten, A Century of Parquet Pavements: Wood as a Paving Material in the United States And Abroad, 1840–1940. Essays in Economic and Business Histor" +328,"involves mixing fine and coarse aggregates such as sand, gravel and crushed rock with asphalt, which acts as the binding agent. Other materials, such as recycled polymers (e.g., rubber tyres), may be added to the bitumen to modify its properties according to the application for which the bitumen is ultimately intended. A further 10% of global bitumen production is used in roofing applications, where its waterproofing qualities are invaluable. The remaining 5% of bitumen is used mainly for sealing and insulating purposes in a variety of building materials, such as pipe coatings, carpet tile backing and paint. Bitumen is applied in the construction and maintenance of many structures, systems, and components, such as: * Highways * Airport runways * Footways and pedestrian ways * Car parks * Racetracks * Tennis courts * Roofing * Damp proofing * Dams * Reservoir and pool linings * Soundproofing * Pipe coatings * Cable coatings * Paints * Building water proofing * Tile underlying waterproof" +329,"resulting shear forces generate bitumen particles between 5 μm and 10 μm coated with emulsifiers. The High Internal Phase Ratio (HIPR) method is used for creating smaller bitumen particles, monomodal, narrow particle size distributions, and very high bitumen concentrations. Here, a highly concentrated bitumen emulsion is produced first by moderate stirring and diluted afterward. In contrast to the Colloid-Mill method, the aqueous phase is introduced into hot bitumen, enabling very high bitumen concentrations. T The High Internal Phase Ratio (HIPR) method is used for creating smaller bitumen particles, monomodal, narrow particle size distributions, and very high bitumen concentrations. Here, a highly concentrated bitumen emulsion is produced first by moderate stirring and diluted afterward. In contrast to the Colloid-Mill method, the aqueous phase is introduced into hot bitumen, enabling very high bitumen concentrations (1).he High Internal Phase Ratio (HIPR) method is used for creating" +330,"of radioactive concentrates (No. CEA-R3742). CEA Marcoule. Bituminised radioactive waste containing highly radiotoxic alpha-emitting transuranic elements from nuclear reprocessing plants have been produced at industrial scale in France, Belgium and Japan, but this type of waste conditioning has been abandoned because operational safety issues (risks of fire, as occurred in a bituminisation plant at Tokai Works in Japan)Sato, Y., Miura, A., Kato, Y., Suzuki, H., Shigetome, Y., Koyama, T., ... & Yamanouchi, T. (2000). Study on the cause of the fire and explosion incident at Bituminization Demonstration Facility of PNC Tokai Works. In Nuclear waste: from research to industrial maturity. International conference (pp. 179–190).Okada, K., Nur, R. M., & Fujii, Y. (1999). The formation of explosive compounds in bitumen/nitrate mixtures. Journal of hazardous materials, 69(3), 245–256. and long-term stability problems related to their geological disposal in deep rock formations. One of the main" +331,"rate of would last about 200 years. ===Alternatives and bioasphalt=== Although uncompetitive economically, bitumen can be made from nonpetroleum-based renewable resources such as sugar, molasses and rice, corn and potato starches. Bitumen can also be made from waste material by fractional distillation of used motor oil, which is sometimes otherwise disposed of by burning or dumping into landfills. Use of motor oil may cause premature cracking in colder climates, resulting in roads that need to be repaved more frequently. Nonpetroleum-based asphalt binders can be made light-colored. Lighter-colored roads absorb less heat from solar radiation, reducing their contribution to the urban heat island effect.Heat Island Effect. From the website of the US Environmental Protection Agency. Parking lots that use bitumen alternatives are called green parking lots. ===Albanian deposits=== Selenizza is a naturally occurring solid hydrocarbon bitumen found in native deposits in Selenice, in Albania, t" +332,"asphalt fumes as a Class 2B possible carcinogen, indicating inadequate evidence of carcinogenicity in humans. In 2020, scientists reported that bitumen currently is a significant and largely overlooked source of air pollution in urban areas, especially during hot and sunny periods. A bitumen-like substance found in the Himalayas and known as shilajit is sometimes used as an Ayurveda medicine, but is not in fact a tar, resin or bitumen. ==See also== * Asphalt plant * Asphaltene * Bioasphalt * Bitumen-based fuel * Bituminous coal * Bituminous rocks * Blacktop * Cariphalte * Duxit * Macadam * Oil sands * Pitch drop experiment * Pitch (resin) * Road surface * Tar * Tarmac * Sealcoat * Stamped asphalt ==Notes== ==References== ===Sources=== * . * * ==External links== * * * * Pavement Interactive – Asphalt * CSU Sacramento, The World Famous Asphalt Museum! * National Institute for Occupational Safety and Health – Asphalt Fumes *Scientific American, Asphalt , 20-Aug-1881, pp. 121 Category:Amor" +333,"The American National Standards Institute (ANSI ) is a private nonprofit organization that oversees the development of voluntary consensus standards for products, services, processes, systems, and personnel in the United States. The organization also coordinates U.S. standards with international standards so that American products can be used worldwide. ANSI accredits standards that are developed by representatives of other standards organizations, government agencies, consumer groups, companies, and others. These standards ensure that the characteristics and performance of products are consistent, that people use the same definitions and terms, and that products are tested the same way. ANSI also accredits organizations that carry out product or personnel certification in accordance with requirements defined in international standards.ANSI 2009 Annual Report The organization's headquarters are in Washington, D.C. ANSI's operations office is located in New York City. The ANSI annual op" +334,"nuclear industry. ===American national standards=== * The ASA (as for American Standards Association) photographic exposure system, originally defined in ASA Z38.2.1 (since 1943) and ASA PH2.5 (since 1954), together with the DIN system (DIN 4512 since 1934), became the basis for the ISO system (since 1974), currently used worldwide (ISO 6, ISO 2240, ISO 5800, ISO 12232). * A standard for the set of values used to represent characters in digital computers. The ANSI code standard extended the previously created ASCII seven bit code standard (ASA X3.4-1963), with additional codes for European alphabets (see also Extended Binary Coded Decimal Interchange Code or EBCDIC). In Microsoft Windows, the phrase ANSI refers to the Windows ANSI code pages (even though they are not ANSI standards). Most of these are fixed width, though some characters for ideographic languages are variable width. Since these characters are based on a draft of the ISO-8859 series, some of Microsoft's symbols are visua" +335,"In logic and philosophy, an argument is an attempt to persuade someone of something, or give evidence or reasons for accepting a particular conclusion. Argument may also refer to: ==Mathematics and computer science== *Argument (complex analysis), a function which returns the polar angle of a complex number *Command-line argument, an item of information provided to a program when it is started *Parameter (computer programming), a piece of data provided as input to a subroutine *Argument principle, a theorem in complex analysis *An argument of a function, also known as an independent variable ==Language and rhetoric== *Argument (literature), a brief summary, often in prose, of a poem or section of a poem or other work *Argument (linguistics), a phrase that appears in a syntactic relationship with the verb in a clause *Oral argument in the United States, a spoken presentation to a judge or appellate court by a lawyer (or parties when representing themselves) of the legal reasons why they" +336,"Apollo 11 was a spaceflight conducted by the United States from July 16 to July 24, 1969. It marked the first time in history that humans landed on the Moon. Commander Neil Armstrong and Lunar Module Pilot Buzz Aldrin landed the Apollo Lunar Module Eagle on July 20, 1969, at 20:17 UTC, and Armstrong became the first person to step onto the Moon's surface six hours and 39 minutes later, on July 21 at 02:56 UTC. Aldrin joined him 19 minutes later, and they spent about two and a quarter hours together exploring the site they had named Tranquility Base upon landing. Armstrong and Aldrin collected of lunar material to bring back to Earth as pilot Michael Collins flew the Command Module Columbia in lunar orbit, and were on the Moon's surface for 21 hours, 36 minutes, before lifting off to rejoin Columbia. Apollo 11 was launched by a Saturn V rocket from Kennedy Space Center on Merritt Island, Florida, on July 16 at 13:32 UTC, and it was the fifth crewed mission of NASA's Apollo program. The" +337,"(IC) chips in the Apollo Guidance Computer (AGC). Project Apollo was abruptly halted by the Apollo 1 fire on January 27, 1967, in which astronauts Gus Grissom, Ed White, and Roger B. Chaffee died, and the subsequent investigation. In October 1968, Apollo 7 evaluated the command module in Earth orbit, and in December Apollo 8 tested it in lunar orbit. In March 1969, Apollo 9 put the lunar module through its paces in Earth orbit, and in May Apollo 10 conducted a dress rehearsal in lunar orbit. By July 1969, all was in readiness for Apollo 11 to take the final step onto the Moon. The Soviet Union appeared to be winning the Space Race by beating the US to firsts, but its early lead was overtaken by the US Gemini program and Soviet failure to develop the N1 launcher, which would have been comparable to the Saturn V. The Soviets tried to beat the US to return lunar material to the Earth by means of uncrewed probes. On July 13, three days before Apollo 11's launch, the Soviet Union launched L" +338,"pads and infrastructure Jamye Flowers Secretary for astronauts Eleanor Foraker Tailor who designed space suits Jack Garman Computer engineer and technician Millicent Goldschmidt Microbiologist who designed aseptic lunar material collection techniques and trained astronauts Eldon C. Hall Apollo Guidance Computer hardware designer Margaret Hamilton Onboard flight computer software engineer John Houbolt Route planner Gene Shoemaker Geologist who trained astronauts in field geology Bill Tindall Coordinated mission techniques == Preparations == === Insignia === The Apollo 11 mission emblem was designed by Collins, who wanted a symbol for peaceful lunar landing by the United States . At Lovell's suggestion, he chose the bald eagle, the national bird of the United States, as the symbol. Tom Wilson, a simulator instructor, suggested an olive branch in its beak to represent their peaceful mission. Collins added a lunar background with the Earth in the distance. The sunlight in the image was com" +339,"you gentlemen will be the first man to step onto the lunar surface? Slayton told the reporter it had not been decided, and Armstrong added that it was not based on individual desire . One of the first versions of the egress checklist had the lunar module pilot exit the spacecraft before the commander, which matched what had been done on Gemini missions, where the commander had never performed the spacewalk. Reporters wrote in early 1969 that Aldrin would be the first man to walk on the Moon, and Associate Administrator George Mueller told reporters he would be first as well. Aldrin heard that Armstrong would be the first because Armstrong was a civilian, which made Aldrin livid. Aldrin attempted to persuade other lunar module pilots he should be first, but they responded cynically about what they perceived as a lobbying campaign. Attempting to stem interdepartmental conflict, Slayton told Aldrin that Armstrong would be first since he was the commander. The decision was announced in a p" +340,"about 9 minutes and 8 seconds, allowing the first ignition of the S-IVB engine a few seconds later. Apollo 11 entered a near-circular Earth orbit at an altitude of by , twelve minutes into its flight. After one and a half orbits, a second ignition of the S-IVB engine pushed the spacecraft onto its trajectory toward the Moon with the trans-lunar injection (TLI) burn at 16:22:13 UTC. About 30 minutes later, with Collins in the left seat and at the controls, the transposition, docking, and extraction maneuver was performed. This involved separating Columbia from the spent S-IVB stage, turning around, and docking with Eagle still attached to the stage. After the LM was extracted, the combined spacecraft headed for the Moon, while the rocket stage flew on a trajectory past the Moon. This was done to avoid the third stage colliding with the spacecraft, the Earth, or the Moon. A slingshot effect from passing around the Moon threw it into an orbit around the Sun. On July 19 at 17:21:50 UTC, Ap" +341,"the turn indicator in a car. Address 413 of the Abort Guidance System (AGS) contained the variable that indicated the LM had landed. Eagle landed at 20:17:40 UTC on Sunday July 20 with of usable fuel remaining. Information available to the crew and mission controllers during the landing showed the LM had enough fuel for another 25 seconds of powered flight before an abort without touchdown would have become unsafe, but post-mission analysis showed that the real figure was probably closer to 50 seconds. Apollo 11 landed with less fuel than most subsequent missions, and the astronauts encountered a premature low fuel warning. This was later found to be the result of the propellant sloshing more than expected, uncovering a fuel sensor. On subsequent missions, extra anti-slosh baffles were added to the tanks to prevent this. Armstrong acknowledged Aldrin's completion of the post-landing checklist with Engine arm is off , before responding to the CAPCOM, Charles Duke, with the words, Housto" +342,"view with the simple phrase: Magnificent desolation. Armstrong said moving in the lunar gravity, one-sixth of Earth's, was even perhaps easier than the simulations ... It's absolutely no trouble to walk around. Aldrin joined him on the surface and tested methods for moving around, including two-footed kangaroo hops. The PLSS backpack created a tendency to tip backward, but neither astronaut had serious problems maintaining balance. Loping became the preferred method of movement. The astronauts reported that they needed to plan their movements six or seven steps ahead. The fine soil was quite slippery. Aldrin remarked that moving from sunlight into Eagle shadow produced no temperature change inside the suit, but the helmet was warmer in sunlight, so he felt cooler in shadow. The MESA failed to provide a stable work platform and was in shadow, slowing work somewhat. As they worked, the moonwalkers kicked up gray dust, which soiled the outer part of their suits. The astronauts planted the" +343,"hours on the lunar surface, in addition to the scientific instruments, the astronauts left behind: an Apollo 1 mission patch in memory of astronauts Roger Chaffee, Gus Grissom, and Edward White, who died when their command module caught fire during a test in January 1967; two memorial medals of Soviet cosmonauts Vladimir Komarov and Yuri Gagarin, who died in 1967 and 1968 respectively; a memorial bag containing a gold replica of an olive branch as a traditional symbol of peace; and a silicon message disk carrying the goodwill statements by presidents Eisenhower, Kennedy, Johnson, and Nixon along with messages from leaders of 73 countries around the world. The disk also carries a listing of the leadership of the US Congress, a listing of members of the four committees of the House and Senate responsible for the NASA legislation, and the names of NASA's past and then-current top management. After about seven hours of rest, the crew was awakened by Houston to prepare for the return flight" +344,"Apollo, a 35-man NASA recovery team, and about 120 media representatives. To make room, most of Hornets air wing was left behind in Long Beach. Special recovery equipment was also loaded, including a boilerplate command module used for training. On July 12, with Apollo 11 still on the launch pad, Hornet departed Pearl Harbor for the recovery area in the central Pacific, in the vicinity of . A presidential party consisting of Nixon, Borman, Secretary of State William P. Rogers and National Security Advisor Henry Kissinger flew to Johnston Atoll on Air Force One, then to the command ship USS Arlington in Marine One. After a night on board, they would fly to Hornet in Marine One for a few hours of ceremonies. On arrival aboard Hornet, the party was greeted by the Commander-in-Chief, Pacific Command (CINCPAC), Admiral John S. McCain Jr., and NASA Administrator Thomas O. Paine, who flew to Hornet from Pago Pago in one of Hornets carrier onboard delivery aircraft. Weather satellites were not" +345,"=== Celebrations === On August 13, the three astronauts rode in ticker-tape parades in their honor in New York and Chicago, with an estimated six million attendees. On the same evening in Los Angeles there was an official state dinner to celebrate the flight, attended by members of Congress, 44 governors, Chief Justice of the United States Warren E. Burger and his predecessor, Earl Warren, and ambassadors from 83 nations at the Century Plaza Hotel. Nixon and Agnew honored each astronaut with a presentation of the Presidential Medal of Freedom. The three astronauts spoke before a joint session of Congress on September 16, 1969. They presented two US flags, one to the House of Representatives and the other to the Senate, that they had carried with them to the surface of the Moon. The flag of American Samoa on Apollo 11 is on display at the Jean P. Haydon Museum in Pago Pago, the capital of American Samoa. This celebration began a 38-day world tour that brought the astronauts to 22 countr" +346,"pioneering flight vehicles such as the Wright Flyer, Spirit of St. Louis, Bell X-1, North American X-15 and Friendship 7. Columbia was moved in 2017 to the NASM Mary Baker Engen Restoration Hangar at the Steven F. Udvar-Hazy Center in Chantilly, Virginia, to be readied for a four-city tour titled Destination Moon: The Apollo 11 Mission. This included Space Center Houston from October 14, 2017, to March 18, 2018, the Saint Louis Science Center from April 14 to September 3, 2018, the Senator John Heinz History Center in Pittsburgh from September 29, 2018, to February 18, 2019, and its last location at Museum of Flight in Seattle from March 16 to September 2, 2019. Continued renovations at the Smithsonian allowed time for an additional stop for the capsule, and it was moved to the Cincinnati Museum Center. The ribbon cutting ceremony was on September 29, 2019. For 40 years Armstrong's and Aldrin's space suits were displayed in the museum's Apollo to the Moon exhibit, until it permanently" +347,"generation of kids out there who are looking up at the sky and are going to be the next Armstrong, Collins, and Aldrin , Obama said. We want to make sure that NASA is going to be there for them when they want to take their journey. On August 7, 2009, an act of Congress awarded the three astronauts a Congressional Gold Medal, the highest civilian award in the United States. The bill was sponsored by Florida Senator Bill Nelson and Florida Representative Alan Grayson. A group of British scientists interviewed as part of the anniversary events reflected on the significance of the Moon landing: ==== 50th anniversary ==== On June 10, 2015, Congressman Bill Posey introduced resolution H.R. 2726 to the 114th session of the United States House of Representatives directing the United States Mint to design and sell commemorative coins in gold, silver and clad for the 50th anniversary of the Apollo 11 mission. On January 24, 2019, the Mint released the Apollo 11 Fiftieth Anniversary commemorative" +348,"Apollo 8 (December 21–27, 1968) was the first crewed spacecraft to leave Earth's gravitational sphere of influence, and the first human spaceflight to reach the Moon. The crew orbited the Moon ten times without landing and then returned to Earth. The three astronauts—Frank Borman, James Lovell, and William Anders—were the first humans to see and photograph the far side of the Moon and an Earthrise. Apollo 8 launched on December 21, 1968, and was the second crewed spaceflight mission flown in the United States Apollo space program (the first, Apollo7, stayed in Earth orbit). Apollo8 was the third flight and the first crewed launch of the Saturn V rocket. It was the first human spaceflight from the Kennedy Space Center, adjacent to Cape Kennedy Air Force Station in Florida. Originally planned as the second crewed Apollo Lunar Module and command module test, to be flown in an elliptical medium Earth orbit in early 1969, the mission profile was changed in August 1968 to a more ambitious co" +349,"flight plan, checklists, and mission ground rules, and ensured that the prime and backup crews were apprised of any changes. The support crew developed procedures in the simulators, especially those for emergency situations, so that the prime and backup crews could practice and master them in their simulator training. For Apollo8, the support crew consisted of Ken Mattingly, Vance Brand, and Gerald Carr. The capsule communicator (CAPCOM) was an astronaut at the Mission Control Center in Houston, Texas, who was the only person who communicated directly with the flight crew. For Apollo8, the CAPCOMs were Michael Collins, Gerald Carr, Ken Mattingly, Neil Armstrong, Buzz Aldrin, Vance Brand, and Fred Haise. The mission control teams rotated in three shifts, each led by a flight director. The directors for Apollo8 were Clifford E. Charlesworth (Green team), Glynn Lunney (Black team), and Milton Windler (Maroon team). ===Mission insignia and callsign=== The triangular shape of the insignia r" +350,"LMP on a flight with no LM. Instead, Apollo8 would carry the LM test article, a boilerplate model that would simulate the correct weight and balance of LM-3. Added pressure on the Apollo program to make its 1969 landing goal was provided by the Soviet Union's Zond5 mission, which flew some living creatures, including Russian tortoises, in a cislunar loop around the Moon and returned them to Earth on September 21. There was speculation within NASA and the press that they might be preparing to launch cosmonauts on a similar circumlunar mission before the end of 1968. Compounding these concerns, American reconnaissance satellites observed a mockup N1 being rolled to the pad at Baikonur on November 25, 1967. The Apollo 8 crew, now living in the crew quarters at Kennedy Space Center, received a visit from Charles Lindbergh and his wife, Anne Morrow Lindbergh, the night before the launch. They talked about how, before his 1927 flight, Lindbergh had used a piece of string to measure the dista" +351,"second stage landed at . The S-IVB third stage injected the craft into Earth orbit and remained attached to perform the TLI burn that would put the spacecraft on a trajectory to the Moon. Once the vehicle reached Earth orbit, both the crew and Houston flight controllers spent the next 2hours and 38 minutes checking that the spacecraft was in proper working order and ready for TLI. The proper operation of the S-IVB third stage of the rocket was crucial, and in the last uncrewed test, it had failed to reignite for this burn. Collins was the first CAPCOM on duty, and at 2hours, 27 minutes and 22 seconds after launch he radioed, Apollo8. You are Go for TLI. This communication meant that Mission Control had given official permission for Apollo8 to go to the Moon. The S-IVB engine ignited on time and performed the TLI burn perfectly. Over the next five minutes, the spacecraft's speed increased from . After the S-IVB had placed the mission on course for the Moon, the command and service modul" +352,"did not want everyone to know about his medical problems, but Lovell and Anders wanted to inform Mission Control. The crew decided to use the Data Storage Equipment (DSE), which could tape voice recordings and telemetry and dump them to Mission Control at high speed. After recording a description of Borman's illness they asked Mission Control to check the recording, stating that they would like an evaluation of the voice comments . The Apollo 8 crew and Mission Control medical personnel held a conference using an unoccupied second-floor control room (there were two identical control rooms in Houston, on the second and third floors, only one of which was used during a mission). The conference participants concluded that there was little to worry about and that Borman's illness was either a 24-hour flu, as Borman thought, or a reaction to the sleeping pill. Researchers now believe that he was suffering from space adaptation syndrome, which affects about a third of astronauts during their" +353,"darn thing anybody here can do about it until we hear from them With ten minutes remaining before LOI-1, the crew began one last check of the spacecraft systems and made sure that every switch was in its correct position. At that time, they finally got their first glimpses of the Moon. They had been flying over the unlit side, and it was Lovell who saw the first shafts of sunlight obliquely illuminating the lunar surface. The LOI burn was only two minutes away, so the crew had little time to appreciate the view. ===Lunar orbit=== The SPS was ignited at 69 hours, 8minutes, and 16 seconds after launch and burned for 4minutes and 7seconds, placing the Apollo8 spacecraft in orbit around the Moon. The crew described the burn as being the longest four minutes of their lives. If the burn had not lasted exactly the correct amount of time, the spacecraft could have ended up in a highly elliptical lunar orbit or even been flung off into space. If it had lasted too long, they could have struck th" +354,"what it was like to be orbiting the Moon. Borman described it as being a vast, lonely, forbidding expanse of nothing . Then, after talking about what they were flying over, Anders said that the crew had a message for all those on Earth. Each man on board read a section from the Biblical creation story from the Book of Genesis. Borman finished the broadcast by wishing a Merry Christmas to everyone on Earth. His message appeared to sum up the feelings that all three crewmen had from their vantage point in lunar orbit. Borman said, And from the crew of Apollo8, we close with good night, good luck, a Merry Christmas and God bless all of you—all of you on the good Earth. Ch.20-9. The only task left for the crew at this point was to perform the trans-Earth injection (TEI), which was scheduled for hours after the end of the television transmission. The TEI was the most critical burn of the flight, as any failure of the SPS to ignite would strand the crew in lunar orbit, with little hope of es" +355,"survived a mission that even the crew themselves had rated as having only a fifty-fifty chance of fully succeeding. The effect of Apollo8 was summed up in a telegram from a stranger, received by Borman after the mission, that stated simply, Thank you Apollo8. You saved 1968. One of the most famous aspects of the flight was the Earthrise picture that the crew took as they came around for their fourth orbit of the Moon. This was the first time that humans had taken such a picture while actually behind the camera, and it has been credited as one of the inspirations of the first Earth Day in 1970. It was selected as the first of Life magazine's 100 Photographs That Changed the World. Apollo 11 astronaut Michael Collins said, Eight's momentous historic significance was foremost ; while space historian Robert K. Poole saw Apollo8 as the most historically significant of all the Apollo missions. The mission was the most widely covered by the media since the first American orbital flight, Mercu" +356,"An astronaut (from the Ancient Greek (), meaning 'star', and (), meaning 'sailor') is a person trained, equipped, and deployed by a human spaceflight program to serve as a commander or crew member aboard a spacecraft. Although generally reserved for professional space travelers, the term is sometimes applied to anyone who travels into space, including scientists, politicians, journalists, and tourists. Astronaut technically applies to all human space travelers regardless of nationality. However, astronauts fielded by Russia or the Soviet Union are typically known instead as cosmonauts (from the Russian kosmos (космос), meaning space , also borrowed from Greek ). Comparatively recent developments in crewed spaceflight made by China have led to the rise of the term taikonaut (from the Mandarin tàikōng (), meaning space ), although its use is somewhat informal and its origin is unclear. In China, the People's Liberation Army Astronaut Corps astronauts and their foreign counterparts are al" +357,"in Hong Kong and Taiwan. The term taikonaut is used by some English-language news media organizations for professional space travelers from China. The word has featured in the Longman and Oxford English dictionaries, and the term became more common in 2003 when China sent its first astronaut Yang Liwei into space aboard the Shenzhou 5 spacecraft. This is the term used by Xinhua News Agency in the English version of the Chinese People's Daily since the advent of the Chinese space program. The origin of the term is unclear; as early as May 1998, Chiew Lee Yih () from Malaysia used it in newsgroups. ===Parastronaut=== For its 2022 Astronaut Group, the European Space Agency envisioned recruiting an astronaut with a physical disability, a category they called parastronauts , with the intention but not guarantee of spaceflight. The categories of disability considered for the program were individuals with lower limb deficiency (either through amputation or congenital), leg length difference," +358,"fly in space, and in 1983, Guion Bluford became the first African American to fly into space. In April 1985, Taylor Wang became the first ethnic Chinese person in space. The first person born in Africa to fly in space was Patrick Baudry (France), in 1985. In 1985, Saudi Arabian Prince Sultan Bin Salman Bin AbdulAziz Al-Saud became the first Arab Muslim astronaut in space. In 1988, Abdul Ahad Mohmand became the first Afghan to reach space, spending nine days aboard the Mir space station. With the increase of seats on the Space Shuttle, the U.S. began taking international astronauts. In 1983, Ulf Merbold of West Germany became the first non-US citizen to fly in a US spacecraft. In 1984, Marc Garneau became the first of eight Canadian astronauts to fly in space (through 2010). In 1985, Rodolfo Neri Vela became the first Mexican-born person in space. In 1991, Helen Sharman became the first Briton to fly in space. In 2002, Mark Shuttleworth became the first citizen of an African country to" +359,"also where the Shuttle Training Aircraft is maintained and developed, although most flights of the aircraft are conducted from Edwards Air Force Base. Astronauts in training must learn how to control and fly the Space Shuttle; further, it is vital that they are familiar with the International Space Station so they know what they must do when they get there. ===NASA candidacy requirements=== * The candidate must be a citizen of the United States. * The candidate must complete a master's degree in a STEM field, including engineering, biological science, physical science, computer science or mathematics. * The candidate must have at least two years of related professional experience obtained after degree completion or at least 1,000 hours pilot-in-command time on jet aircraft. * The candidate must be able to pass the NASA long-duration flight astronaut physical. * The candidate must also have skills in leadership, teamwork and communications. The master's degree requirement can also be me" +360,"analyzed for nutritional content by the shuttle dietician. Foods are tested to see how they will react in a reduced gravity environment. Caloric requirements are determined using a basal energy expenditure (BEE) formula. On Earth, the average American uses about of water every day. On board the ISS astronauts limit water use to only about per day. ==Insignia== In Russia, cosmonauts are awarded Pilot-Cosmonaut of the Russian Federation upon completion of their missions, often accompanied with the award of Hero of the Russian Federation. This follows the practice established in the USSR where cosmonauts were usually awarded the title Hero of the Soviet Union. At NASA, those who complete astronaut candidate training receive a silver lapel pin. Once they have flown in space, they receive a gold pin. U.S. astronauts who also have active-duty military status receive a special qualification badge, known as the Astronaut Badge, after participation on a spaceflight. The United States Air Force" +361,"A Modest Proposal for Preventing the Children of Poor People from Being a Burthen to Their Parents or Country, and for Making Them Beneficial to the Publick, commonly referred to as A Modest Proposal, is a Juvenalian satirical essay written and published anonymously by Anglo-Irish writer and clergyman Jonathan Swift in 1729. The essay suggests that poor people in Ireland could ease their economic troubles by selling their children as food to the elite. Swift's use of satirical hyperbole was intended to mock hostile attitudes towards the poor and anti-Catholicism among the Protestant Ascendancy as well as the Dublin Castle administration's policies in general.Swift notes that the number of Popish infants, is at least three to one in this kingdom, and therefore it will have one another collateral advantage, by lessening the number of Papists among us. In English writing, the phrase a modest proposal is now conventionally an allusion to this style of straight-faced satire. ==Synopsis== Sw" +362,"believes that Swift saw major similarities between the two situations.Johnson, Tertullian and A Modest Proposal, p. 563 Johnson notes Swift's obvious affinity for Tertullian and the bold stylistic and structural similarities between the works A Modest Proposal and Apology.Johnson, Tertullian and A Modest Proposal, p. 562 In structure, Johnson points out the same central theme, that of cannibalism and the eating of babies as well as the same final argument, that human depravity is such that men will attempt to justify their own cruelty by accusing their victims of being lower than human . Stylistically, Swift and Tertullian share the same command of sarcasm and language. In agreement with Johnson, Donald C. Baker points out the similarity between both authors' tones and use of irony. Baker notes the uncanny way that both authors imply an ironic justification by ownership over the subject of sacrificing children—Tertullian while attacking pagan parents, and Swift while attacking the mist" +363,"within Georgian society after its publication. The work was aimed at the elite, and they responded in turn. Several prominent members of society wrote to Swift regarding the work. Lord Bathurst's letter (12 February 1729–30) intimated that he certainly understood the message, and interpreted it as a work of comedy: ==Modern usage== A Modest Proposal is included in many literature courses as an example of early modern western satire. It also serves as an introduction to the concept and use of argumentative language, lending itself to secondary and post-secondary essay courses. Outside of the realm of English studies, A Modest Proposal is included in many comparative and global literature and history courses, as well as those of numerous other disciplines in the arts, humanities, and even the social sciences. A Modest Video Game Proposal is the title of an open letter sent by activist/former attorney Jack Thompson on 10 October 2005. The 2012 horror film Butcher Boys, written by the orig" +364,"|- ! colspan=2 style= text-align:left; | ↓ Period |- ! 2 | |- ! 3 | |- ! 4 | |- ! 5 | |- ! 6 | |- ! 7 | |- | colspan= 2 | ---- Legend primordial element by radioactive decay |} The alkali metals consist of the chemical elements lithium (Li), sodium (Na), potassium (K),The symbols Na and K for sodium and potassium are derived from their Latin names, natrium and kalium; these are still the origins of the names for the elements in some languages, such as German and Russian. rubidium (Rb), caesium (Cs), and francium (Fr). Together with hydrogen they constitute group 1, which lies in the s-block of the periodic table. All alkali metals have their outermost electron in an s-orbital: this shared electron configuration results in their having very similar characteristic properties. Indeed, the alkali metals provide the best example of group trends in properties in the periodic table, with elements exhibiting well-characterised homologous behaviour. This family of elements is also known as the" +365,"lines in their emission spectra: a bright red line for rubidium (from the Latin word rubidus, meaning dark red or bright red), and a sky-blue line for caesium (derived from the Latin word caesius, meaning sky-blue). Around 1865 John Newlands produced a series of papers where he listed the elements in order of increasing atomic weight and similar physical and chemical properties that recurred at intervals of eight; he likened such periodicity to the octaves of music, where notes an octave apart have similar musical functions. His version put all the alkali metals then known (lithium to caesium), as well as copper, silver, and thallium (which show the +1 oxidation state characteristic of the alkali metals), together into a group. His table placed hydrogen with the halogens. After 1869, Dmitri Mendeleev proposed his periodic table placing lithium at the top of a group with sodium, potassium, rubidium, caesium, and thallium. Two years later, Mendeleev revised his table, placing hydrogen in" +366,"In turn, the natural history of the Earth caused parts of this planet to have differing concentrations of the elements. The mass of the Earth is approximately 5.98 kg. It is composed mostly of iron (32.1%), oxygen (30.1%), silicon (15.1%), magnesium (13.9%), sulfur (2.9%), nickel (1.8%), calcium (1.5%), and aluminium (1.4%); with the remaining 1.2% consisting of trace amounts of other elements. Due to planetary differentiation, the core region is believed to be primarily composed of iron (88.8%), with smaller amounts of nickel (5.8%), sulfur (4.5%), and less than 1% trace elements. The alkali metals, due to their high reactivity, do not occur naturally in pure form in nature. They are lithophiles and therefore remain close to the Earth's surface because they combine readily with oxygen and so associate strongly with silica, forming relatively low-density minerals that do not sink down into the Earth's core. Potassium, rubidium and caesium are also incompatible elements due to their lar" +367,"−2.98 −3.03 ? Flame test colourPrincipal emission/absorption wavelength (nm) Crimson670.8 Yellow589.2 Violet766.5 Red-violet780.0 Blue455.5 ? The alkali metals are more similar to each other than the elements in any other group are to each other. Indeed, the similarity is so great that it is quite difficult to separate potassium, rubidium, and caesium, due to their similar ionic radii; lithium and sodium are more distinct. For instance, when moving down the table, all known alkali metals show increasing atomic radius, decreasing electronegativity, increasing reactivity, and decreasing melting and boiling points as well as heats of fusion and vaporisation. In general, their densities increase when moving down the table, with the exception that potassium is less dense than sodium. One of the very few properties of the alkali metals that does not display a very smooth trend is their reduction potentials: lithium's value is anomalous, being more negative than the others. This is because th" +368,"of the rest of the group as the small Li+ cation polarises anions and gives its compounds a more covalent character. Lithium and magnesium have a diagonal relationship due to their similar atomic radii, so that they show some similarities. For example, lithium forms a stable nitride, a property common among all the alkaline earth metals (magnesium's group) but unique among the alkali metals. In addition, among their respective groups, only lithium and magnesium form organometallic compounds with significant covalent character (e.g. LiMe and MgMe2). Lithium fluoride is the only alkali metal halide that is poorly soluble in water, and lithium hydroxide is the only alkali metal hydroxide that is not deliquescent. Conversely, lithium perchlorate and other lithium salts with large anions that cannot be polarised are much more stable than the analogous compounds of the other alkali metals, probably because Li+ has a high solvation energy. This effect also means that most simple lithium salts" +369,"the Goiânia accident and the Chernobyl disaster. As of 2005, caesium-137 is the principal source of radiation in the zone of alienation around the Chernobyl nuclear power plant. Its chemical properties as one of the alkali metals make it one of the most problematic of the short-to-medium-lifetime fission products because it easily moves and spreads in nature due to the high water solubility of its salts, and is taken up by the body, which mistakes it for its essential congeners sodium and potassium. == Periodic trends == The alkali metals are more similar to each other than the elements in any other group are to each other. For instance, when moving down the table, all known alkali metals show increasing atomic radius, decreasing electronegativity, increasing reactivity, and decreasing melting and boiling points as well as heats of fusion and vaporisation. In general, their densities increase when moving down the table, with the exception that potassium is less dense than sodium. === A" +370,"deliquescent. === Melting and boiling points === The melting point of a substance is the point where it changes state from solid to liquid while the boiling point of a substance (in liquid state) is the point where the vapour pressure of the liquid equals the environmental pressure surrounding the liquid Section 17.43, page 321 and all the liquid changes state to gas. As a metal is heated to its melting point, the metallic bonds keeping the atoms in place weaken so that the atoms can move around, and the metallic bonds eventually break completely at the metal's boiling point. Therefore, the falling melting and boiling points of the alkali metals indicate that the strength of the metallic bonds of the alkali metals decreases down the group. This is because metal atoms are held together by the electromagnetic attraction from the positive ions to the delocalised electrons. As the atoms increase in size going down the group (because their atomic radius increases), the nuclei of the ions mo" +371,"and 12% potassium has the lowest known melting point of any metal or alloy, −78 °C. === Compounds with the group 13 elements === The intermetallic compounds of the alkali metals with the heavier group 13 elements (aluminium, gallium, indium, and thallium), such as NaTl, are poor conductors or semiconductors, unlike the normal alloys with the preceding elements, implying that the alkali metal involved has lost an electron to the Zintl anions involved. Nevertheless, while the elements in group 14 and beyond tend to form discrete anionic clusters, group 13 elements tend to form polymeric ions with the alkali metal cations located between the giant ionic lattice. For example, NaTl consists of a polymeric anion (—Tl−—)n with a covalent diamond cubic structure with Na+ ions located between the anionic lattice. The larger alkali metals cannot fit similarly into an anionic lattice and tend to force the heavier group 13 elements to form anionic clusters.S.M. Kauzlarich, Encyclopedia of Inorgani" +372,"and Pn represents a pnictogen – phosphorus, arsenic, antimony, or bismuth). This is due to the greater size of the P3− and As3− ions, so that less lattice energy needs to be released for the salts to form. These are not the only phosphides and arsenides of the alkali metals: for example, potassium has nine different known phosphides, with formulae K3P, K4P3, K5P4, KP, K4P6, K3P7, K3P11, KP10.3, and KP15.H.G. Von Schnering, W. Hönle Phosphides – Solid-state Chemistry Encyclopedia of Inorganic Chemistry Ed. R. Bruce King (1994) John Wiley & Sons While most metals form arsenides, only the alkali and alkaline earth metals form mostly ionic arsenides. The structure of Na3As is complex with unusually short Na–Na distances of 328–330 pm which are shorter than in sodium metal, and this indicates that even with these electropositive metals the bonding cannot be straightforwardly ionic. Other alkali metal arsenides not conforming to the formula M3As are known, such as LiAs, which has a metallic" +373,"of the great instability and rarity of astatine and francium. The most well-known of the twenty is certainly sodium chloride, otherwise known as common salt. All of the stable alkali metal halides have the formula MX where M is an alkali metal and X is a halogen. They are all white ionic crystalline solids that have high melting points. All the alkali metal halides are soluble in water except for lithium fluoride (LiF), which is insoluble in water due to its very high lattice enthalpy. The high lattice enthalpy of lithium fluoride is due to the small sizes of the Li+ and F− ions, causing the electrostatic interactions between them to be strong: a similar effect occurs for magnesium fluoride, consistent with the diagonal relationship between lithium and magnesium. The alkali metals also react similarly with hydrogen to form ionic alkali metal hydrides, where the hydride anion acts as a pseudohalide: these are often used as reducing agents, producing hydrides, complex metal hydrides, or" +374,"The principal organosodium compound of commercial importance is sodium cyclopentadienide. Sodium tetraphenylborate can also be classified as an organosodium compound since in the solid state sodium is bound to the aryl groups. Organometallic compounds of the higher alkali metals are even more reactive than organosodium compounds and of limited utility. A notable reagent is Schlosser's base, a mixture of n-butyllithium and potassium tert-butoxide. This reagent reacts with propene to form the compound allylpotassium (KCH2CHCH2). cis-2-Butene and trans-2-butene equilibrate when in contact with alkali metals. Whereas isomerisation is fast with lithium and sodium, it is slow with the heavier alkali metals. The heavier alkali metals also favour the sterically congested conformation. Several crystal structures of organopotassium compounds have been reported, establishing that they, like the sodium compounds, are polymeric. Organosodium, organopotassium, organorubidium and organocaesium compou" +375,"periodic trends, ignoring relativistic effects would predict ununennium to be even more reactive than caesium and francium. This lowered reactivity is due to the relativistic stabilisation of ununennium's valence electron, increasing ununennium's first ionisation energy and decreasing the metallic and ionic radii; this effect is already seen for francium. This assumes that ununennium will behave chemically as an alkali metal, which, although likely, may not be true due to relativistic effects. The relativistic stabilisation of the 8s orbital also increases ununennium's electron affinity far beyond that of caesium and francium; indeed, ununennium is expected to have an electron affinity higher than all the alkali metals lighter than it. Relativistic effects also cause a very large drop in the polarisability of ununennium. On the other hand, ununennium is predicted to continue the trend of melting points decreasing going down the group, being expected to have a melting point between 0 °C" +376,"R.L. (1993) Inorganic Chemistry: Principles of Structure and Reactivity, 4th edition, HarperCollins, New York, USA.James, A.M. and Lord, M.P. (1992) Macmillan's Chemical and Physical Data, Macmillan, London, UK. As only one additional electron is required to fill in the outermost shell of the hydrogen atom, hydrogen often behaves like a halogen, forming the negative hydride ion, and is very occasionally considered to be a halogen on that basis. (The alkali metals can also form negative ions, known as alkalides, but these are little more than laboratory curiosities, being unstable.) An argument against this placement is that formation of hydride from hydrogen is endothermic, unlike the exothermic formation of halides from halogens. The radius of the H− anion also does not fit the trend of increasing size going down the halogens: indeed, H− is very diffuse because its single proton cannot easily control both electrons. It was expected for some time that liquid hydrogen would show metalli" +377,"bonds is not worth the high ionisation energies of the 6s electrons. It displays the +1 oxidation state that all the known alkali metals display, and thallium compounds with thallium in its +1 oxidation state closely resemble the corresponding potassium or silver compounds stoichiometrically due to the similar ionic radii of the Tl+ (164 pm), K+ (152 pm) and Ag+ (129 pm) ions. It was sometimes considered an alkali metal in continental Europe (but not in England) in the years immediately following its discovery, and was placed just after caesium as the sixth alkali metal in Dmitri Mendeleev's 1869 periodic table and Julius Lothar Meyer's 1868 periodic table. Mendeleev's 1871 periodic table and Meyer's 1870 periodic table put thallium in its current position in the boron group and left the space below caesium blank. However, thallium also displays the oxidation state +3, which no known alkali metal displays (although ununennium, the undiscovered seventh alkali metal, is predicted to poss" +378,"then undergoes fractional distillation for purification. Most routes to the pure alkali metals require the use of electrolysis due to their high reactivity; one of the few which does not is the pyrolysis of the corresponding alkali metal azide, which yields the metal for sodium, potassium, rubidium, and caesium and the nitride for lithium. Lithium salts have to be extracted from the water of mineral springs, brine pools, and brine deposits. The metal is produced electrolytically from a mixture of fused lithium chloride and potassium chloride. Sodium occurs mostly in seawater and dried seabed, but is now produced through electrolysis of sodium chloride by lowering the melting point of the substance to below 700 °C through the use of a Downs cell. Extremely pure sodium can be produced through the thermal decomposition of sodium azide.Merck Index, 9th ed., monograph 8325 Potassium occurs in many minerals, such as sylvite (potassium chloride). Previously, potassium was generally made from" +379,"reactors due to its low melting point, viscosity, and cross-section towards neutron absorption. Sodium-ion batteries may provide cheaper alternatives to their equivalent lithium-based cells. Both sodium and potassium are commonly used as GRAS counterions to create more water-soluble and hence more bioavailable salt forms of acidic pharmaceuticals. Potassium compounds are often used as fertilisers as potassium is an important element for plant nutrition. Potassium hydroxide is a very strong base, and is used to control the pH of various substances. Potassium nitrate and potassium permanganate are often used as powerful oxidising agents. Potassium superoxide is used in breathing masks, as it reacts with carbon dioxide to give potassium carbonate and oxygen gas. Pure potassium metal is not often used, but its alloys with sodium may substitute for pure sodium in fast breeder nuclear reactors. Rubidium and caesium are often used in atomic clocks. Caesium atomic clocks are extraordinarily ac" +380,"to do so. There is only very limited evidence in the form of deficiency symptoms for rubidium being possibly essential in goats; even if this is true, the trace amounts usually present in food are more than enough. Caesium compounds are rarely encountered by most people, but most caesium compounds are mildly toxic. Like rubidium, caesium tends to substitute potassium in the body, but is significantly larger and is therefore a poorer substitute. Excess caesium can lead to hypokalemia, arrhythmia, and acute cardiac arrest, but such amounts would not ordinarily be encountered in natural sources. As such, caesium is not a major chemical environmental pollutant. The median lethal dose (LD50) value for caesium chloride in mice is 2.3 g per kilogram, which is comparable to the LD50 values of potassium chloride and sodium chloride. Caesium chloride has been promoted as an alternative cancer therapy, but has been linked to the deaths of over 50 patients, on whom it was used as part of a scienti" +381,"An alphabet is a standard set of letters written to represent particular sounds in a spoken language. Specifically, letters largely correspond to phonemes as the smallest sound segments that can distinguish one word from another in a given language. Not all writing systems represent language in this way: a syllabary assigns symbols to spoken syllables, while logographies assign symbols to words, morphemes, or other semantic units. The first letters were invented in Ancient Egypt to serve as an aid in writing Egyptian hieroglyphs; these are referred to as Egyptian uniliteral signs by lexicographers.Himelfarb, Elizabeth J. First Alphabet Found in Egypt , Archaeology 53, Issue 1 (January/February 2000): 21. This system was used until the 5th century CE, and fundamentally differed by adding pronunciation hints to existing hieroglyphs that had previously carried no pronunciation information. Later on, these phonemic symbols also became used to transcribe foreign words. The first fully phone" +382,"from it. ==== European alphabets ==== The Greek alphabet, in Euboean form, was carried over by Greek colonists to the Italian peninsula giving rise to many different alphabets used to write the Italic languages, like the Etruscan alphabet. One of these became the Latin alphabet, which spread across Europe as the Romans expanded their republic. After the fall of the Western Roman Empire, the alphabet survived in intellectual and religious works. It came to be used for the Romance languages that descended from Latin and most of the other languages of western and central Europe. Today, it is the most widely used script in the world. The Etruscan alphabet remained nearly unchanged for several hundred years. Only evolving once the Etruscan language changed itself. The letters used for non-existent phonemes were dropped. Afterwards, however, the alphabet went through many different changes. The final classical form of Etruscan contained 20 letters. Four of them are vowels——six fewer letters" +383,"how they treat vowels. Abjads have letters for consonants and leave most vowels unexpressed. Abugidas are also consonant-based but indicate vowels with diacritics, a systematic graphic modification of the consonants.For critics of the abjad-abugida-alphabet distinction, see Reinhard G. Lehmann: 27-30-22-26. How Many Letters Needs an Alphabet? The Case of Semitic , in: The idea of writing: Writing across borders; edited by Alex de Voogt and Joachim Friedrich Quack, Leiden: Brill 2012, p. 11-52, esp p. 22-27 The earliest known alphabet using this sense is the Wadi el-Hol script, believed to be an abjad. Its successor, Phoenician, is the ancestor of modern alphabets, including Arabic, Greek, Latin (via the Old Italic alphabet), Cyrillic (via the Greek alphabet), and Hebrew (via Aramaic). Examples of present-day abjads are the Arabic and Hebrew scripts; true alphabets include Latin, Cyrillic, and Korean hangul; and abugidas, used to write Tigrinya, Amharic, Hindi, and Thai. The Canadian Ab" +384,"several languages such as Albanian, in which dh-, ë-, gj-, ll-, rr-, th-, xh-, and zh-, which all represent phonemes and considered separate single letters, would follow the letters respectively, as well as Hungarian and Welsh. Further, German words with an umlaut get collated ignoring the umlaut as—contrary to Turkish, which adopted the graphemes ö and ü, and where a word like tüfek would come after tuz, in the dictionary. An exception is the German telephone directory, where umlauts are sorted like ä=ae since names such as Jäger also appear with the spelling Jaeger and are not distinguished in the spoken language. The Danish and Norwegian alphabets end with , whereas the Swedish conventionally put at the end. However, æ phonetically corresponds with , as does and . === Early alphabets === It is unknown whether the earliest alphabets had a defined sequence. Some alphabets today, such as the Hanuno'o script, are learned one letter at a time, in no particular order, and are not used for" +385,"foreign words, such as in the Japanese katakana syllabary is used for foreign words, and there are rules in English for using loanwords from other languages. National languages sometimes elect to address the problem of dialects by associating the alphabet with the national standard. Some national languages like Finnish, Armenian, Turkish, Russian, Serbo-Croatian (Serbian, Croatian, and Bosnian), and Bulgarian have a very regular spelling system with nearly one-to-one correspondence between letters and phonemes. Similarly, the Italian verb corresponding to 'spell (out),' compitare, is unknown to many Italians because spelling is usually trivial, as Italian spelling is highly phonemic. In standard Spanish, one can tell the pronunciation of a word from its spelling, but not vice versa, as phonemes sometimes can be represented in more than one way, but a given letter is consistently pronounced. French using silent letters, nasal vowels, and elision, may seem to lack much correspondence bet" +386,"The atomic number or nuclear charge number (symbol Z) of a chemical element is the charge number of an atomic nucleus. For ordinary nuclei composed of protons and neutrons, this is equal to the proton number (np) or the number of protons found in the nucleus of every atom of that element. The atomic number can be used to uniquely identify ordinary chemical elements. In an ordinary uncharged atom, the atomic number is also equal to the number of electrons. For an ordinary atom which contains protons, neutrons and electrons, the sum of the atomic number Z and the neutron number N gives the atom's atomic mass number A. Since protons and neutrons have approximately the same mass (and the mass of the electrons is negligible for many purposes) and the mass defect of the nucleon binding is always small compared to the nucleon mass, the atomic mass of any atom, when expressed in daltons (making a quantity called the relative isotopic mass ), is within 1% of the whole number A. Atoms with the s" +387,"by his method. There were seven elements (with Z < 92) which were not found and therefore identified as still undiscovered, corresponding to atomic numbers 43, 61, 72, 75, 85, 87 and 91.Eric Scerri, A tale of seven elements, (Oxford University Press 2013) , p.47 From 1918 to 1947, all seven of these missing elements were discovered.Scerri chaps. 3–9 (one chapter per element) By this time, the first four Transuranium elements had also been discovered, so that the periodic table was complete with no gaps as far as curium (Z = 96). === The proton and the idea of nuclear electrons === In 1915, the reason for nuclear charge being quantized in units of Z, which were now recognized to be the same as the element number, was not understood. An old idea called Prout's hypothesis had postulated that the elements were all made of residues (or protyles ) of the lightest element hydrogen, which in the Bohr-Rutherford model had a single electron and a nuclear charge of one. However, as early as 1907," +388,"Anatomy () is the branch of morphology concerned with the study of the internal structure of organisms and their parts. Anatomy is a branch of natural science that deals with the structural organization of living things. It is an old science, having its beginnings in prehistoric times. Anatomy is inherently tied to developmental biology, embryology, comparative anatomy, evolutionary biology, and phylogeny, as these are the processes by which anatomy is generated, both over immediate and long-term timescales. Anatomy and physiology, which study the structure and function of organisms and their parts respectively, make a natural pair of related disciplines, and are often studied together. Human anatomy is one of the essential basic sciences that are applied in medicine, and is often studied alongside physiology. Anatomy is a complex and dynamic field that is constantly evolving as discoveries are made. In recent years, there has been a significant increase in the use of advanced imaging" +389,"be modified to form a skeleton to support or protect the body. An exoskeleton is a thickened, rigid cuticle which is stiffened by mineralization, as in crustaceans or by the cross-linking of its proteins as in insects. An endoskeleton is internal and present in all developed animals, as well as in many of those less developed. ===Epithelium=== Epithelial tissue is composed of closely packed cells, bound to each other by cell adhesion molecules, with little intercellular space. Epithelial cells can be squamous (flat), cuboidal or columnar and rest on a basal lamina, the upper layer of the basement membrane, the lower layer is the reticular lamina lying next to the connective tissue in the extracellular matrix secreted by the epithelial cells. There are many different types of epithelium, modified to suit a particular function. In the respiratory tract there is a type of ciliated epithelial lining; in the small intestine there are microvilli on the epithelial lining and in the large inte" +390,"circulatory loop. The eyes are adapted for seeing underwater and have only local vision. There is an inner ear but no external or middle ear. Low frequency vibrations are detected by the lateral line system of sense organs that run along the length of the sides of fish, and these respond to nearby movements and to changes in water pressure. Sharks and rays are basal fish with numerous primitive anatomical features similar to those of ancient fish, including skeletons composed of cartilage. Their bodies tend to be dorso-ventrally flattened, they usually have five pairs of gill slits and a large mouth set on the underside of the head. The dermis is covered with separate dermal placoid scales. They have a cloaca into which the urinary and genital passages open, but not a swim bladder. Cartilaginous fish produce a small number of large, yolky eggs. Some species are ovoviviparous and the young develop internally but others are oviparous and the larvae develop externally in egg cases. The bo" +391,"off from a common ancestral lineage during the Cretaceous period, and they share many of the same features. The skeleton consists of a skull, a hyoid bone, spine and ribs though a few species retain a vestige of the pelvis and rear limbs in the form of pelvic spurs. The bar under the second fenestra has also been lost and the jaws have extreme flexibility allowing the snake to swallow its prey whole. Snakes lack moveable eyelids, the eyes being covered by transparent spectacle scales. They do not have eardrums but can detect ground vibrations through the bones of their skull. Their forked tongues are used as organs of taste and smell and some species have sensory pits on their heads enabling them to locate warm-blooded prey. Crocodilians are large, low-slung aquatic reptiles with long snouts and large numbers of teeth. The head and trunk are dorso-ventrally flattened and the tail is laterally compressed. It undulates from side to side to force the animal through the water when swimming" +392,"multicellular animals but some parts are specialized into the equivalent of tissues and organs. Locomotion is often provided by cilia or flagella or may proceed via the advance of pseudopodia, food may be gathered by phagocytosis, energy needs may be supplied by photosynthesis and the cell may be supported by an endoskeleton or an exoskeleton. Some protozoans can form multicellular colonies. Metazoans are a multicellular organism, with different groups of cells serving different functions. The most basic types of metazoan tissues are epithelium and connective tissue, both of which are present in nearly all invertebrates. The outer surface of the epidermis is normally formed of epithelial cells and secretes an extracellular matrix which provides support to the organism. An endoskeleton derived from the mesoderm is present in echinoderms, sponges and some cephalopods. Exoskeletons are derived from the epidermis and is composed of chitin in arthropods (insects, spiders, ticks, shrimps, cr" +393,"its cerebrum and cerebellum During his study in Alexandria, Erasistratus was particularly concerned with studies of the circulatory and nervous systems. He could distinguish the human body's sensory and motor nerves and believed air entered the lungs and heart, which was then carried throughout the body. His distinction between the arteries and veins—the arteries carrying the air through the body, while the veins carry the blood from the heart was a great anatomical discovery. Erasistratus was also responsible for naming and describing the function of the epiglottis and the heart's valves, including the tricuspid. During the third century, Greek physicians were able to differentiate nerves from blood vessels and tendons and to realize that the nerves convey neural impulses. It was Herophilus who made the point that damage to motor nerves induced paralysis. Herophilus named the meninges and ventricles in the brain, appreciated the division between cerebellum and cerebrum and recognized" +394,"anatomy in medical schools. Medical museums provided examples in comparative anatomy, and were often used in teaching. Ignaz Semmelweis investigated puerperal fever and he discovered how it was caused. He noticed that the frequently fatal fever occurred more often in mothers examined by medical students than by midwives. The students went from the dissecting room to the hospital ward and examined women in childbirth. Semmelweis showed that when the trainees washed their hands in chlorinated lime before each clinical examination, the incidence of puerperal fever among the mothers could be reduced dramatically. Before the modern medical era, the primary means for studying the internal structures of the body were dissection of the dead and inspection, palpation, and auscultation of the living. The advent of microscopy opened up an understanding of the building blocks that constituted living tissues. Technical advances in the development of achromatic lenses increased the resolving power o" +395,"In propositional logic, affirming the consequent, sometimes called converse error, fallacy of the converse, or confusion of necessity and sufficiency, is a formal fallacy of taking a true conditional statement (e.g., if the lamp were broken, then the room would be dark ) under certain assumptions (there are no other lights in the room, it is nighttime and the windows are closed), and invalidly inferring its converse ( the room is dark, so the lamp must be broken ), even though that statement may not be true under the same assumptions. This arises when the consequent ( the room would be dark ) has other possible antecedents (for example, the lamp is in working order, but is switched off or there is no lamp in the room ). Converse errors are common in everyday thinking and communication and can result from, among other causes, communication issues, misconceptions about logic, and failure to consider other causes. The opposite statement, denying the consequent, is called modus tollens and" +396,"Andrei Arsenyevich Tarkovsky (, ; 4 April 1932 // Большая российская энциклопедия. Том 31. Москва, 2016, с. 674. – 29 December 1986) was a Soviet film director and screenwriter. He has been widely considered one of the best directors in cinema history. His films explore spiritual and metaphysical themes and are known for their slow pacing and long takes, dreamlike visual imagery and preoccupation with nature and memory. Tarkovsky studied film at the All-Union State Institute of Cinematography under filmmaker Mikhail Romm and subsequently directed his first five features in the Soviet Union: Ivan's Childhood (1962), Andrei Rublev (1966), Solaris (1972), Mirror (1975), and Stalker (1979). After years of creative conflict with state film authorities, he left the country in 1979 and made his final two films—Nostalghia (1983) and The Sacrifice (1986)—abroad. In 1986, he published Sculpting in Time, a book about cinema and art. He died later that year of cancer, a condition possibly caused b" +397,"in 1959. Both films were a collaboration between the VGIK students. Classmate Aleksandr Gordon, who married Tarkovsky's sister, in particular directed, wrote, edited, and acted in the two films with Tarkovsky. An important influence on Tarkovsky was the film director Grigory Chukhray, who was teaching at the VGIK. Impressed by the talent of his student, Chukhray offered Tarkovsky a position as assistant director for his film Clear Skies. Tarkovsky initially showed interest but then decided to concentrate on his studies and his own projects. During his third year at the VGIK, Tarkovsky met Andrei Konchalovsky. They found much in common as they liked the same film directors and shared ideas on cinema and films. In 1959, they wrote the script Antarctica – Distant Country, which was later published in the Moskovsky Komsomolets. Tarkovsky submitted the script to Lenfilm, but it was rejected. They were more successful with the script The Steamroller and the Violin, which they sold to Mosfilm" +398,"it became apparent that the film differed from the script submitted to the censors. Tarkovsky was reportedly infuriated by this interruption and destroyed most of the film. ===Film career outside the Soviet Union=== During the summer of 1979, Tarkovsky traveled to Italy, where he shot the documentary Voyage in Time together with his long-time friend Tonino Guerra. Tarkovsky returned to Italy in 1980 for an extended trip, during which he and Guerra completed the script for the film Nostalghia. During this period, he took Polaroid photographs depicting his personal life. Tarkovsky returned to Italy in 1982 to start shooting Nostalghia, but Mosfilm then withdrew from the project, so he sought and received financial backing from the Italian RAI. Tarkovsky completed the film in 1983, and it was presented at the Cannes Film Festival where it won the FIPRESCI prize and the Prize of the Ecumenical Jury. Tarkovsky also shared a special prize called Grand Prix du cinéma de création with Robert B" +399,"ten favorite films. The list is as follows: Diary of a Country Priest and Mouchette by Robert Bresson; Winter Light, Wild Strawberries, and Persona by Ingmar Bergman; Nazarín by Luis Buñuel; City Lights by Charlie Chaplin; Ugetsu by Kenji Mizoguchi; Seven Samurai by Akira Kurosawa, and Woman in the Dunes by Hiroshi Teshigahara. He also liked Pier Paolo Pasolini's film The Gospel According to St. Matthew. Among his favorite directors were Buñuel, Mizoguchi, Bergman, Bresson, Kurosawa, Michelangelo Antonioni, Jean Vigo, and Carl Theodor Dreyer. With the exception of City Lights, the list does not contain any films of the early silent era. The reason is that Tarkovsky saw film as an art as only a relatively recent phenomenon, with the early film-making forming only a prelude. The list has also no films or directors from Tarkovsky's native Soviet Union, although he rated Soviet directors such as Boris Barnet, Sergei Parajanov and Alexander Dovzhenko highly. He said of Dovzhenko's Earth: I" +400,"known as a film director. During his career he directed seven feature films, as well as three shorts from his time at VGIK. His features are: * Ivan's Childhood (1962) * Andrei Rublev (1966) * Solaris (1972) * Mirror (1975) * Stalker (1979) * Nostalghia (1983) * The Sacrifice (1986) He also wrote several screenplays. Furthermore, he directed the play Hamlet for the stage in Moscow, directed the opera Boris Godunov in London, and he directed a radio production of the short story Turnabout by William Faulkner. He also wrote Sculpting in Time, a book on film theory. Tarkovsky's first feature film was Ivan's Childhood in 1962. He then directed Andrei Rublev in 1966, Solaris in 1972, Mirror in 1975 and Stalker in 1979. The documentary Voyage in Time was produced in Italy in 1982, as was Nostalghia in 1983. His last film The Sacrifice was produced in Sweden in 1986. Tarkovsky was personally involved in writing the screenplays for all his films, sometimes with a cowriter. Tarkovsky once said" +401,"three consecutive events, the Moscow International Film Festival awarded the Andrei Tarkovsky Award in 1993, 1995, and 1997. In 1996, the Andrei Tarkovsky Museum opened in Yuryevets, his childhood town. A minor planet, 3345 Tarkovskij, discovered by Soviet astronomer Lyudmila Karachkina in 1982, has been named after him. Tarkovsky has been the subject of several documentaries. Most notable is the 1988 documentary Moscow Elegy, by Russian film director Alexander Sokurov. Sokurov's own work has been heavily influenced by Tarkovsky. The film consists mostly of narration over stock footage from Tarkovsky's films. Directed by Andrei Tarkovsky is a 1988 documentary film by Michal Leszczylowski, an editor of the film The Sacrifice. Film director Chris Marker produced the television documentary One Day in the Life of Andrei Arsenevich as an homage to Andrei Tarkovsky in 2000. At the entrance to the Gerasimov Institute of Cinematography in Moscow, there is a monument that includes statues of Ta" +402,"me he is a god . The Japanese composer Ryuichi Sakamoto was an admirer of Tarkovsky's work, describing his penultimate solo album, async as “a soundtrack for an imaginary Tarkovsky film.” On Tarkovsky's overall influence on his own work, Sakamoto stated, “As I’ve been making music and trying to go deeper and deeper, I was finally able to understand what the Tarkovsky movies are about — how symphonic they are — it’s almost music. Not just the sounds — it’s a symphony of moving images and sounds. They are more complex than music.” ===Film festival=== Two film festivals have been named in his honor: * International Human Rights Film Festival Stalker , named after the film held annually in Moscow and regional centres since 1995 * International Film Festival Zerkalo named after Andrei Tarkovsky (meaning mirror ), for fans of intellectual cinema ; also known as Tarkovsky Film festival – Zerkalo, Zerkalo International Film Festival, Andrei Tarkovsky Zerkalo International Film Festival, 4 July" +403,"Ambiguity is the type of meaning in which a phrase, statement, or resolution is not explicitly defined, making for several interpretations; others describe it as a concept or statement that has no real reference. A common aspect of ambiguity is uncertainty. It is thus an attribute of any idea or statement whose intended meaning cannot be definitively resolved, according to a rule or process with a finite number of steps. (The prefix ambi- reflects the idea of two , as in two meanings ). The concept of ambiguity is generally contrasted with vagueness. In ambiguity, specific and distinct interpretations are permitted (although some may not be immediately obvious), whereas with vague information it is difficult to form any interpretation at the desired level of specificity. == Linguistic forms == Lexical ambiguity is contrasted with semantic ambiguity. The former represents a choice between a finite number of known and meaningful context-dependent interpretations. The latter represents a" +404,"ambiguities, where there is more than one way to compose a set of sounds into words. For example, ice cream and I scream . Such ambiguity is generally resolved according to the context. A mishearing of such, based on incorrectly resolved ambiguity, is called a mondegreen. == Philosophy == Philosophers (and other users of logic) spend a lot of time and effort searching for and removing (or intentionally adding) ambiguity in arguments because it can lead to incorrect conclusions and can be used to deliberately conceal bad arguments. For example, a politician might say, I oppose taxes which hinder economic growth , an example of a glittering generality. Some will think they oppose taxes in general because they hinder economic growth. Others may think they oppose only those taxes that they believe will hinder economic growth. In writing, the sentence can be rewritten to reduce possible misinterpretation, either by adding a comma after taxes (to convey the first sense) or by changing which" +405,"algorithmic languages (C++ and Fortran) require the character * as symbol of multiplication. The Wolfram Language used in Mathematica allows the user to omit the multiplication symbol, but requires square brackets to indicate the argument of a function; square brackets are not allowed for grouping of expressions. Fortran, in addition, does not allow use of the same name (identifier) for different objects, for example, function and variable; in particular, the expression f = f(x) is qualified as an error. The order of operations may depend on the context. In most programming languages, the operations of division and multiplication have equal priority and are executed from left to right. Until the last century, many editorials assumed that multiplication is performed first, for example, a/bc is interpreted as in this case, the insertion of parentheses is required when translating the formulas to an algorithmic language. In addition, it is common to write an argument of a function without" +406,"have been criticized. Languages composed from many diverse sources contain much ambiguity and inconsistency. The many exceptions to syntax and semantic rules are time-consuming and difficult to learn. == Biology == In structural biology, ambiguity has been recognized as a problem for studying protein conformations. The analysis of a protein three-dimensional structure consists in dividing the macromolecule into subunits called domains. The difficulty of this task arises from the fact that different definitions of what a domain is can be used (e.g. folding autonomy, function, thermodynamic stability, or domain motions), which sometimes results in a single protein having different—yet equally valid—domain assignments. == Christianity and Judaism == Christianity and Judaism employ the concept of paradox synonymously with ambiguity . Many Christians and Jews endorse Rudolf Otto's description of the sacred as 'mysterium tremendum et fascinans', the awe-inspiring mystery that fascinates huma" +407,"Abel is a biblical figure in the Book of Genesis within the Abrahamic religions. Born as the second son of Adam and Eve, the first two humans created by God, he was a shepherd who offered his firstborn flock to God as a religious offering. God accepted Abel's offering but not the offering of his older brother Cain, leading Cain to stone Abel to death out of jealousy. This act marked the first death in biblical history, making Abel the first murder victim. ==Life and death== ==Interpretations== ===Jewish and Christian interpretations=== According to the narrative in Genesis, Abel ( Hébel, in pausa Hā́ḇel; Hábel; , Hābēl) is Eve's second son. His name in Hebrew is composed of the same three consonants as a root meaning the air that remains after you exhale also synonymous in Hebrew to nothing , as stated in Ecclesiastes. Julius Wellhausen has proposed that the name is independent of the root.Julius Wellhausen, Skizzen und Vorarbeiten, volume 3, (1887), p. 70. Eberhard Schrader had previo" +408,"An animal is a multicellular, eukaryotic organism of the kingdom Animalia or Metazoa. Animal, Animals, or The Animal may also refer to: ==People== * The Animal (nickname), a list of people nicknamed The Animal or Animal * Animal Hamaguchi, a ring name of Japanese retired professional wrestler Heigo Hamaguchi (born 1947) * Road Warrior Animal or Animal, ring names of American professional wrestler Joseph Michael Laurinaitis (1960–2020) ==Books and publications== * Animal (book), full title Animal: The Definitive Visual Guide to The World's WildLife * Animal, 2012 novel by K'wan Foye * Animal (journal), full title: Animal: An International Journal of Animal Bioscience *Animals (novel), a 2014 novel by Emma Jane Unsworth ==Film and television== ===Films=== * Animal (1977 film), a French film (L'Animal) starring Jean-Paul Belmondo and Raquel Welch * Animals (1998 film), an American film starring Tim Roth and Rod Steiger * Animal (2001 film), an Argentine comedy film by Sergio Bizzio with C" +409,"Aardvarks ( ; Orycteropus afer) are medium-sized, burrowing, nocturnal mammals native to Africa. Aardvark, n. Dictionary of South African English. Dictionary Unit for South African English, 2018. 26 February 2019. They have a long snout, similar to that of a pig, which is used to sniff out food. Aardvarks are the only living species of the order Tubulidentata, although other prehistoric species and genera of Tubulidentata are known. They are afrotheres, a clade that also includes elephants, manatees, and hyraxes. They are found over much of the southern two-thirds of the African continent, avoiding areas that are mainly rocky. Nocturnal feeders, aardvarks subsist on ants and termites by using their sharp claws and powerful legs to dig the insects out of their hills. Aardvarks also dig to create burrows in which to live and rear their young. Aardvarks are listed as least concern by the IUCN, although their numbers are decreasing. ==Name and taxonomy== === Name === The aardvark is someti" +410,"of the aardvark contains many unique and different features. One of the most distinctive characteristics of the Tubulidentata is their teeth. Instead of having a pulp cavity, each tooth has a cluster of thin, hexagonal, upright, parallel tubes of vasodentin (a modified form of dentine), with individual pulp canals, held together by cementum. The number of columns is dependent on the size of the tooth, with the largest having about 1,500. The teeth have no enamel coating and are worn away and regrow continuously. The aardvark is born with conventional incisors and canines at the front of the jaw, which fall out and are not replaced. Adult aardvarks have only cheek teeth at the back of the jaw, and have a dental formula of: These remaining teeth are peg-like and rootless and are of unique composition. The teeth consist of 14 upper and 12 lower jaw molars. The nasal area of the aardvark is another unique area, as it contains ten nasal conchae, more than any other placental mammal. The sid" +411,"stinging attacks are rendered futile by the tough skin. After an aardvark visit at a termite mound, other animals will visit to pick up all the leftovers. Termite mounds alone do not provide enough food for the aardvark, so they look for termites that are on the move. When these insects move, they can form columns long and these tend to provide easy pickings with little effort exerted by the aardvark. These columns are more common in areas of livestock or other hoofed animals. The trampled grass and dung attract termites from the Odontotermes, Microtermes, and Pseudacanthotermes genera. On a nightly basis they tend to be more active during the first portion of night (roughly the four hours between 8:00p.m. and 12:00a.m.); however, they do not seem to prefer bright or dark nights over the other. During adverse weather or if disturbed they will retreat to their burrow systems. They cover between per night; however, some studies have shown that they may traverse as far as in a night. Aard" +412,"to soldier ants. Hausa magicians make a charm from the heart, skin, forehead, and nails of the aardvark, which they then proceed to pound together with the root of a certain tree. Wrapped in a piece of skin and worn on the chest, the charm is said to give the owner the ability to pass through walls or roofs at night. The charm is said to be used by burglars and those seeking to visit young girls without their parents' permission. Also, some tribes, such as the Margbetu, Ayanda, and Logo, will use aardvark teeth to make bracelets, which are regarded as good luck charms. The meat, which has a resemblance to pork, is eaten in certain cultures. In the mythology of the Dagbon people of Ghana, the aardvark is believed to possess superpowers. The Dagombas believe this animal can transfigure into and interact with humans. The ancient Egyptian god Set is usually depicted with the head of an unidentified animal, whose similarity to an aardvark has been noted in scholarship. The titular character" +413,"The aardwolf (Proteles cristatus) is an insectivorous hyaenid species, native to East and Southern Africa. Its name means earth-wolf in Afrikaans and Dutch. Aardwolf, n. Dictionary of South African English. Dictionary Unit for South African English, 2018. Web. 25 February 2019. It is also called the maanhaar-jackal Maanhaar, n. Dictionary of South African English. Dictionary Unit for South African English, 2018. Web. 25 February 2019. (Afrikaans for mane-jackal ), termite-eating hyena and civet hyena, based on its habit of secreting substances from its anal gland, a characteristic shared with the African civet. Unlike many of its relatives in the order Carnivora, the aardwolf does not hunt large animals. It eats insects and their larvae, mainly termites; one aardwolf can lap up as many as 300,000 termites during a single night using its long, sticky tongue. The aardwolf's tongue has adapted to be tough enough to withstand the strong bite of termites. The aardwolf lives in the shrubland" +414,"every 20 minutes or so. If they are patrolling their territorial boundaries, the marking frequency increases drastically, to once every . At this rate, an individual may mark 60 marks per hour, and upwards of 200 per night. An aardwolf pair may have up to 10 dens, and numerous feces middens, within their territory. When they deposit excreta at their middens, they dig a small hole and cover it with sand. Their dens are usually abandoned aardvark, springhare, or porcupine dens, or on occasion they are crevices in rocks. They will also dig their own dens, or enlarge dens started by springhares. They typically will only use one or two dens at a time, rotating through all of their dens every six months. During the summer, they may rest outside their den during the night and sleep underground during the heat of the day. Aardwolves are not fast runners nor are they particularly adept at fighting off predators. Therefore, when threatened, the aardwolf may attempt to mislead its foe by doubling" +415,"* * * * * * * * * * * ==Further reading== * * ==External links== * Animal Diversity Web * IUCN Hyaenidae Specialist Group Aardwolf pages on hyaenidae.org * Cam footage from the Namib desert https://m.youtube.com/watch?v=lRevqS6Pxgg * Category:Mammals described in 1783 Category:Carnivorans of Africa Category:Hyenas Category:Mammals of Southern Africa Category:Fauna of East Africa Category:Myrmecophagous mammals Category:Taxa named by Anders Sparrman Category:Nocturnal animals" +416,"thumb|Adobe walls separate urban gardens in Shiraz, Iran Adobe ( ; ) is a building material made from earth and organic materials. is Spanish for mudbrick. In some English-speaking regions of Spanish heritage, such as the Southwestern United States, the term is used to refer to any kind of earthen construction, or various architectural styles like Pueblo Revival or Territorial Revival. Most adobe buildings are similar in appearance to cob and rammed earth buildings. Adobe is among the earliest building materials, and is used throughout the world. Adobe architecture has been dated to before 5,100 BC. ==Description== Adobe bricks are rectangular prisms small enough that they can quickly air dry individually without cracking. They can be subsequently assembled, with the application of adobe mud to bond the individual bricks into a structure. There is no standard size, with substantial variations over the years and in different regions. In some areas a popular size measured weighing about" +417,"desert climates, a factor which has contributed to its longevity as a building material. Thermodynamic material properties have significant variation in the literature. Some experiments suggest that the standard consideration of conductivity is not adequate for this material, as its main thermodynamic property is inertia, and conclude that experimental tests should be performed over a longer period of time than usual - preferably with changing thermal jumps. There is an effective R-value for a north facing 10-in wall of R0=10 hr ft2 °F/Btu, which corresponds to thermal conductivity k=10 in x 1 ft/12 in /R0=0.33 Btu/(hr ft °F) or 0.57 W/(m K) in agreement with the thermal conductivity reported from another source. To determine the total R-value of a wall, scale R0 by the thickness of the wall in inches. The thermal resistance of adobe is also stated as an R-value for a 10-inch wall R0=4.1 hr ft2 °F/Btu. Another source provides the following properties: conductivity=0.30 Btu/(hr ft °F) o" +418,"fencing or wiring over the framework to allow an even load as masses of adobe are spread across the metal fencing like cob and allowed to air dry accordingly. This method was demonstrated with an adobe blend heavily impregnated with cement to allow even drying and prevent cracking. The more traditional flat adobe roofs are functional only in dry climates that are not exposed to snow loads. The heaviest wooden beams, called vigas, lie atop the wall. Across the vigas lie smaller members called latillas and upon those brush is then laid. Finally, the adobe layer is applied. To construct a flat adobe roof, beams of wood were laid to span the building, the ends of which were attached to the tops of the walls. Once the vigas, latillas and brush are laid, adobe bricks are placed. An adobe roof is often laid with bricks slightly larger in width to ensure a greater expanse is covered when placing the bricks onto the roof. Following each individual brick should be a layer of adobe mortar, recomm" +419,"An adventure is an exciting experience or undertaking that is typically bold, sometimes risky. Adventures may be activities with danger such as traveling, exploring, skydiving, mountain climbing, scuba diving, river rafting, or other extreme sports. Adventures are often undertaken to create psychological arousal or in order to achieve a greater goal, such as the pursuit of knowledge that can only be obtained by such activities. ==Motivation== Adventurous experiences create psychological arousal, which can be interpreted as negative (e.g. fear) or positive (e.g. flow). For some people, adventure becomes a major pursuit in and of itself. According to adventurer André Malraux, in his Man's Fate (1933), If a man is not ready to risk his life, where is his dignity? Similarly, Helen Keller stated that Life is either a daring adventure or nothing. Outdoor adventurous activities are typically undertaken for the purposes of recreation or excitement: examples are adventure racing and adventure t" +420,"Asia ( , ) is the largest continent in the world by both land area and population. It covers an area of more than 44 million square kilometers, about 30% of Earth's total land area and 8% of Earth's total surface area. The continent, which has long been home to the majority of the human population, was the site of many of the first civilizations. Its 4.7 billion people constitute roughly 60% of the world's population. Asia shares the landmass of Eurasia with Europe, and of Afro-Eurasia with both Europe and Africa. In general terms, it is bounded on the east by the Pacific Ocean, on the south by the Indian Ocean, and on the north by the Arctic Ocean. The border of Asia with Europe is a historical and cultural construct, as there is no clear physical and geographical separation between them. A commonly accepted division places Asia to the east of the Suez Canal separating it from Africa; and to the east of the Turkish straits, the Ural Mountains and Ural River, and to the south of the Ca" +421,"Melanesian people, with varying degrees of intermixing between the two. In general, the further west and coastal a region is, the stronger the Austronesian influences, and the further east and inland a region is, the stronger the Melanesian influences. The terms Southeast Asia and Oceania, devised in the 19th century, have had several vastly different geographic meanings since their inception. The chief factor in determining which islands of the Indonesian Archipelago are Asian has been the location of the colonial possessions of the various empires there (not all European). Lewis and Wigen assert, The narrowing of 'Southeast Asia' to its present boundaries was thus a gradual process. . === Asia–North America boundary === The Bering Strait and Bering Sea separate the landmasses of Asia and North America, as well as forming the international boundary between Russia and the United States. This national and continental boundary separates the Diomede Islands in the Bering Strait, with Big" +422,"support a large equestrian force; for this and other reasons, the nomads who conquered states in China, India, and the Middle East often found themselves adapting to the local, more affluent societies. === Medieval era === The Islamic Caliphate's defeats of the Byzantine and Persian empires led to West Asia and southern parts of Central Asia and western parts of South Asia under its control during its conquests of the 7th century; Islam also spread over centuries to the southern regions of India and Southeast Asia through trade along the Maritime Silk Road. The Mongol Empire conquered a large part of Asia in the 13th century, an area extending from China to Europe. Before the Mongol invasion, Song dynasty reportedly had approximately 120 million citizens; the 1300 census which followed the invasion reported roughly 60 million people.Ping-ti Ho. An Estimate of the Total Population of Sung-Chin China , in Études Song, Series 1, No. 1, (1970). pp. 33–53. The Black Death, one of the most d" +423,"Mongolia and the Arabian Desert stretches across much of the Middle East. The Yangtze in China is the longest river in the continent. The Himalayas between Nepal and China is the tallest mountain range in the world. Tropical rainforests stretch across much of southern Asia and coniferous and deciduous forests lie farther north. File:Tundra in Siberia.jpg|Siberian tundra File:Gunung Palung Jungle.jpg|Rainforest in Borneo File:Kerala Backwaters, India.JPG|Kerala backwaters File:Naadam rider 2.jpg|Mongolian steppe File:1 li jiang guilin yangshuo 2011.jpg|South China Karst File:Taman Negara, Malaysia, Panoramic view.jpg|Taman Negara, Peninsular Malaysia File:Akkem Valley 2011.jpg|Altai Mountains File:Hunza Valley from Eagle Point.jpg|Hunza Valley File:Baa atoll islands.JPG|Atolls of the Maldives File:Red sand of the Wadi Rum desert.jpg|Wadi Rum in Jordan === Main regions === There are various approaches to the regional division of Asia. The following subdivision into regions is used, among" +424,"Asia. Individual main routes have emerged from this. The main route leads from the Chinese coast south via Hanoi to Jakarta, Singapore and Kuala Lumpur through the Strait of Malacca via the Sri Lankan Colombo to the southern tip of India via Malé to East Africa Mombasa, from there to Djibouti, then through the Red Sea over the Suez Canal into Mediterranean, there via Haifa, Istanbul and Athens to the upper Adriatic to the northern Italian hub of Trieste with its rail connections to Central and Eastern Europe or further to Barcelona and around Spain and France to the European northern ports. A far smaller part of the goods traffic runs via South Africa to Europe. A particularly significant part of the Asian goods traffic is carried out across the Pacific towards Los Angeles and Long Beach. In contrast to the sea routes, the Silk Road via the land route to Europe is on the one hand still under construction and on the other hand is much smaller in terms of scope. Intra-Asian trade, includ" +425,"communities in Turkey (17,400), Azerbaijan (9,100), Iran (8,756), See Persian Jews#Iran India (5,000) and Uzbekistan (4,000),, American Jewish Yearbook, vol. 107 (2007), p. 592. among many other places. In total, there are 14.4–17.5 million (2016, est.) Jews alive in the world today, making them one of the smallest Asian minorities, at roughly 0.3 to 0.4 percent of the total population of the continent. Christianity is a widespread religion in Asia with more than 286 million adherents according to Pew Research Center in 2010, and nearly 364 million according to Britannica Book of the Year 2014. Christians constitute around 12.6% of the total population of Asia. In the Philippines and East Timor, Roman Catholicism is the predominant religion; it was introduced by the Spaniards and the Portuguese, respectively. In Armenia and Georgia, Eastern Orthodoxy is the predominant religion. In the Middle East, such as in the Levant, Anatolia and Fars, Syriac Christianity (Church of the East) and O" +426,"Involved international forces and led to the division of the Korean Peninsula. *The First Indochina War (1946–1954): Ended with the defeat of French colonial forces and the partition of Vietnam. *The Vietnam War (1955–1975): A protracted conflict with significant global implications, especially during the Cold War. * The Bangladesh Liberation War (1971): India helped East Pakistan become independent, with Cold War ramifications. *The Sino-Vietnamese War (1979): Conflict between China and Vietnam following Vietnam's invasion of Cambodia. *The Indonesian occupation of East Timor (1975–1999): Involved Indonesia's annexation and subsequent independence through a UN-backed referendum. *The Soviet–Afghan War (1979–1989): Soviet intervention in Afghanistan, contributing to the rise of the mujahideen. *The Iran–Iraq War (1980–1988): Long-lasting conflict with regional and international implications. *The Gulf War (1990–1991): Resulted from Iraq's invasion of Kuwait, with international interven" +427,"760 Manama Bangladesh 147,570 Dhaka Bhutan 38,394 Thimphu Brunei 5,765 Bandar Seri Begawan Cambodia 181,035 Phnom Penh China (PRC) 9,596,961 Beijing Cyprus 9,251 Nicosia East Timor 14,874 Dili Egypt 1,001,449 Cairo Georgia 69,700 Tbilisi Hong Kong 2,754 Hong Kong India 3,287,263 New Delhi Indonesia 1,904,569 Jakarta Iran 1,648,195 Tehran Iraq 438,317 Baghdad Israel 20,770 Jerusalem (disputed) Japan 377,915 Tokyo Jordan 89,342 Amman Kazakhstan 2,724,900 Astana Kuwait 17,818 Kuwait City Kyrgyzstan 199,951 Bishkek Laos 236,800 Vientiane Lebanon 10,400 Beirut Macau 32.9 Macau Malaysia 329,847 Kuala Lumpur Maldives 298 Malé Mongolia 1,564,116 Ulaanbaatar Myanmar 676,578 Naypyidaw Nepal 147,181 Kathmandu North Korea 120,538 Pyongyang Oman 309,500 Muscat Pakistan 211,103,000 881,913 Islamabad Palestine 6,220 Philippines 343,448 Manila Qatar 11,586 Doha Russia 17,098,242 Moscow Saudi Arabia 2,149,690 Riyadh Singapore 697 Singapore South Korea 100,210 Seoul Sri Lanka 65,610 Sri Jayawardenepura" +428,"Aruba ( , or , ), officially the Country of Aruba (; ), is a constituent country within the Kingdom of the Netherlands, in the southern Caribbean Sea north of the Venezuelan peninsula of Paraguaná and northwest of Curaçao. In 1986, it became a constituent country within the Kingdom of the Netherlands and acquired the formal name the Country of Aruba. Aruba has an area of . Aruba measures in length from its northwestern to its southeastern end and is across at its widest point. Alongside Bonaire and Curaçao, Aruba forms a group referred to as the ABC islands. The Dutch Caribbean encompasses the ABC islands along with the other three substantial islands, the SSS islands. In contrast to much of the Caribbean, which experiences humid tropical climates, Aruba has a dry climate with an arid xeric landscape. The relatively warm and sunny weather persists throughout the year. With a population of 108,166,Aruba is home to about one-third of the population of the Dutch Caribbean. As one of the f" +429,"Bonaireans. Archaeological-Anthropological Institute of the Netherlands Antilles. The name Caquetío refers to how this group referred to themselves during their first contact with Europeans. They had longer and narrower skulls than the archaic population, and their height was up to . The newcomers brought pottery and agriculture to the islands and are therefore classified as part of the neo-Indian period. ==== Caquetío chiefdom ==== The area over which the legendary cacique Manaure exercised his authority was the coastal region of the current state Falcón-Zulia at Venezuela, including the Paraguaná Peninsula, as well as Aruba, Curaçao and Bonaire. The Caquetío people had a highly developed process of state formation. They had a chiefdom, which in human evolution is often a precursor to a kingdom, where central leaders—paramount chiefs—controlled multiple subordinate political-administrative units.Sahlins, M.D. (1968). Tribesmen (Foundations of Modern Anthropology) (1st ed.). Prentice H" +430,"India had been the spearhead of European trade expansion and the foreign policy of the Spanish Crown since the travels of Marco Polo a century earlier. India, China, and Japan formed the focal point of medieval ideas about boundless riches, cities with houses covered in gold, and islands with inexhaustible amounts of spices, pearls, and silk. The suspicion arose that India could be reached via the relatively short route to the west, across the ocean of Atlantis. During his third voyage to the New World, Columbus was searching for the southern route to India and explored the Paria Peninsula (eastern Venezuela) and the Orinoco region, where he discovered the fresh river water of the Orinoco Delta. The suspicion arose that he had not found islands off the coast of India but a much more extensive land mass; an extension of Asia. Columbus did not realize that this was an unknown continent. Characteristic of his Christian medieval perspective, Columbus solved the puzzle by assuming that he h" +431,"encomienda, which ended the autonomy of Caquetío community in the islands. Their relatives on the mainland did not fare any better. After an unsuccessful attempt by Bartolomé de Las Casas to convert the local population to Christianity, the coastal region of the mainland was leased to the banking firm of the Welsers in 1528. This led to the violent conquest of the Caquetío kingdom. Before 1634, Curaçao, along with its neighboring islands Bonaire and Aruba, were considered part of the province of Venezuela. They had been separated from Venezuela only during the period of the Welser grant. The appropriation of the Caribbean region turned out to be a failure for the Spaniards. The exploitation of the West Indian islands proved unprofitable, and gold mining on Hispaniola and Puerto Rico declined. Spanish settlers moved elsewhere, and In 1569, the Spanish Crown banned settlement on the Caribbean islands by royal decree. This measure that did not apply to the leased islands of Curaçao, Aruba" +432,"and acquired by the WIC in June 1634, primarily by their desire to obtain salt. In Van Walbeeck's report of 1634, Aruba is mentioned only in relation to Curaçao, where he refers to Bonaire and Aruba collectively as the islands of Curaçao . By 1816, Aruba possessed seven salt pans, all of which yielded salt of subpar quality. The salt production was just sufficient to meet the local demand. Aruban laborers, often assisted by donkeys, were tasked with gathering the salt, which was subsequently distributed among the island's inhabitants. Around 1924, salt extraction at Rancho had limited benefits, primarily being used in the preservation of fish during shipping. Paardenbaai (Horses' Bay) contained salt pans up until 1949 when it was dredged and disappeared beneath the sand. ==== New Netherland ==== Between the Peace of Westphalia in 1648 and the Peace of Nijmegen in 1678, there were 30 years of crisis in the Dutch Antilles and the entire Caribbean region. By 1648, Curaçao had lost its imp" +433,"Venezuelan oil fields, bringing greater prosperity to the island.Albert Gastmann, Suriname and the Dutch in the Caribbean in Encyclopedia of Latin American History and Culture, vol. 5, p. 189. New York: Charles Scribner's Sons 1996. During World War II, the Netherlands was occupied by Nazi Germany. In 1940, the oil facilities in Aruba came under the administration of the Dutch government-in-exile in London, causing them to be attacked by the German navy in 1942.Central American and Caribbean Air Forces, Daniel Hagedorn, Air Britain (Historians) Ltd., Tonbridge, 1993, p.135, In August 1947, Aruba formulated its first staatsreglement (constitution) for Aruba's status aparte as an autonomous state within the Kingdom of the Netherlands, prompted by the efforts of Henny Eman, a noted Aruban politician. By 1954, the Charter of the Kingdom of the Netherlands was established, providing a framework for relations between Aruba and the rest of the kingdom. That created the Netherlands Antilles, w" +434,"Drought-tolerant trees like Caesalpinia coriaria and Vachellia tortuosa are present. The isolation from the South America mainland contributed to the evolution of multiple endemic species. The island provides a habitat for unique wildlife, including the endemic Aruban Whiptail, Aruba Rattlesnake, as well as subspecies of Aruban Burrowing Owl and Brown-throated Parakeet. ===Climate and natural hazards=== According to the Köppen climate classification, Aruba is characterized by a hot semi-arid climate (Köppen BSh), characterized by limited rainfall, totaling just annually. Notably, Aruba remains dry even during its supposed rainy season. Rainfall can be highly variable,Dewar, Robert E. and Wallis, James R; 'Geographical patterning in interannual rainfall variability in the tropics and near tropics: An L-moments approach'; in Journal of Climate, 12; pp. 3457–3466 ranging from as little as during strong El Niño years (e.g. 1911/1912, 1930/1931, 1982/1983, 1997/1998) to over in La Niña year" +435,"within a framework of a 21-member Staten (Parliament) and an eight-member Cabinet; the Staten's 21 members are elected by direct, popular vote to serve a four-year term. The governor of Aruba is appointed for a six-year term by the monarch, and the prime minister and deputy prime minister are indirectly elected by the Staten for four-year terms. ===Politics=== The legal system is based on the Dutch model. Legal jurisdiction lies with the Gerecht in Eerste Aanleg (Court of First Instance), the Gemeenschappelijk Hof van Justitie van Aruba, Curaçao, Sint Maarten, en van Bonaire, Sint Eustatius en Saba (Joint Court of Justice of Aruba, Curaçao, Sint Maarten, and of Bonaire, Sint Eustatius and Saba) and the Hoge Raad der Nederlanden (Supreme Court of Justice of the Netherlands). The Korps Politie Aruba (Aruba Police Force) is the law enforcement agency and operates district precincts in Oranjestad, Noord, San Nicolaas, and Santa Cruz, where it is headquartered. Divergent Legal Protections f" +436,"from the Netherlands. For private aircraft passengers bound for the United States, the United States Department of Homeland Security, U.S. Customs and Border Protection has a full pre-clearance facility since 1 February 2001 when Queen Beatrix Airport expanded. Since 2008, Aruba has been the only island to have this service for private flights. ==Culture== Aruba boasts a diverse culture. According to the Bureau Burgelijke Stand en Bevolkingsregister (BBSB, Civil Registry and Population Register), in 2005, the island was home to people from 92 different nationalities. Dutch influence is still evident in traditions like the celebration of Sinterklaas (Saint Nicholas) on December 5 and 6, as well as national holidays like April 27 when Aruba, along with the rest of the Kingdom of the Netherlands, celebrates Koningsdag (King's day) or Dia di Rey (in Papiamento) is celebrated. On 18 March, Aruba celebrates its National Anthem and Flag Day. Christmas and New Year's Eve are celebrated with th" +437,"The Articles of Confederation and Perpetual Union was an agreement among the 13 states of the United States, formerly the Thirteen Colonies, that served as the nation's first frame of government. It was debated by the Second Continental Congress at Independence Hall in Philadelphia between July 1776 and November 1777, and finalized by the Congress on November 15, 1777. It came into force on March 1, 1781, after being ratified by all 13 colonial states. A guiding principle of the Articles was the establishment and preservation of the independence and sovereignty of the states. The Articles consciously established a weak confederal government, affording it only those powers the former colonies had recognized as belonging to king and parliament. The document provided clearly written rules for how the states' league of friendship, known as the Perpetual Union, would be organized. While waiting for all states to ratify, the Congress observed the Articles as it conducted business, directing" +438,"of the following individuals: * John Dickinson (Pennsylvania, chairman of the committee) * Samuel Adams (Massachusetts) * Josiah Bartlett (New Hampshire) * Button Gwinnett (Georgia) * Joseph Hewes (North Carolina) * Stephen Hopkins (Rhode Island) * Robert R. Livingston (New York) * Thomas McKean (Delaware) * Thomas Nelson (Virginia) * Edward Rutledge (South Carolina) * Roger Sherman (Connecticut) * Thomas Stone (Maryland) * Francis Hopkinson (New Jersey, added to the committee last) The committee met frequently, and chairman John Dickinson presented their results to the Congress on July 12, 1776. Afterward, there were long debates on such issues as state sovereignty, the exact powers to be given to Congress, whether to have a judiciary, western land claims, and voting procedures. To further complicate work on the constitution, Congress was forced to leave Philadelphia twice, for Baltimore, Maryland, in the winter of 1776, and later for Lancaster then York, Pennsylvania, in the fall of" +439,"America will be paid with funds raised by state legislatures, and apportioned to the states in proportion to the real property values of each. # Powers and functions of the United States in Congress Assembled. #* Grants to the United States in Congress assembled the sole and exclusive right and power to determine peace and war; to exchange ambassadors; to enter into treaties and alliances, with some provisos; to establish rules for deciding all cases of captures or prizes on land or water; to grant letters of marque and reprisal (documents authorizing privateers) in times of peace; to appoint courts for the trial of pirates and crimes committed on the high seas; to establish courts for appeals in all cases of captures, but no member of Congress may be appointed a judge; to set weights and measures (including coins), and for Congress to serve as a final court for disputes between states. #* The court will be composed of jointly appointed commissioners or Congress shall appoint them. Eac" +440,"in the treasury. There never will be money in the treasury till the Confederacy shows its teeth. [Original source: The Papers of Thomas Jefferson, vol. 10, June 22–December 31, 1786, ed. Julian P. Boyd. Princeton: Princeton University Press, 1954, pp. 560–566] Furthermore, the 1786 Jay–Gardoqui Treaty with Spain also showed weakness in foreign policy. In this treaty, which was never ratified, the United States was to give up rights to use the Mississippi River for 25 years, which would have economically strangled the settlers west of the Appalachian Mountains. Finally, due to the Confederation's military weakness, it could not compel the British army to leave frontier forts which were on American soil—and which, in 1783, the British promised to leave, but which they delayed leaving pending U.S. implementation of other provisions such as ending action against Loyalists and allowing them to seek compensation. This incomplete British implementation of the Treaty of Paris would later be re" +441,"1787 Cyrus Griffin November 15, 1788 ==U.S. under the Articles== The peace treaty left the United States independent and at peace but with an unsettled governmental structure. The Articles envisioned a permanent confederation but granted to the Congress—the only federal institution—little power to finance itself or to ensure that its resolutions were enforced. There was no president, no executive agencies, no judiciary, and no tax base. The absence of a tax base meant that there was no way to pay off state and national debts from the war years except by requesting money from the states, which seldom arrived. Although historians generally agree that the Articles were too weak to hold the fast-growing nation together, they do give credit to the settlement of the western issue, as the states voluntarily turned over their lands to national control. By 1783, with the end of the British blockade, the new nation was regaining its prosperity. However, trade opportunities were restricted by the" +442,"Lee in the Senate Chamber, in the presence of the members of both Houses ... an Act to empower the delegates of this state in Congress to subscribe and ratify the articles of confederation and perpetual union among the states. The Senate then adjourned to the first Monday in August next. The decision of Maryland to ratify the Articles was reported to the Continental Congress on February 12. The confirmation signing of the Articles by the two Maryland delegates took place in Philadelphia at noon time on March 1, 1781, and was celebrated in the afternoon. With these events, the Articles were entered into force and the United States of America came into being as a sovereign federal state. Congress had debated the Articles for over a year and a half, and the ratification process had taken nearly three and a half years. Many participants in the original debates were no longer delegates, and some of the signers had only recently arrived. The Articles of Confederation and Perpetual Union were" +443,"be made by political means, thus potential rulers were uninspired to seek power. When the war ended in 1783, certain special interests had incentives to create a new merchant state, much like the British state people had rebelled against. In particular, holders of war scrip and land speculators wanted a central government to pay off scrip at face value and to legalize western land holdings with disputed claims. Also, manufacturers wanted a high tariff as a barrier to foreign goods, but competition among states made this impossible without a central government. ===Legitimacy of closing down=== Two prominent political leaders in the Confederation, John Jay of New York and Thomas Burke of North Carolina believed that the authority of the congress rested on the prior acts of the several states, to which the states gave their voluntary consent, and until those obligations were fulfilled, neither nullification of the authority of congress, exercising its due powers, nor secession from the co" +444,"The Atlantic Ocean is the second-largest of the world's five oceanic divisions, with an area of about . It covers approximately 17% of Earth's surface and about 24% of its water surface area. During the Age of Discovery, it was known for separating the New World of the Americas (North America and South America) from the Old World of Afro-Eurasia (Africa, Asia, and Europe). Through its separation of Afro-Eurasia from the Americas, the Atlantic Ocean has played a central role in the development of human society, globalization, and the histories of many nations. While the Norse were the first known humans to cross the Atlantic, it was the expedition of Christopher Columbus in 1492 that proved to be the most consequential. Columbus' expedition ushered in an age of exploration and colonization of the Americas by European powers, most notably Portugal, Spain, France, and the United Kingdom. From the 16th to 19th centuries, the Atlantic Ocean was the center of both an eponymous slave trade an" +445,"Sea1.205 million km2 # Argentine Sea1 million km2 # Labrador Sea841,000 km2 # Irminger Sea780,000 km2 # Baffin Bay689,000 km2 # North Sea575,000 km2 # Black Sea436,000 km2 # Baltic Sea377,000 km2 # Libyan Sea350,000 km2 # Levantine Sea320,000 km2 # Celtic Sea300,000 km2 # Tyrrhenian Sea275,000 km2 # Gulf of Saint Lawrence226,000 km2 # Bay of Biscay223,000 km2 # Aegean Sea214,000 km2 # Ionian Sea169,000 km2 # Balearic Sea150,000 km2 # Adriatic Sea138,000 km2 # Gulf of Bothnia116,300 km2 # Sea of Crete95,000 km2 # Gulf of Maine93,000 km2 # Ligurian Sea80,000 km2 # English Channel75,000 km2 # James Bay68,300 km2 # Bothnian Sea66,000 km2 # Gulf of Sidra57,000 km2 # Sea of the Hebrides47,000 km2 # Irish Sea46,000 km2 # Sea of Azov39,000 km2 # Bothnian Bay36,800 km2 # Gulf of Venezuela17,840 km2 # Bay of Campeche16,000 km2 # Gulf of Lion15,000 km2 # Sea of Marmara11,350 km2 # Wadden Sea10,000 km2 # Archipelago Sea8,300 km2 == Bathymetry == The bathymetry of the Atlantic is dominated by a sub" +446,"season. Evaporation, precipitation, river inflow and sea ice melting influence surface salinity values. Although the lowest salinity values are just north of the equator (because of heavy tropical rainfall), in general, the lowest values are in the high latitudes and along coasts where large rivers enter. Maximum salinity values occur at about 25° north and south, in subtropical regions with low rainfall and high evaporation. The high surface salinity in the Atlantic, on which the Atlantic thermohaline circulation is dependent, is maintained by two processes: the Agulhas Leakage/Rings, which brings salty Indian Ocean waters into the South Atlantic, and the Atmospheric Bridge , which evaporates subtropical Atlantic waters and exports it to the Pacific. === Water masses === Temperature-salinity characteristics for Atlantic water masses Water mass Temperature Salinity Upper waters () Upper waters () Upper waters () Atlantic SubarcticUpper Water (ASUW) 0.0–4.0 °C 34.0–35.0 Western North At" +447,"and natans) float, an area wide and encircled by the Gulf Stream, North Atlantic Drift, and North Equatorial Current. This population of seaweed probably originated from Tertiary ancestors on the European shores of the former Tethys Ocean and has, if so, maintained itself by vegetative growth, floating in the ocean for millions of years. Other species endemic to the Sargasso Sea include the sargassum fish, a predator with algae-like appendages which hovers motionless among the Sargassum. Fossils of similar fishes have been found in fossil bays of the former Tethys Ocean, in what is now the Carpathian region, that were similar to the Sargasso Sea. It is possible that the population in the Sargasso Sea migrated to the Atlantic as the Tethys closed at the end of the Miocene around 17 Ma. The origin of the Sargasso fauna and flora remained enigmatic for centuries. The fossils found in the Carpathians in the mid-20th century often called the quasi-Sargasso assemblage , finally showed that t" +448,"American Interchange, but the closure of the seaway resulted in a Great American Schism as it affected ocean currents, salinity, and temperatures in both the Atlantic and Pacific. Marine organisms on both sides of the isthmus became isolated and either diverged or went extinct. === North Atlantic === Geologically, the northern Atlantic is the area delimited to the south by two conjugate margins, Newfoundland and Iberia, and to the north by the Arctic Eurasian Basin. The opening of the northern Atlantic closely followed the margins of its predecessor, the Iapetus Ocean, and spread from the central Atlantic in six stages: Iberia–Newfoundland, Porcupine–North America, Eurasia–Greenland, Eurasia–North America. Active and inactive spreading systems in this area are marked by the interaction with the Iceland hotspot. Seafloor spreading led to the extension of the crust and the formation of troughs and sedimentary basins. The Rockall Trough opened between 105 and 84 million years ago although" +449,"kya. The earliest evidence for the complex behavior associated with this behavioral modernity has been found in the Greater Cape Floristic Region (GCFR) along the coast of South Africa. During the latest glacial stages, the now-submerged plains of the Agulhas Bank were exposed above sea level, extending the South African coastline farther south by hundreds of kilometers. A small population of modern humansprobably fewer than a thousand reproducing individualssurvived glacial maxima by exploring the high diversity offered by these Palaeo-Agulhas plains. The GCFR is delimited to the north by the Cape Fold Belt and the limited space south of it resulted in the development of social networks out of which complex Stone Age technologies emerged. Human history thus begins on the coasts of South Africa where the Atlantic Benguela Upwelling and the Indian Ocean Agulhas Current meet to produce an intertidal zone on which shellfish, fur seal, fish and sea birds provided the necessary protein sour" +450,"and thrown over cliffs and by the early 1200s hay had to be abandoned for short-season crops such as barley. === Atlantic World === Christopher Columbus reached the Americas in 1492, sailing under the Spanish flag. Six years later Vasco da Gama reached India under the Portuguese flag, by navigating south around the Cape of Good Hope, thus proving that the Atlantic and Indian Oceans are connected. In 1500, in his voyage to India following Vasco da Gama, Pedro Álvares Cabral reached Brazil, taken by the currents of the South Atlantic Gyre. Following these explorations, Spain and Portugal quickly conquered and colonized large territories in the New World and forced the Amerindian population into slavery in order to exploit the vast quantities of silver and gold they found. Spain and Portugal monopolized this trade in order to keep other European nations out, but conflicting interests nevertheless led to a series of Spanish-Portuguese wars. A peace treaty mediated by the Pope divided the c" +451,"northern shrimp and Norwegian lobster are in good condition. In the Northeast Atlantic, 21% of stocks are considered overfished. : This zone makes almost three-quarters (72.8 %) of European Union fishing catches in 2020. Main fishing EU countries are Denmark, France, the Netherlands and Spain. Most common species include herring, mackerel, and sprats. ;Northwest Atlantic: In the Northwest Atlantic landings have decreased from 4.2 million tons in the early 1970s to 1.9 million tons in 2013. During the 21st century, some species have shown weak signs of recovery, including Greenland halibut, yellowtail flounder, Atlantic halibut, haddock, spiny dogfish, while other stocks shown no such signs, including cod, witch flounder, and redfish. Stocks of invertebrates, in contrast, remain at record levels of abundance. 31% of stocks are overfished in the northwest Atlantic. In 1497, John Cabot became the first Western European since the Vikings to explore mainland North America and one of his maj" +452,"have increased in strength, which is apparently not the case. Furthermore, it is clear from statistical analyses of annual tropical cyclones that these changes do not display multidecadal cyclicity. Therefore, these changes in SST must be caused by human activities. The ocean mixed layer plays an important role in heat storage over seasonal and decadal time scales, whereas deeper layers are affected over millennia and have a heat capacity about 50 times that of the mixed layer. This heat uptake provides a time-lag for climate change but it also results in thermal expansion of the oceans which contributes to sea level rise. 21st-century global warming will probably result in an equilibrium sea-level rise five times greater than today, whilst melting of glaciers, including that of the Greenland ice sheet, expected to have virtually no effect during the 21st century, will likely result in a sea-level rise of over a millennium. == Theories of natural delimitation between the Atlantic and P" +453,"Arthur Schopenhauer ( ; ; 22 February 1788 – 21 September 1860) was a German philosopher. He is known for his 1818 work The World as Will and Representation (expanded in 1844), which characterizes the phenomenal world as the manifestation of a blind and irrational noumenal will. Building on the transcendental idealism of Immanuel Kant (1724–1804), Schopenhauer developed an atheistic metaphysical and ethical system that rejected the contemporaneous ideas of German idealism. Schopenhauer was among the first thinkers in Western philosophy to share and affirm significant tenets of Indian philosophy, such as asceticism, denial of the self, and the notion of the world-as-appearance.See the book-length study about oriental influences on the genesis of Schopenhauer's philosophy by Urs App: Schopenhauer's Compass. An Introduction to Schopenhauer's Philosophy and its Origins. Wil: UniversityMedia, 2014 () * His work has been described as an exemplary manifestation of philosophical pessimism. * S" +454,"terms. In one letter, she wrote: You are unbearable and burdensome, and very hard to live with; all your good qualities are overshadowed by your conceit, and made useless to the world simply because you cannot restrain your propensity to pick holes in other people. His mother, Johanna, was generally described as vivacious and sociable. She died 24 years later. Some of Arthur's negative opinions about women may be rooted in his troubled relationship with his mother.Durant, Will, The Story of Philosophy, Garden City Publishing Co., Inc., New York, p. 350 Arthur moved to Hamburg to live with his friend Jean Anthime, who was also studying to become a merchant. === Education === He moved to Weimar but did not live with his mother, who even tried to discourage him from coming by explaining that they would not get along very well. Their relationship deteriorated even further due to their temperamental differences. He accused his mother of being financially irresponsible, flirtatious and seeki" +455,"Forest and writing his dissertation, On the Fourfold Root of the Principle of Sufficient Reason. Schopenhauer completed his dissertation at about the same time as the French army was defeated at the Battle of Leipzig. He became irritated by the arrival of soldiers in the town and accepted his mother's invitation to visit her in Weimar. She tried to convince him that her relationship with Gerstenbergk was platonic and that she had no intention of remarrying. But Schopenhauer remained suspicious and often came in conflict with Gerstenbergk because he considered him untalented, pretentious, and nationalistic. His mother had just published her second book, Reminiscences of a Journey in the Years 1803, 1804, and 1805, a description of their family tour of Europe, which quickly became a hit. She found his dissertation incomprehensible and said it was unlikely that anyone would ever buy a copy. In a fit of temper Arthur told her that people would read his work long after the rubbish she wrote" +456,"Gerstenbergk also deteriorated. She informed him about their financial troubles as the banking house of A. L. Muhl in Danzig—in which her mother invested their whole savings and Arthur a third of his—was near bankruptcy. Arthur offered to share his assets, but his mother refused and became further enraged by his insulting comments. The women managed to receive only thirty percent of their savings while Arthur, using his business knowledge, took a suspicious and aggressive stance towards the banker and eventually received his part in full. The affair additionally worsened the relationships among all three members of the Schopenhauer family. He shortened his stay in Italy because of the trouble with Muhl and returned to Dresden. Disturbed by the financial risk and the lack of responses to his book he decided to take an academic position since it provided him with both income and an opportunity to promote his views. He contacted his friends at universities in Heidelberg, Göttingen and Ber" +457,"to remain there for the rest of his life, except for a few short journeys. He lived alone except for a succession of pet poodles named Atman and Butz. In 1836, he published On the Will in Nature. In 1838, he sent his essay On the Freedom of the Will to the contest of the Royal Norwegian Society of Sciences in 1838 and won the prize in 1839. He sent another essay, On the Basis of Morality , to the Royal Danish Society of Sciences in 1839, but did not win the (1840) prize despite being the only contestant. The Society was appalled that several distinguished contemporary philosophers were mentioned in a very offensive manner, and claimed that the essay missed the point of the set topic and that the arguments were inadequate. Schopenhauer, who had been very confident that he would win, was enraged by this rejection. He published both essays as The Two Basic Problems of Ethics. The first edition, published September 1840 but with an 1841 date, again failed to draw attention to his philosoph" +458,"but was at peace with dying. He died of pulmonary-respiratory failureDale Jacquette, The Philosophy of Schopenhauer, Routledge, 2015: Biographical sketch . on 21 September 1860 while sitting at home on his couch. He died at the age of 72 and had a funeral conducted by a Lutheran minister.Schopenhauer: his life and philosophy by H. Zimmern – 1932 – G. Allen & Unwin. == Philosophy == === Theory of perception === In November 1813 Goethe invited Schopenhauer to help him on his Theory of Colours. Although Schopenhauer considered colour theory a minor matter,Letter to Goethe on 23 January 1816: Ich weiß, daß durch mich die Wahrheit geredet hat, – in dieser kleinen Sache, wie dereinst in größern. he accepted the invitation out of admiration for Goethe. Nevertheless, these investigations led him to his most important discovery in epistemology: finding a demonstration for the a priori nature of causality. Kant openly admitted that it was Hume's skeptical assault on causality that motivated the" +459,"as Will and Representation and again in his two prize essays on ethics, On the Freedom of the Will and On the Basis of Morality. No individual human actions are free, Schopenhauer argues, because they are events in the world of appearance and thus are subject to the principle of sufficient reason: a person's actions are a necessary consequence of motives and the given character of the individual human. Necessity extends to the actions of human beings just as it does to every other appearance, and thus we cannot speak of freedom of individual willing. Albert Einstein quoted the Schopenhauerian idea that a man can do as he will, but not will as he will. Einstein, Albert (1935). The World as I See It, p. 14. Snowball Publishing. . Yet the will as thing in-itself is free, as it exists beyond the realm of representation and thus is not constrained by any of the forms of necessity that are part of the principle of sufficient reason. According to Schopenhauer, salvation from our miserable exi" +460,"of sufficient reason. The egoistic individual who focuses his aims on his own interests has to deal with empirical laws as well as he can. What is relevant for ethics are individuals who can act against their own self-interest. If we take a man who suffers when he sees his fellow men living in poverty and consequently uses a significant part of his income to support their needs instead of his own pleasures, then the simplest way to describe this is that he makes less distinction between himself and others than is usually made. Regarding how things appear to us, the egoist asserts a gap between two individuals, but the altruist experiences the sufferings of others as his own. In the same way a compassionate man cannot hurt animals, though they appear as distinct from himself. What motivates the altruist is compassion. The suffering of others is for him not a cold matter to which he is indifferent, but he feels connectiveness to all beings. Compassion is thus the basis of morality. ====" +461,"Jurisprudence and Politics, §127, trans. Payne (p. 255). By his own admission, Schopenhauer did not give much thought to politics, and several times he wrote proudly of how little attention he paid to political affairs of [his] day . In a life that spanned several revolutions in French and German government, and a few continent-shaking wars, he maintained his position of minding not the times but the eternities . He wrote many disparaging remarks about Germany and the Germans. A typical example is: For a German it is even good to have somewhat lengthy words in his mouth, for he thinks slowly, and they give him time to reflect. The World as Will and Representation, Vol. 2, Ch. 12 ==== Punishment ==== The State, Schopenhauer claimed, punishes criminals to prevent future crimes. It places beside every possible motive for committing a wrong a more powerful motive for leaving it undone, in the inescapable punishment. Accordingly, the criminal code is as complete a register as possible of co" +462,"succeeds in withdrawing from the mass, or rather raising herself above the mass, she grows ceaselessly and more than a man. Safranski (1990), Chapter 24. p. 348. ==== Pederasty ==== In the third, expanded edition of The World as Will and Representation (1859), Schopenhauer added an appendix to his chapter on the Metaphysics of Sexual Love. He wrote that pederasty has the benefit of preventing ill-begotten children. Concerning this, he stated that the vice we are considering appears to work directly against the aims and ends of nature, and that in a matter that is all important and of the greatest concern to her it must in fact serve these very aims, although only indirectly, as a means for preventing greater evils. Schopenhauer ends the appendix with the statement that by expounding these paradoxical ideas, I wanted to grant to the professors of philosophy a small favour. I have done so by giving them the opportunity of slandering me by saying that I defend and commend pederasty. ====" +463,"hunter. [Sir William Harris] describes how he shot his first elephant, a female. The next morning he went to look for the dead animal; all the other elephants had fled from the neighborhood except a young one, who had spent the night with its dead mother. Forgetting all fear, he came toward the sportsmen with the clearest and liveliest evidence of inconsolable grief, and put his tiny trunk round them in order to appeal to them for help. Harris says he was then filled with real remorse for what he had done, and felt as if he had committed a murder. On the basis of morality, § 19. Schopenhauer was very attached to his succession of pet poodles. He criticized Spinoza's His contempt for animals, who, as mere things for our use, are declared by him to be without rights, ... in conjunction with Pantheism, is at the same time absurd and abominable. The World as Will and Representation, Vol. 2, Chapter 50. belief that animals are a mere means for the satisfaction of humans.Spinoza, Ethics, Pt." +464,"Philosophical Study. Ashgate Publishing, Ltd., 2004, p. 22. Buddhist nirvāṇa is not equivalent to the condition that Schopenhauer described as denial of the will. Nirvāṇa is not the extinguishing of the person as some Western scholars have thought, but only the extinguishing (the literal meaning of nirvana) of the flames of greed, hatred, and delusion that assail a person's character.John J. Holder, Early Buddhist Discourses. Hackett Publishing Company, 2006, p. xx. Schopenhauer made the following statement in his discussion of religions: Schopenhauer is often said to be the first modern Western philosopher to attempt integration of his work with Eastern ways of thinking. That he was the first is true, but the claim that he was influenced by Indian thought needs qualification. There is a remarkable correspondence in broad terms between some central Schopenhauerian doctrines and Buddhism: notably in the views that empirical existence is suffering, that suffering originates in desires, a" +465,"Will and Representation: If the reader has also received the benefit of the Vedas, the access to which by means of the Upanishads is in my eyes the greatest privilege which this still young century (1818) may claim before all previous centuries, if then the reader, I say, has received his initiation in primeval Indian wisdom, and received it with an open heart, he will be prepared in the very best way for hearing what I have to tell him. It will not sound to him strange, as to many others, much less disagreeable; for I might, if it did not sound conceited, contend that every one of the detached statements which constitute the Upanishads, may be deduced as a necessary result from the fundamental thoughts which I have to enunciate, though those deductions themselves are by no means to be found there.The World as Will and Representation Preface to the first edition, p. xiii == Thoughts on other philosophers == === Giordano Bruno and Spinoza === Schopenhauer saw Bruno and Spinoza as philos" +466,"to address issues they would otherwise have completely ignored, and in doing so he changed them markedly. The French writer Maupassant commented that to-day even those who execrate him seem to carry in their own souls particles of his thought .Beside Schopenhauer's Corpse Other philosophers of the 19th century who cited his influence include Hans Vaihinger, Volkelt, Solovyov and Weininger. Schopenhauer was well read by physicists, most notably Einstein, Schrödinger, Wolfgang Pauli, and Majorana. Einstein described Schopenhauer's thoughts as a continual consolation and called him a genius. In his Berlin study three figures hung on the wall: Faraday, Maxwell, Schopenhauer.Howard (1997). p. 87 Konrad Wachsmann recalled: He often sat with one of the well-worn Schopenhauer volumes, and as he sat there, he seemed so pleased, as if he were engaged with a serene and cheerful work. Howard (1997). p. 92 When Erwin Schrödinger discovered Schopenhauer ( the greatest savant of the West ) he conside" +467,"colorum physiologica), 1830. * The World as Will and Representation (alternatively translated as The World as Will and Idea; original German is Die Welt als Wille und Vorstellung): vol. 1, 1818–1819, vol. 2, 1844 ** Vol. 1 Dover edition 1966, ** Vol. 2 Dover edition 1966, ** Peter Smith Publisher hardcover set 1969, ** Everyman Paperback combined abridged edition (290 pp.) ** The Longman Library of Primary Sources in Philosophy, vol. 1, 2008; vol. 2, 2010. Title translated as The World as Will and Presentation, rather than Representation. * The Art of Being Right (Eristische Dialektik: Die Kunst, Recht zu Behalten), 1831 * On the Will in Nature (Ueber den Willen in der Natur), 1836 * On the Freedom of the Will (Ueber die Freiheit des menschlichen Willens), 1838 * On the Basis of Morality (Ueber die Grundlage der Moral), 1839 * Contains On the Freedom of the Will and On the Basis of Morality. * Freely available from Internet Archive. Contains Preisschrift über die Freiheit des Willens a" +468,"études en esthétique musicale, Université du Québec à Montréal, Département de musique, 2000. * Zimmern, Helen, Arthur Schopenhauer, his Life and Philosophy, London, Longman, and Co., 1876. * Kastrup, Bernardo. Decoding Schopenhauer's Metaphysics – The key to understanding how it solves the hard problem of consciousness and the paradoxes of quantum mechanics. Winchester/Washington, iff Books, 2020. * de Botton, Alain: The Consolations of Philosophy. Hamish Hamilton, London 2000. (Chapter: Consolation for a Broken Heart). === Fiction === * Poschenrieder, Christoph: Die Welt ist im Kopf. Diogenes, Zürich 2010, (The novel accompanies Schopenhauer on a trip to Italy). * Yalom, Irvin D.: The Schopenhauer Cure. HarperCollins, New York City 2005, (The novel switches between the current events happening around a therapy group and the psychobiography of Arthur Schopenhauer). * Kortmann, Christian: Happy Hour Schopenhauer. Roman einer Bibliotherapie. Turia + Kant, Wien + Berlin 2022, (In the nov" +469,"Angola, officially the Republic of Angola, is a country on the west-central coast of Southern Africa. It is the second-largest Lusophone (Portuguese-speaking) country in both total area and population and is the seventh-largest country in Africa. It is bordered by Namibia to the south, the Democratic Republic of the Congo to the north, Zambia to the east, and the Atlantic Ocean to the west. Angola has an exclave province, the province of Cabinda, that borders the Republic of the Congo and the Democratic Republic of the Congo. The capital and most populous city is Luanda. Angola has been inhabited since the Paleolithic Age. Its formation as a nation-state originates from the Kingdom of Kongo, the hegemonic state of a number of other Kikongo-speaking kingdoms that flourished in and after the 14th century. The Kingdom of Kongo became extremely wealthy and powerful through establishing the Atlantic slave trade with the Portuguese Empire. Its first explorers established relations with Kongo" +470,"It had a strong economy, based on the industries of copper, ivory, salt, hides, and, to a lesser extent, slaves. The transition from a feudal system of slavery to a capitalist one with Portugal would prove crucial to the history of the Kingdom of Kongo.Heywood, Linda M. Slavery and Its Transformation in the Kingdom of Kongo: 1491–1800. The Journal of African History 50, no. 1 (2009): 1–22. http://www.jstor.org/stable/40206695. As relations between Kongo and Portugal grew in the early 16th century, trade between the kingdoms also increased. Most of the trade was in palm cloth, copper, and ivory, but also increasing numbers of slaves. Kongo exported few slaves, and its slave market had remained internal. But, following the development of a successful sugar-growing colony after Portuguese settlement of São Tomé, Kongo became a major source of slaves for the island's traders and plantations. Correspondence by King Afonso documents the purchase and sale of slaves within the country. His acc" +471,"also expanded inward from Benguela, but until the late 19th century the inroads from Luanda and Benguela were very limited. Hamstrung by a series of political upheavals in the early 1800s, Portugal was slow to mount a large scale annexation of Angolan territory. The slave trade was abolished in Angola in 1836, and in 1854 the colonial government freed all its existing slaves. Four years later, a more progressive administration appointed by Portugal abolished slavery altogether. However, these decrees remained largely unenforceable, and the Portuguese depended on assistance from the British Royal Navy and what became known as the Blockade of Africa to enforce their ban on the slave trade. This coincided with a series of renewed military expeditions into the bush. By the mid-nineteenth century Portugal had established its dominion as far north as the Congo River and as far south as Mossâmedes. Until the late 1880s, Portugal entertained proposals to link Angola with its colony in Mozambiq" +472,"ratified by the Alvor Agreement later that month, which called for general elections and set the country's independence date for 11 November 1975. All three factions, however, followed up on the ceasefire by taking advantage of the gradual Portuguese withdrawal to seize various strategic positions, acquire more arms, and enlarge their militant forces. The rapid influx of weapons from numerous external sources, especially the Soviet Union and the United States, as well as the escalation of tensions between the nationalist parties, fueled a new outbreak of hostilities. With tacit American and Zairean support the FNLA began massing large numbers of troops in northern Angola in an attempt to gain military superiority. Meanwhile, the MPLA began securing control of Luanda, a traditional Ambundu stronghold. Sporadic violence broke out in Luanda over the next few months after the FNLA attacked the MPLA's political headquarters in March 1975. The fighting intensified with street clashes in Apri" +473,"in the northwest of the country. Angola had a 2018 Forest Landscape Integrity Index mean score of 8.35/10, ranking it 23rd globally out of 172 countries. In Angola forest cover is around 53% of the total land area, equivalent to 66,607,380 hectares (ha) of forest in 2020, down from 79,262,780 hectares (ha) in 1990. In 2020, naturally regenerating forest covered 65,800,190 hectares (ha) and planted forest covered 807,200 hectares (ha). Of the naturally regenerating forest 40% was reported to be primary forest (consisting of native tree species with no clearly visible indications of human activity) and around 3% of the forest area was found within protected areas. For the year 2015, 100% of the forest area was reported to be under public ownership. ===Climate=== Like the rest of tropical Africa, Angola experiences distinct, alternating rainy and dry seasons. In the north, the rainy season may last for as long as seven months—usually from September to April, with perhaps a brief slackenin" +474,"97,602 986,363 15 Moxico Luena 223,023 758,568 16 Namibe Moçâmedes 57,091 495,326 17 Uíge Uíge 58,698 1,483,118 18 Zaire M'banza-Kongo 40,130 594,428 ====Exclave of Cabinda==== With an area of approximately , the Northern Angolan province of Cabinda is unusual in being separated from the rest of the country by a strip, some wide, of the Democratic Republic of Congo along the lower Congo River. Cabinda borders the Congo Republic to the north and north-northeast and the DRC to the east and south. The town of Cabinda is the chief population centre. According to a 1995 census, Cabinda had an estimated population of 600,000, approximately 400,000 of whom are citizens of neighboring countries. Population estimates are, however, highly unreliable. Consisting largely of tropical forest, Cabinda produces hardwoods, coffee, cocoa, crude rubber and palm oil. The product for which it is best known, however, is its oil, which has given it the nickname, the Kuwait of Africa . Cabinda's petroleum pro" +475,"MFW4A. Retrieved 9 August 2013. In the period 2001–10, Angola had the world's highest annual average GDP growth, at 11.1%. In 2004, the Exim Bank of China approved a $2 billion line of credit to Angola, to be used for rebuilding Angola's infrastructure, and to limit the influence of the International Monetary Fund there. China is Angola's biggest trade partner and export destination as well as a significant source of imports. Bilateral trade reached $27.67 billion in 2011, up 11.5% year-on-year. China's imports, mainly crude oil and diamonds, increased 9.1% to $24.89 billion while China's exports to Angola, including mechanical and electrical products, machinery parts and construction materials, surged 38.8%. The oil glut led to a local price for unleaded gasoline of £0.37 a gallon. As of 2021, the biggest import partners were the European Union, followed by China, Togo, the United States, and Brazil. More than half of Angola's exports go to China, followed by a significantly smaller a" +476,"kwanza , Visão (Lisbon), 993, 15 May 2012, pp. 50–54 thumb|left|Corporate headquarters in Luanda Angola has upgraded critical infrastructure, an investment made possible by funds from the country's development of oil resources.The New Prosperity: Strategies for Improving Well-Being in Sub-Saharan Africa Tony Blair Africa Governance Initiative 1 May 2013 According to a report, just slightly more than ten years after the end of the civil war Angola's standard of living has overall greatly improved. Life expectancy, which was just 46 years in 2002, reached 51 in 2011. Mortality rates for children fell from 25 per cent in 2001 to 19 per cent in 2010 and the number of students enrolled in primary school has tripled since 2001.The New Prosperity: Strategies for Improving Well-Being in Sub-Saharan Africa Report by The Boston Consulting Group and Tony Blair Africa Governance Initiative, May 2013 However, at the same time the social and economic inequality that has characterised the country for" +477,"Highway. Travel on highways outside of towns and cities in Angola (and in some cases within) is often not best advised for those without four-by-four vehicles. While reasonable road infrastructure has existed within Angola, time and war have taken their toll on the road surfaces, leaving many severely potholed, littered with broken asphalt. In many areas drivers have established alternative tracks to avoid the worst parts of the surface, although careful attention must be paid to the presence or absence of landmine warning markers by the side of the road. The Angolan government has contracted the restoration of many of the country's roads. The road between Lubango and Namibe, for example, was completed recently with funding from the European Union,ec.europa.eu: 1992 and is comparable to many European main routes. Completing the road infrastructure is likely to take some decades, but substantial efforts are already being made. ===Telecommunications=== thumb|right|Luanda's construction b" +478,"Angola are those originally spoken by the different ethnic groups and Portuguese, introduced during the Portuguese colonial era. The most widely spoken indigenous languages are Umbundu, Kimbundu and Kikongo, in that order. Portuguese is the official language of the country. Although the exact numbers of those fluent in Portuguese or who speak Portuguese as a first language are unknown, a 2012 study mentions that Portuguese is the first language of 39% of the population.Silva, José António Maria da Conceição (2004) Angola . 7th World Urban Forum In 2014, a census carried out by the Instituto Nacional de Estatística in Angola mentions that 71.15% of the nearly 25.8 million inhabitants of Angola (meaning around 18.3 million people) use Portuguese as a first or second language. According to the 2014 census, Portuguese is spoken by 71.1% of Angolans, Umbundu by 23%, Kikongo by 8.2%, Kimbundu by 7.8%, Chokwe by 6.5%, Nyaneka by 3.4%, Ngangela by 3.1%, Fiote by 2.4%, Kwanyama by 2.3%, Muhumbi" +479,"suspected of being infected. As many as 369 may have died. The outbreak began in the capital, Luanda, and spread to at least 16 of the 18 provinces. === Education === Although by law education in Angola is compulsory and free for eight years, the government reports that a percentage of pupils are not attending due to a lack of school buildings and teachers. Botswana . 2005 Findings on the Worst Forms of Child Labor. Bureau of International Labor Affairs, U.S. Department of Labor (2006). This article incorporates text from this source, which is in the public domain. Pupils are often responsible for paying additional school-related expenses, including fees for books and supplies. In 1999, the gross primary enrollment rate was 74 per cent and in 1998, the most recent year for which data are available, the net primary enrollment rate was 61 per cent. Gross and net enrollment ratios are based on the number of pupils formally registered in primary school and therefore do not necessarily refl" +480,"have historic roots in the martial art Capoeira Angola and Batuque which were practised by enslaved African Angolans transported as part of the Atlantic slave trade. ==See also== *Outline of Angola *Index of Angola-related articles ==Notes== ==References== ==Further reading== * * Birmingham, David (2006) Empire in Africa: Angola and its Neighbors, Ohio University Press: Athens, Ohio. * Bösl, Anton (2008) Angola's Parliamentary Elections in 2008. A Country on its Way to One-Party-Democracy, KAS Auslandsinformationen 10/2008. Die Parlamentswahlen in Angola 2008 * Cilliers, Jackie and Christian Dietrich, Eds. (2000). Angola's War Economy: The Role of Oil and Diamonds. Pretoria, South Africa, Institute for Security Studies. * Global Witness (1999). A Crude Awakening, The Role of Oil and Banking Industries in Angola's Civil War and the Plundering of State Assets. London, UK, Global Witness. A Crude Awakening * Hodges, Tony (2001). Angola from Afro-Stalinism to Petro-Diamond Capitalism. Oxfo" +481,"Demographic features of the population of Angola include population density, ethnicity, education level, health of the populace, economic status, religious affiliations and other aspects. According to 2014 census data, Angola had a population of 25,789,024 inhabitants in 2014. Ethnically, there are three main groups, each speaking a Bantu language: the Ovimbundu who represent 37% of the population, the Ambundu with 25%, and the Bakongo 11%. Other numerically important groups include the closely interrelated Chokwe and Lunda, the Ganguela and Nyaneka-Khumbi (in both cases classification terms that stand for a variety of small groups), the Ovambo, the Herero, the Xindonga and scattered residual groups of San. In addition, mixed race (European and African) people amount to about 7%, with nearly 1% of the population being whites, mainly ethnically Portuguese. As a former overseas territory of Portugal until 1975, Angola possesses a Portuguese population of over 200,000, a number that has b" +482,"222 814 258 393 481 207 1.55 65–69 148 455 180 188 328 643 1.06 70–74 93 883 119 433 213 316 0.69 75–79 53 235 71 070 124 305 0.40 80+ 38 905 58 174 97 079 0.31 Age group Male Female Total Percent 0–14 7 095 188 7 174 164 14 269 352 45.84 15–64 7 738 514 8 356 465 16 094 979 51.71 65+ 334 478 428 865 763 343 2.45 ==Vital statistics== Registration of vital events in Angola is not complete. The website Our World in Data prepared the following estimates based on statistics from the Population Department of the United Nations. Mid-year population (thousands) Live births (thousands) Deaths (thousands) Natural change (thousands) Crude birth rate (per 1000) Crude death rate (per 1000) Natural change (per 1000) Total fertility rate (TFR) Infant mortality (per 1000 live births) Life expectancy (in years) 1950 4 478 207 119 88 46.1 26.5 19.7 5.77 181.3 36.35 1951 4 570 212 122 91 46.5 26.7 19.8 5.83 180.9 36.39 1952 4 664 220 125 95 47.2 26.8 20.4 5.95 180.0 36.52 1953 4 759 227 128 100 47.8 26." +483,"5.30 48.1 61.64 2022 35 635 1 358 253 1 105 38.1 7.1 31.0 5.21 50.2 61.7 2023 36 750 1 381 254 1 127 37.6 6.9 30.7 5.12 48.6 62.1 ===Fertility and Births=== Total Fertility Rate (TFR) (Wanted TFR) and Crude Birth Rate (CBR): Year Total Total Urban Urban Rural Rural Year CBR TFR CBR TFR CBR TFR 2006–2007 42.4 5.8 35.0 4.4 50.2 7.7 2011 45.5 6.3 36.5 4.6 51.8 7.7 2014 (census) 5.7 5.2 6.5 2015–16 43.4 6.2 (5.2) 40.6 5.3 (4.4) 48.4 8.2 (7.1) ==Other demographics statistics== Demographic statistics according to the World Population Review in 2022. *One birth every 23 seconds *One death every 2 minutes *One net migrant every 360 minutes *Net gain of one person every 29 seconds The following demographic statistics are from the CIA World Factbook, unless otherwise indicated. ===Population=== :34,795,287 (2022 est.) :30,355,880 (July 2018 est.) :29,310,273 (July 2017 est.) ===Age structure=== :0-14 years: 47.83% (male 7,758,636 /female 7,797,869) :15-24 years: 18.64% (male 2,950,999 /female 3," +484,"The current political regime in Angola is presidentialism, in which the President of the Republic is also head of state and government; it is advised by a Council of Ministers, which together with the President form the national executive power. Legislative power rests with the 220 parliamentarians elected to the National Assembly. The President of the Republic, together with the parliament, appoints the majority of the members of the two highest bodies of the judiciary, that is, the Constitutional Court and the Supreme Court. The judiciary is still made up of the Court of Auditors and the Supreme Military Court. The Angolan government is composed of three branches of government: executive, legislative and judicial. For decades, political power has been concentrated in the presidency with the People's Movement for the Liberation of Angola. == History == Since the adoption of a new constitution in 2010, the politics of Angola takes place in a framework of a presidential republic, whereb" +485,"party. The only relevant post that is not directly appointed by the President is the vice-president, which is the second in the winning party. José Eduardo dos Santos stepped down as President of Angola after 38 years in 2017, being peacefully succeeded by João Lourenço, Santos' chosen successor. ==Legislative branch== The National Assembly (Assembleia Nacional) has 223 members, elected for a four-year term, 130 members by proportional representation, 90 members in provincial districts, and 3 members to represent Angolans abroad. The general elections in 1997 were rescheduled for 5 September 2008. The ruling party MPLA won 82% (191 seats in the National Assembly) and the main opposition party won only 10% (16 seats). The elections however have been described as only partly free but certainly not fair. A White Book on the elections in 2008 lists up all irregularities surrounding the Parliamentary elections of 2008.http://www.kas.de/proj/home/pub/8/2/year-2009/dokument_id-17396/index.htm" +486,"The economy of Angola remains heavily influenced by the effects of four decades of conflict in the last part of the 20th century, the war for independence from Portugal (1961–75) and the subsequent civil war (1975–2002). Poverty since 2002 is reduced over 50% and a third of the population relies on subsistence agriculture. Since 2002, when the 27-year civil war ended, government policy prioritized the repair and improvement of infrastructure and strengthening of political and social institutions. During the first decade of the 21st century, Angola's economy was one of the fastest-growing in the world,Birgitte Refslund Sørensen and Marc Vincent. Caught Between Borders: Response Strategies of the Internally Displaced, 2001. Page 17. with reported annual average GDP growth of 11.1 percent from 2001 to 2010. High international oil prices and rising oil production contributed to strong economic growth, although with high inequality, at that time. 2022 trade surplus was $30 billion, compared" +487,"industrial or agricultural enterprises, nor had they actively educated the population. Upon independence Angola thus found itself without markets or expertise to maintain even minimal economic growth. As a result, the government intervened, nationalizing most businesses and farms abandoned by the Portuguese. It established state farms to continue producing coffee, sugar, and sisal, and it took over the operations of all factories to maintain production. These attempts usually failed, primarily because of the lack of experienced managers and the continuing disruptions in rural areas caused by the UNITA insurgency. Only the petroleum sector continued to operate successfully, and by 1980 this sector had helped the gross domestic product reach US$3.6 billion, its highest level up to 1988. In the face of serious economic problems and the continuing war throughout the countryside, in 1987 the government announced plans to liberalize economic policies and promote private investment and involv" +488,"14 Angolan Kwanza 1.58 2000 9,135 91,666 Angolan Kwanza 1.96 2005 28,860 2,515,452 Angolan Kwanza 4.73 The following table shows the main economic indicators in 1980–2023. Inflation below 5% is in green. Year GDP(in bn. US$ PPP) GDP per capita(in US$ PPP) GDP (in bn. US$ nominal) GDP growth(real) Inflation rate(in Percent) Government debt(in % of GDP) 1980 10.9 1,317 6.6 2.4% 46.7% n/a 1981 11.4 1,341 6.2 −4.4% 1.3% n/a 1982 12.1 1,388 6.2 0.0% 1.8% n/a 1983 13.1 1,464 6.5 4.2% 1.8% n/a 1984 14.4 1,567 6.9 6.0% 1.8% n/a 1985 15.4 1,484 8.5 3.5% 1.8% n/a 1986 16.1 1,515 7.9 2.9% 1.8% n/a 1987 17.2 1,576 9.1 4.1% 1.8% n/a 1988 18.9 1,685 9.8 6.1% 1.8% n/a 1989 19.6 1,705 11.4 0.0% 1.8% n/a 1990 19.7 1,664 12.6 −3.5% 1.8% n/a 1991 22.8 1,865 12.2 1.0% 85.3% n/a 1992 26.0 2,057 9.4 11.4% 299.1% n/a 1993 29.6 2,067 6.8 10.7% 1,379.5% n/a 1994 33.3 2,475 5.0 10.5% 949.8% n/a 1995 37.5 2,698 6.2 10.4% 2,672.2% n/a 1996 42.5 2,955 8.0 11.2% 4,146.0% n/a 1997 46.4 3,120 9.4 7.3% 221.9% n/a 1998" +489,"government began Operation Brilliant, an anti-smuggling investigation that arrested and deported 250,000 smugglers between 2003 and 2006. Rafael Marques, a journalist and human rights activist, described the diamond industry in his 2006 Angola's Deadly Diamonds report as plagued by murders, beatings, arbitrary detentions and other human rights violations. Marques called on foreign countries to boycott Angola's conflict diamonds . In December 2014, the Bureau of International Labor Affairs issued a List of Goods Produced by Child Labor or Forced Labor that classified Angola as one of the major diamond-producing African countries relying on both child labor and forced labor. The U.S. Department of Labor reported that there is little publicly available information on [Angola's] efforts to enforce child labor law . Diamonds accounted for 1.48% of Angolan exports in 2014. ===Iron=== Under Portuguese rule, Angola began mining iron in 1957, producing 1.2 million tons in 1967 and 6.2 million t" +490,"Transport in Angola comprises: == Roads == Two trans-African automobile routes pass through Angola: * the Tripoli-Cape Town Highway * the Beira-Lobito Highway Map of Trans-African Highways.PNG|Map of Trans-African Highways. Walking home.jpg|Walking home on EN 105. Tired are they.jpg|Donkey-drawn carts. Transportation Jingu.jpg|Three-wheeled motorcycles. The riches transportation.jpg|Trucks. Midd Town Luanda.jpg|Automobiles in Luanda. The Nowhere road.jpg|New highway (2019). == Railways == There are three separate railway lines in Angola: * Luanda Railway (CFL) (northern); * Benguela Railway (CFB) (central), operated by the Lobito Atlantic Railway joint venture; * Moçâmedes Railway (CFM) (southern); Reconstruction of these three lines began in 2005 and they are now all operational. The Benguela Railway connects to the Democratic Republic of the Congo. == Waterways == * 1,300 km navigable (2008) :country comparison to the world: 36 == Pipelines == * gas 352 km; liquid petroleum gas 85 km" +491,"The Angolan Armed Forces () or FAA is the military of Angola. The FAA consist of the Angolan Army (), the Angolan Navy () and the National Air Force of Angola (). Reported total manpower in 2021 was about 107,000. The FAA is headed by the Chief of the General Staff António Egídio de Sousa Santos since 2018, who reports to the minister of National Defense, currently João Ernesto dos Santos. ==History== === Roots === The FAA succeeded to the previous People's Armed Forces for the Liberation of Angola (FAPLA) following the abortive Bicesse Accord with the Armed Forces of the Liberation of Angola (FALA), armed wing of the National Union for the Total Independence of Angola (UNITA). As part of the peace agreement, troops from both armies were to be demilitarized and then integrated. Integration was never completed as UNITA and FALA went back to war in 1992. Later, consequences for FALA personnel in Luanda were harsh with FAPLA veterans persecuting their erstwhile opponents in certain areas" +492,"of the Special Forces Brigade (BRIFE, Brigada de Forças Especiais), based at Cabo Ledo, in the Bengo Province. The BRIFE includes two battalions of commandos, a battalion of special operations and sub-units of combat support and service support. The BRIFE also included the Special Actions Group (GAE, Grupo de Ações Especiais), which is presently inactive and that was dedicated to long range reconnaissance, covert and sabotage operations. In the Cabo Ledo base is also installed the Special Forces Training School (EFFE, Escola de Formação de Forças Especiais). Both the BRIFE and the EFFE are directly under the Directorate of Special Forces of the General Staff of the Armed Forces. The marines (fuzileiros navais) constitute the Marines Brigade of the Angolan Navy. The Marines Brigade is not permanently dependent of the Directorate of Special Forces, but can detach their units and elements to be put under the command of that body for the conduction of exercises or real operations. Since th" +493,"The foreign relations of Angola are based on Angola's strong support of U.S. foreign policy as the Angolan economy is dependent on U.S. foreign aid. From 1975 to 1989, Angola was aligned with the Eastern bloc, in particular the Soviet Union, Libya, and Cuba. Since then, it has focused on improving relationships with Western countries, cultivating links with other Portuguese-speaking countries, and asserting its own national interests in Central Africa through military and diplomatic intervention. In 1993, it established formal diplomatic relations with the United States. It has entered the Southern African Development Community as a vehicle for improving ties with its largely Anglophone neighbors to the south. Zimbabwe and Namibia joined Angola in its military intervention in the Democratic Republic of the Congo, where Angolan troops remain in support of the Joseph Kabila government. It also has intervened in the Republic of the Congo (Brazzaville) in support of Denis Sassou-Nguesso in" +494,"has an embassy in Buenos Aires. * Argentina has an embassy in Luanda. *Argentine Ministry of Foreign Relations: list of bilateral treaties with Angola (in Spanish only) 12 November 1975 See Angola–Brazil relations Commercial and economic ties dominate the relations of each country. Parts of both countries were part of the Portuguese Empire from the early 16th century until Brazil's independence in 1822. As of November 2007, trade between the two countries is booming as never before ANGOLA-BRAZIL: Portuguese – the Common Language of Trade by Mario de Queiroz, ipsnews.net, 13 November 2007 * Angola has an embassy in Brasília and a consulates-general in Rio de Janeiro and São Paulo. * Brazil has an embassy in Luanda. 1 February 1978 Both countries established diplomatic relations on 1 February 1978 Canada-Angola relations were established in 1978, and Canada is accredited to Angola from its Embassy in Harare, Zimbabwe. Ties have grown since the end of the civil war in 2002, with increased" +495,"both governments by exposing corruption and illicit arms deals. Following French President Nicolas Sarkozy's visit in 2008, relations have improved. * Angola has an embassy in Paris. * France has an embassy in Luanda. 16 August 1979 Both countries established diplomatic relations on 16 August 1979 See Angola–Germany relations * Angola has an embassy in Berlin. * Germany has an embassy in Luanda. 14 April 1975 * Angola has an embassy to the Holy See based in Rome. * Holy See has an Apostolic Nuncio to Angola. 4 June 1976 See Angola–Italy relations * Angola has an embassy in Rome. * Italy has an embassy in Luanda. 18 February 1976 * Angola has an embassy in The Hague and a consulate-general in Rotterdam. * Netherlands has an embassy in Luanda. 9 March 1976 See Angola–Portugal relations Angola-Portugal relations have significantly improved since the Angolan government abandoned communism and nominally embraced democracy in 1991, embracing a pro-U.S. and to a lesser degree pro-Europe forei" +496,"Albert Sidney Johnston (February 2, 1803 – April 6, 1862) was an American military officer who served as a general in three different armies: the Texian Army, the United States Army, and the Confederate States Army. He saw extensive combat during his 34-year military career, fighting actions in the Black Hawk War, the Texas-Indian Wars, the Mexican–American War, the Utah War, and the American Civil War. Considered by Confederate States President Jefferson Davis to be the finest general officer in the Confederacy before the later emergence of Robert E. Lee, he was killed early in the Civil War at the Battle of Shiloh on April 6, 1862. Johnston was the highest-ranking officer on either side, killed during the entire war. Davis believed the loss of General Johnston was the turning point of our fate. Johnston was unrelated to Confederate general Joseph E. Johnston. ==Early life and education== Johnston was born in Washington, Kentucky, the youngest son of Dr. John and Abigail (Harris) John" +497,"mainly, that our division was saved from a cruel slaughter... The coolness and magnificent presence [that he] displayed on this field... left an impression on my mind that I have never forgotten. Roland, pp. 46 General Taylor considered Johnston the best soldier he had ever commanded. Johnston resigned from the army just after the battle of Monterrey in October 1846. He had promised his wife, Eliza, that he would only volunteer for six months' service. In addition, President James K. Polk's administration's preference for officers associated with the Democratic Party prevented the promotion of those, such as Johnston, who were perceived as Whigs: He remained on his plantation after the war until he was appointed by later 12th president Zachary Taylor to the U.S. Army as a major and was made a paymaster in December 1849 for a district of Texas encompassing the military posts from the upper Colorado River to the upper Trinity River. He served in that role for more than five years, making" +498,"discover the robbers of the last two parties that were attacked. They are supposed to be vagabonds from the Shoshonee (sic) or Snake and Bannack (sic) Indians, whose chiefs deny any complicity with these predatory bands. There is abundant evidence to prove that these robber bands are accompanied by white men, and probably instigated and led by them. On that account I am inclined to believe the disclaimer of the Indians referred to, of having any knowledge of the robberies or any share in the plunder. The only evidence of the massacre is the account of Elijah Nicholas Wilson (written in 1910, about 51 years after the incident) and oral histories.Senate of the United States; First Session of the 36th Congress, 1859–60; No. 42, p. 26Wilson, pp. 165 In late February 1860, Johnston received orders from the War Department recalling him to Washington D.C. to prepare for a new assignment. He spent 1860 in Kentucky until December 21, when he sailed for California to take command of the Departme" +499,"another 4,000 men blocking the railroad route to Tennessee at Bowling Green, Kentucky.Long, p. 119. Johnston had fewer than 40,000 men spread throughout Kentucky, Tennessee, Arkansas, and Missouri.Woodworth, p. 53. Of these, 10,000 were in Missouri under Missouri State Guard Maj. Gen. Sterling Price. Johnston did not quickly gain many recruits when he first requested them from the governors, but his more serious problem was lacking sufficient arms and ammunition for the troops he already had. As the Confederate government concentrated efforts on the units in the East, they gave Johnston small numbers of reinforcements and minimal amounts of arms and material.Woodworth, p. 55. Johnston maintained his defense by conducting raids and other measures to make it appear he had larger forces than he did, a strategy that worked for several months. Johnston's tactics had so annoyed and confused U.S. Brig. Gen. William Tecumseh Sherman in Kentucky that he became paranoid and mentally unstable. Sh" +500,"396.A Confederate battery and the beginning of some fortifications were sited across the river at Fort Heiman, but these were of little value when the U.S. flotilla appeared. Gen. Beauregard arrived at Johnston's headquarters at Bowling Green on February 4, 1862, and was given overall command of Polk's force at the western end of Johnston's line at Columbus, Kentucky.Woodworth, p. 78.After some preliminary work with Johnston, Beauregard assumed command of this force, which he named the Army of the Mississippi, on March 5, 1862, while at Jackson, Tennessee. Like the other Confederate commander, he had to withdraw to the south after the fall of the forts or be surrounded by the advancing U.S. forces. Long, p. 178. On February 6, 1862, U.S. gunboats quickly reduced the defenses of ill-sited Fort Henry, inflicting 21 casualties on the small remaining Confederate force.Woodworth, pp. 78–79.Long, p. 167. Brig. Gen. Lloyd Tilghman surrendered the 94 remaining officers and men of his approxima" +501,"Mississippi); pp. 405–409. Beauregard, p. 579. Boritt, p. 53. Connelly, Army of the Heartland: The Army of Tennessee, 1861–1862. p. 151. ( The Army retained Beauregard's chosen name... ) Connelly, Civil War Tennessee: Battles And Leaders. p. 35. Cunningham, pp. 98, 122, 397. Engle, p. 123. Hattaway, p. 163. Hess, pp. 47, 49, 112 ( ...Braxton Bragg's renamed Army of Tennessee (formerly the Army of the Mississippi)... ). Isbell, p. 102. McDonough, pp. 60, 66, 78. Kennedy, p. 48. Noe, p. 19. Williams, p. 122. Johnston's only hope was to crush Grant before Buell and others could reinforce him. He started his army in motion on April 3, intent on surprising Grant's force as soon as the next day. It was not an easy undertaking; his army had been hastily thrown together, two-thirds of the soldiers had never fired a shot in battle, and drill, discipline, and staff work were so poor that the different divisions kept stumbling into each other on the march.Woodworth, pp. 96–97.Long, p. 192 Beaureg" +502,"Griffin. Johnston's eldest son, Albert Sidney Jr. (born in Texas), had already followed him into the Confederate States Army. In 1863, Albert Jr. was on his way out of San Pedro harbor on a ferry after taking home leave in Los Angeles. While a steamer was taking on passengers from the ferry, a wave swamped the smaller boat, causing its boilers to explode. Albert Jr. was killed in the accident. Upon his passing, General Johnston received the highest praise ever given by the Confederate government: accounts were published on December 20, 1862, and after that, in the Los Angeles Star of his family's hometown. Johnston Street, Hancock Street, and Griffin Avenue, each in northeast Los Angeles, are named after the general and his family, who lived in the neighborhood. Johnston was initially buried in New Orleans. In 1866, a joint resolution of the Texas Legislature was passed to have his body moved and reinterred at the Texas State Cemetery in Austin. The re-interment occurred in 1867. Forty" +503,"An android is a humanoid robot or other artificial being often made from a flesh-like material. Historically, androids existed only in the domain of science fiction and were frequently seen in film and television, but advances in robot technology have allowed the design of functional and realistic humanoid robots.Ishiguro, Hiroshi. Android science. , Cognitive Science Society, Osaka, 2005. Retrieved on 3 October 2013. ==Terminology== The Oxford English Dictionary traces the earliest use (as Androides ) to Ephraim Chambers' 1728 Cyclopaedia, in reference to an automaton that St. Albertus Magnus allegedly created.OED at android citing Ephraim Chambers, Cyclopædia; or, a universal dictionary of arts and sciences. 1728. By the late 1700s, androides , elaborate mechanical devices resembling humans performing human activities, were displayed in exhibit halls. The term android appears in US patents as early as 1863 in reference to miniature human-like toy automatons. The term android was used" +504,"the Tokyo University of Science, has developed an android head called Saya, which was exhibited at Robodex 2002 in Yokohama, Japan. There are several other initiatives around the world involving humanoid research and development at this time, which will hopefully introduce a broader spectrum of realized technology in the near future. Now Saya is working at the Science University of Tokyo as a guide. The Waseda University (Japan) and NTT docomo's manufacturers have succeeded in creating a shape-shifting robot WD-2. It is capable of changing its face. At first, the creators decided the positions of the necessary points to express the outline, eyes, nose, and so on of a certain person. The robot expresses its face by moving all points to the decided positions, they say. The first version of the robot was first developed back in 2003. After that, a year later, they made a couple of major improvements to the design. The robot features an elastic mask made from the average head dummy. It use" +505,"Einstein, using Hanson's facial android technology mounted on KAIST's life-size walking bipedal robot body. This Einstein android, also called Albert Hubo , thus represents the first full-body walking android in history. Hanson Robotics, the FedEx Institute of Technology, and the University of Texas at Arlington also developed the android portrait of sci-fi author Philip K. Dick (creator of Do Androids Dream of Electric Sheep?, the basis for the film Blade Runner), with full conversational capabilities that incorporated thousands of pages of the author's works. In 2005, the PKD android won a first-place artificial intelligence award from AAAI. ==Use in fiction== Androids are a staple of science fiction. Isaac Asimov pioneered the fictionalization of the science of robotics and artificial intelligence, notably in his 1950s series I, Robot. One thing common to most fictional androids is that the real-life technological challenges associated with creating thoroughly human-like robots — su" +506,"Alberta is one of the thirteen provinces and territories of Canada. It is a part of Western Canada and is one of the three prairie provinces. Alberta borders British Columbia to the west, Saskatchewan to the east, the Northwest Territories to the north, and the U.S. state of Montana to the south. It is one of the only two landlocked provinces in Canada, with Saskatchewan being the other. The eastern part of the province is occupied by the Great Plains, while the western part borders the Rocky Mountains. The province has a predominantly continental climate but experiences quick temperature changes due to air aridity. Seasonal temperature swings are less pronounced in western Alberta due to occasional Chinook winds. Alberta is the fourth-largest province by area at , and the fourth-most populous, being home to 4,262,635 people. Alberta's capital is Edmonton, while Calgary is its largest city. The two are Alberta's largest census metropolitan areas. More than half of Albertans live in eit" +507,"along the southwestern boundary are largely temperate coniferous forests of the Alberta Mountain forests and Alberta–British Columbia foothills forests. The southern quarter of the province is prairie, ranging from shortgrass prairie in the southeastern corner to mixed grass prairie in an arc to the west and north of it. The central aspen parkland region extending in a broad arc between the prairies and the forests, from Calgary, north to Edmonton, and then east to Lloydminster, contains the most fertile soil in the province and most of the population. Much of the unforested part of Alberta is given over either to grain farming or cattle ranching, with mixed farming more common in the north and centre, while ranching and irrigated agriculture predominate in the south. The Alberta badlands are in southeastern Alberta, where the Red Deer River crosses the flat prairie and farmland, and features deep canyons and striking landforms. Dinosaur Provincial Park, near Brooks, showcases the badl" +508,"prairie in the summer months between July and September. The southern and east central parts of Alberta are covered by short prairie grass, which dries up as summer lengthens, to be replaced by hardy perennials such as the prairie coneflower (Ratibida), fleabane, and sage (Artemisia). Both yellow and white sweet clover (Melilotus) can be found throughout the southern and central areas of the province. The trees in the parkland region of the province grow in clumps and belts on the hillsides. These are largely deciduous, typically aspen, poplar, and willow. Many species of willow and other shrubs grow in virtually any terrain. North of the North Saskatchewan River, evergreen forests prevail for thousands of square kilometres. Aspen poplar, balsam poplar (Populus balsamifera) or in some parts cottonwood (Populus deltoides), and paper birch (Betula papyrifera) are the primary large deciduous species. Conifers include jack pine (Pinus banksiana), Rocky Mountain pine, lodgepole pine (Pinus" +509,"Blackfoot Confederacy and the Plains Cree, who generally lived by hunting buffalo, and the more northerly tribes such as the Woodland Cree and Chipewyan who hunted, trapped, and fished for a living. The first Europeans to visit Alberta were French Canadians during the late 18th century, working as fur traders. French was the predominant language used in some early fur trading forts in the region, such as the first Fort Edmonton (in present-day Fort Saskatchewan). After the British arrival in Canada, approximately half of the province of Alberta, south of the Athabasca River drainage, became part of Rupert's Land which consisted of all land drained by rivers flowing into Hudson Bay. This area was granted by Charles II of England to the Hudson's Bay Company (HBC) in 1670, and rival fur trading companies were not allowed to trade in it. The Athabasca River and the rivers north of it were not in HBC territory because they drained into the Arctic Ocean instead of Hudson Bay, and they were p" +510,"metro areas and municipalities by population as of 2016 Census metropolitan areas: 2016 2011 2006 2001 1996 Calgary CMA 1,392,609 1,214,839 1,079,310 951,395 821,628 Edmonton CMA 1,321,426 1,159,869 1,034,945 937,845 862,597 Lethbridge CMA 117,394 105,999 95,196 87,388 82,025 Urban municipalities (10 largest): 2016 2011 2006 2001 1996 Calgary 1,239,220 1,096,833 988,193 878,866 768,082 Edmonton 932,546 812,201 730,372 666,104 616,306 Red Deer 100,418 90,564 82,772 67,707 60,080 Lethbridge 92,729 83,517 78,713 68,712 64,938 St. Albert (included in Edmonton CMA) 65,589 61,466 57,719 53,081 46,888 Medicine Hat 63,260 60,005 56,997 51,249 46,783 Grande Prairie 63,166 55,032 47,076 36,983 31,353 Airdrie (included in Calgary CMA) 61,581 42,564 28,927 20,382 15,946 Spruce Grove (included in Edmonton CMA) 34,066 26,171 19,496 15,983 14,271 Leduc (included in Edmonton CMA) 29,993 24,304 16,967 15,032 14,346 Specialized/rural municipalities (5 largest): 2016 2011 2006 2001 1996 Strathcona County" +511,"and to a lesser extent, agriculture and technology. In 2013, Alberta's per capita GDP exceeded that of the United States, Norway, or Switzerland, and was the highest of any province in Canada at This was 56% higher than the national average of and more than twice that of some of the Atlantic provinces. In 2006, the deviation from the national average was the largest for any province in Canadian history. According to the 2006 census, the median annual family income after taxes was $70,986 in Alberta (compared to $60,270 in Canada as a whole). In 2014, Alberta had the second-largest economy in Canada after Ontario, with a GDP exceeding . The GDP of the province calculated at basic prices rose by 4.6% in 2017 to $327.4 billion, which was the largest increase recorded in Canada, and it ended two consecutive years of decreases. Alberta's debt-to-GDP ratio is projected to peak at 12.1% in fiscal year 2021–2022, falling to 11.3% the following year. The Calgary-Edmonton Corridor is the most ur" +512,"three million people per year. Alberta tourism relies heavily on Southern Ontario tourists, as well as tourists from other parts of Canada, the United States, and many other countries. There are also natural attractions like Elk Island National Park, Wood Buffalo National Park, and the Columbia Icefield. Alberta's Rockies include well-known tourist destinations Banff National Park and Jasper National Park. The two mountain parks are connected by the scenic Icefields Parkway. Banff is located west of Calgary on Highway 1, and Jasper is located west of Edmonton on the Yellowhead Highway. Five of Canada's fourteen UNESCO World Heritage Sites are located within the province: Canadian Rocky Mountain Parks, Waterton-Glacier International Peace Park, Wood Buffalo National Park, Dinosaur Provincial Park and Head-Smashed-In Buffalo Jump. A number of these areas hold ski resorts, most notably Banff Sunshine, Lake Louise, Marmot Basin, Norquay and Nakiska. About 1.2 million people visit the Calga" +513,"Weapons Range. CFB Edmonton is the headquarters for the 3rd Canadian Division. CFB Suffield hosts British troops and is the largest training facility in Canada. === Taxation === According to Alberta's 2009 budget, government revenue in that year came mainly from royalties on non-renewable natural resources (30.4%), personal income taxes (22.3%), corporate and other taxes (19.6%), and grants from the federal government primarily for infrastructure projects (9.8%). In 2014, Alberta received $6.1 billion in bitumen royalties. With the drop in the price of oil in 2015 it was down to $1.4 billion. In 2016, Alberta received about $837 million in royalty payments from oil sands Royalty Projects . According to the 2018–2021 fiscal plan, the two top sources of revenue in 2016 were personal income tax at $10,763 million and federal transfers of $7,976 million with total resource revenue at $3,097 million. Alberta is the only province in Canada without a provincial sales tax. Alberta residents ar" +514,"Hockey League Rogers Place 18 347 Calgary Flames Calgary National Hockey League Scotiabank Saddledome 19 289 Edmonton Elks Edmonton Canadian Football League Commonwealth Stadium 60 081 Calgary Stampeders Calgary Canadian Football League McMahon Stadium 40 000 Calgary Wranglers Calgary American Hockey League Scotiabank Saddledome 19 289 Calgary Hitmen Calgary Canadian Hockey League Scotiabank Saddledome 19 289 Edmonton Oil Kings Edmonton Canadian Hockey League Rogers Place 18 347 Lethbridge Hurricanes Lethbridge Canadian Hockey League Enmax Centre 5 479 Medicine Hat Tigers Medicine Hat Canadian Hockey League Canalta Centre 7 100 Red Deer Rebels Red Deer Canadian Hockey League Peavey Mart Centrium 7 111 Cavalry FC Calgary Canadian Premier League ATCO Field 6 000 Calgary Surge Calgary Canadian Elite Basketball League Winsport Event Centre 2 900 Edmonton Stingers Edmonton Canadian Elite Basketball League Edmonton Expo Centre 4 000 Calgary Roughnecks Calgary National Lacrosse League Scotiab" +515,"in Alberta. *Alberta Children's Hospital *Foothills Medical Centre *Grace Women's Health Centre *Libin Cardiovascular Institute of Alberta *Peter Lougheed Centre *Rockyview General Hospital *South Health Campus *Tom Baker Cancer Centre *University of Calgary Medical Centre (UCMC) Health centres in Edmonton include: *Alberta Diabetes Institute *Cross Cancer Institute *Edmonton Clinic *Grey Nuns Community Hospital *Lois Hole Hospital for Women *Mazankowski Alberta Heart Institute *Misericordia Community Hospital *Rexall Centre for Pharmacy and Health Research *Royal Alexandra Hospital *Stollery Children's Hospital *University of Alberta Hospital The Edmonton Clinic complex, completed in 2012, provides a similar research, education, and care environment as the Mayo Clinic in the United States. All public health care services funded by the Government of Alberta are delivered operationally by Alberta Health Services. AHS is the province's single health authority, established on July 1, 2008" +516,"its largest urban expressways trails and naming many of them after prominent First Nations individuals and tribes, such as Crowchild Trail, Deerfoot Trail, and Stoney Trail. == Friendship partners == Alberta has relationships with many provinces, states, and other entities worldwide. * Gangwon-do, South Korea (1974) * Hokkaido, Japan (1980) * Heilongjiang, China (1981) * Montana, United States (1985) * Tyumen, Russia (1992) * Khanty–Mansi, Russia (1995) * Yamalo-Nenets, Russia (1997) * Jalisco, Mexico (1999) * Alaska, United States (2002) * Saxony, Germany (2002) * Ivano-Frankivsk, Ukraine (2004) * Lviv, Ukraine (2005) * California, United States (1997) * Guangdong, China (2017) ==See also== * Index of Alberta-related articles * Outline of Alberta * Royal eponyms in Canada * Edmonton * Calgary * Banff National Park ==Notes== == References == == Further reading == * * * * * * == External links == * * Alberta Encyclopedia * List of streets in Alberta with maps Category:1905 establishment" +517,"Anthropologist ==A== *John Adair *B. R. Ambedkar *Giulio Angioni *Jon Altman *Arjun Appadurai *Talal Asad *Timothy Asch *Scott Atran *Marc Augé ==B== *Nigel Barley *Fredrik Barth *Vasily Bartold *Keith H. Basso *Daisy Bates *Gregory Bateson *Mary Catherine Bateson *Richard Bauman *Ruth Behar *Ruth Benedict *Dorothy A. Bennett *Carl H. Berendt *Lee Berger *Brent Berlin *Catherine Helen Webb Berndt *Catherine L. Besteman *Theodore C. Bestor *Lewis Binford *Evelyn Blackwood *Wilhelm Bleek *Maurice Bloch *Anton Blok *Franz Boas *Tom Boellstorff *Paul Bohannan *Dmitri Bondarenko *Pere Bosch-Gimpera *Pierre Bourdieu *Philippe Bourgois *Charles L. Briggs *Paul Broca *Christian Bromberger *Kari Bruwelheide *Peter Buck (Te Rangi Hīroa) ==C== *Julio Caro Baroja *Edmund Carpenter *Napoleon Chagnon *Pierre Clastres *Mabel Cook Cole *Malcolm Carr Collier *Harold C. Conklin *Carleton S. Coon *Frank Hamilton Cushing ==D== *Regna Darnell *Raymond Dart *Emma Lou Davis *Wade Davis *Ernesto de Martino *E" +518,"Actinopterygii (; ), members of which are known as ray-finned fish or actinopterygians, is a class of bony fish that comprise over 50% of living vertebrate species. They are so called because of their lightly built fins made of webbings of skin supported by radially extended thin bony spines called lepidotrichia, as opposed to the bulkier, fleshy lobed fins of the sister class Sarcopterygii (lobe-finned fish). Resembling folding fans, the actinopterygian fins can easily change shape and wetted area, providing superior thrust-to-weight ratios per movement compared to sarcopterygian and chondrichthyian fins. The fin rays attach directly to the proximal or basal skeletal elements, the radials, which represent the articulation between these fins and the internal skeleton (e.g., pelvic and pectoral girdles). The vast majority of actinopterygians are teleosts. By species count, they dominate the subphylum Vertebrata, and constitute nearly 99% of the over 30,000 extant species of fish.(Davis," +519,"lineage of all other actinopterygians, the Acipenseriformes (sturgeons and paddlefishes) are the sister lineage of Neopterygii, and Holostei (bowfin and gars) are the sister lineage of teleosts. The Elopomorpha (eels and tarpons) appear to be the most basal teleosts. The earliest known fossil actinopterygian is Andreolepis hedei, dating back 420 million years (Late Silurian), remains of which have been found in Russia, Sweden, and Estonia. Crown group actinopterygians most likely originated near the Devonian-Carboniferous boundary. The earliest fossil relatives of modern teleosts are from the Triassic period (Prohalecites, Pholidophorus), although it is suspected that teleosts originated already during the Paleozoic Era. Chondrostei Chondrostei (cartilage bone) is a subclass of primarily cartilaginous fish showing some ossification. Earlier definitions of Chondrostei are now known to be paraphyletic, meaning that this subclass does not contain all the descendants of their common ancest" +520,"(Rosen & Greenwood 1970) Sagemehl 1885 *********** Order †Sorbininardiformes Taverne 1999 *********** Order Gonorynchiformes Regan 1909 (milkfishes) ********** Section Otophysa Garstang 1931 *********** Order Cypriniformes Bleeker 1859 sensu Goodrich 1909 (barbs, carp, danios, goldfishes, loaches, minnows, rasboras) *********** Order Characiformes Goodrich 1909 (characins, pencilfishes, hatchetfishes, piranhas, tetras, dourado / golden (genus Salminus) and pacu) *********** Order Gymnotiformes Berg 1940 (electric eels and knifefishes) *********** Order Siluriformes Cuvier 1817 sensu Hay 1929 (catfishes) ******** Cohort Euteleosteomorpha (Greenwood et al. 1966) (Euteleostei Greenwood 1967 sensu Johnson & Patterson 1996) *********Subcohort Lepidogalaxii ********** Order Lepidogalaxiiformes Betancur-Rodriguez et al. 2013 (salamanderfish) ********* Subcohort Protacanthopterygii Greenwood et al. 1966 sensu Johnson & Patterson 1996 ********** Order Argentiniformes (barreleyes and slickheads)" +521,"Albert Einstein ( ; ; 14 March 1879 – 18 April 1955) was a German-born theoretical physicist who is widely held as one of the most influential scientists. Best known for developing the theory of relativity, Einstein also made important contributions to quantum mechanics. His mass–energy equivalence formula , which arises from special relativity, has been called the world's most famous equation . He received the 1921 Nobel Prize in Physics for his services to theoretical physics, and especially for his discovery of the law of the photoelectric effect , a pivotal step in the development of quantum theory. Born in the German Empire, Einstein moved to Switzerland in 1895, forsaking his German citizenship (as a subject of the Kingdom of Württemberg) the following year. In 1897, at the age of seventeen, he enrolled in the mathematics and physics teaching diploma program at the Swiss federal polytechnic school in Zürich, graduating in 1900. In 1901, he acquired Swiss citizenship, which he kep" +522,"of the Pythagorean theorem before his thirteenth birthday. A family tutor, Max Talmud, said that only a short time after he had given the twelve year old Einstein a geometry textbook, the boy had worked through the whole book. He thereupon devoted himself to higher mathematics ... Soon the flight of his mathematical genius was so high I could not follow. Einstein recorded that he had mastered integral and differential calculus while still just fourteen. His love of algebra and geometry was so great that at twelve, he was already confident that nature could be understood as a mathematical structure . At thirteen, when his range of enthusiasms had broadened to include music and philosophy, Talmud introduced Einstein to Kant's Critique of Pure Reason. Kant became his favorite philosopher; according to Talmud, At the time he was still a child, only thirteen years old, yet Kant's works, incomprehensible to ordinary mortals, seemed to be clear to him. In 1895, at the age of sixteen, Einstein" +523,"1900, duly certified as competent to teach mathematics and physics. His successful acquisition of Swiss citizenship in February 1901 was not followed by the usual sequel of conscription; the Swiss authorities deemed him medically unfit for military service. He found that Swiss schools too appeared to have no use for him, failing to offer him a teaching position despite the almost two years that he spent applying for one. Eventually it was with the help of Marcel Grossmann's father that he secured a post in Bern at the Swiss Patent Office, as an assistant examiner – level III. Patent applications that landed on Einstein's desk for his evaluation included ideas for a gravel sorter and an electric typewriter. His employers were pleased enough with his work to make his position permanent in 1903, although they did not think that he should be promoted until he had fully mastered machine technology . It is conceivable that his labors at the patent office had a bearing on his development of h" +524,"the entire scientific community when S. N. Bose derived the Planck spectrum in 1924. That same year, Einstein was elected an International Honorary Member of the American Academy of Arts and Sciences. Britain's closest equivalent of the Nobel award, the Royal Society's Copley Medal, was not hung around Einstein's neck until 1925. He was elected an International Member of the American Philosophical Society in 1930. Einstein resigned from the Prussian Academy in March 1933. His accomplishments in Berlin had included the completion of the general theory of relativity, proving the Einstein–de Haas effect, contributing to the quantum theory of radiation, and the development of Bose–Einstein statistics. === 1919: Putting general relativity to the test === In 1907, Einstein reached a milestone on his long journey from his special theory of relativity to a new idea of gravitation with the formulation of his equivalence principle, which asserts that an observer in an infinitesimally small box f" +525,"a chance to meet a fellow Nobel laureate, the neuroanatomist Santiago Ramón y Cajal.) === 1922–1932: Serving the League of Nations === From 1922 until 1932, with the exception of a few months in 1923 and 1924, Einstein was a member of the Geneva-based International Committee on Intellectual Cooperation of the League of Nations, a group set up by the League to encourage scientists, artists, scholars, teachers and other people engaged in the life of the mind to work more closely with their counterparts in other countries. He was appointed as a German delegate rather than as a representative of Switzerland because of the machinations of two Catholic activists, Oskar Halecki and Giuseppe Motta. By persuading Secretary General Eric Drummond to deny Einstein the place on the committee reserved for a Swiss thinker, they created an opening for Gonzague de Reynold, who used his League of Nations position as a platform from which to promote traditional Catholic doctrine. Einstein's former physic" +526,"magazine included him in a list of enemies of the German regime with the phrase, not yet hanged , offering a $5,000 bounty on his head. In a subsequent letter to physicist and friend Max Born, who had already emigrated from Germany to England, Einstein wrote, ... I must confess that the degree of their brutality and cowardice came as something of a surprise. After moving to the US, he described the book burnings as a spontaneous emotional outburst by those who shun popular enlightenment , and more than anything else in the world, fear the influence of men of intellectual independence . Einstein was now without a permanent home, unsure where he would live and work, and equally worried about the fate of countless other scientists still in Germany. Aided by the Academic Assistance Council, founded in April 1933 by British Liberal politician William Beveridge to help academics escape Nazi persecution, Einstein was able to leave Germany. He rented a house in De Haan, Belgium, where he lived" +527,"develop the bomb, drawing on its immense material, financial, and scientific resources to initiate the Manhattan Project. For Einstein, war was a disease ... [and] he called for resistance to war. By signing the letter to Roosevelt, some argue he went against his pacifist principles. In 1954, a year before his death, Einstein said to his old friend, Linus Pauling, I made one great mistake in my life—when I signed the letter to President Roosevelt recommending that atom bombs be made; but there was some justification—the danger that the Germans would make them ... In 1955, Einstein and ten other intellectuals and scientists, including British philosopher Bertrand Russell, signed a manifesto highlighting the danger of nuclear weapons. In 1960 Einstein was included posthumously as a charter member of the World Academy of Art and Science (WAAS), an organization founded by distinguished scientists and intellectuals who committed themselves to the responsible and ethical advances of science," +528,". Einstein wrote that he was deeply moved , but at once saddened and ashamed that he could not accept it. ==== Religious and philosophical views ==== Per Lee Smolin, I believe what allowed Einstein to achieve so much was primarily a moral quality. He simply cared far more than most of his colleagues that the laws of physics have to explain everything in nature coherently and consistently. Einstein expounded his spiritual outlook in a wide array of writings and interviews. He said he had sympathy for the impersonal pantheistic God of Baruch Spinoza's philosophy. He did not believe in a personal god who concerns himself with fates and actions of human beings, a view which he described as naïve. He clarified, however, that I am not an atheist , preferring to call himself an agnostic, or a deeply religious nonbeliever . He wrote that A spirit is manifest in the laws of the universe—a spirit vastly superior to that of man, and one in the face of which we with our modest powers must feel hum" +529,"of 76, having continued to work until near the end. During the autopsy, the pathologist Thomas Stoltz Harvey removed Einstein's brain for preservation without the permission of his family, in the hope that the neuroscience of the future would be able to discover what made Einstein so intelligent. Einstein's remains were cremated in Trenton, New Jersey, and his ashes were scattered at an undisclosed location. In a memorial lecture delivered on 13 December 1965 at UNESCO headquarters, nuclear physicist J. Robert Oppenheimer summarized his impression of Einstein as a person: He was almost wholly without sophistication and wholly without worldliness ... There was always with him a wonderful purity at once childlike and profoundly stubborn. Einstein bequeathed his personal archives, library, and intellectual assets to the Hebrew University of Jerusalem in Israel. == Scientific career == Throughout his life, Einstein published hundreds of books and articles. He published more than 300 scient" +530,"general relativity was that the preference of inertial motions within special relativity was unsatisfactory, while a theory which from the outset prefers no state of motion (even accelerated ones) should appear more satisfactory. Consequently, in 1907 he published an article on acceleration under special relativity. In that article titled On the Relativity Principle and the Conclusions Drawn from It , he argued that free fall is really inertial motion, and that for a free-falling observer the rules of special relativity must apply. This argument is called the equivalence principle. In the same article, Einstein also predicted the phenomena of gravitational time dilation, gravitational redshift and gravitational lensing. In 1911, Einstein published another article On the Influence of Gravitation on the Propagation of Light expanding on the 1907 article, in which he estimated the amount of deflection of light by massive bodies. Thus, the theoretical prediction of general relativity could" +531,"the program outlined in the paper Do Gravitational Fields play an Important Role in the Constitution of the Elementary Particles? . These solutions cut and pasted Schwarzschild black holes to make a bridge between two patches. Because these solutions included spacetime curvature without the presence of a physical body, Einstein and Rosen suggested that they could provide the beginnings of a theory that avoided the notion of point particles. However, it was later found that Einstein–Rosen bridges are not stable. ==== Einstein–Cartan theory ==== In order to incorporate spinning point particles into general relativity, the affine connection needed to be generalized to include an antisymmetric part, called the torsion. This modification was made by Einstein and Cartan in the 1920s. ==== Equations of motion ==== In general relativity, gravitational force is reimagined as curvature of spacetime. A curved path like an orbit is not the result of a force deflecting a body from an ideal straight" +532,"Einstein published an article in Physikalische Zeitschrift that proposed the possibility of stimulated emission, the physical process that makes possible the maser and the laser. This article showed that the statistics of absorption and emission of light would only be consistent with Planck's distribution law if the emission of light into a mode with n photons would be enhanced statistically compared to the emission of light into an empty mode. This paper was enormously influential in the later development of quantum mechanics, because it was the first paper to show that the statistics of atomic transitions had simple laws. ==== Matter waves ==== Einstein discovered Louis de Broglie's work and supported his ideas, which were received skeptically at first. In another major paper from this era, Einstein observed that de Broglie waves could explain the quantization rules of Bohr and Sommerfeld. This paper would inspire Schrödinger's work of 1926. === Quantum mechanics === ==== Einstein's" +533,"entity. In 1950, he described his unified field theory in a Scientific American article titled On the Generalized Theory of Gravitation . His attempt to find the most fundamental laws of nature won him praise but not success: a particularly conspicuous blemish of his model was that it did not accommodate the strong and weak nuclear forces, neither of which was well understood until many years after his death. Although most researchers now believe that Einstein's approach to unifying physics was mistaken, his goal of a theory of everything is one to which his successors still aspire. === Other investigations === Einstein conducted other investigations that were unsuccessful and abandoned. These pertain to force, superconductivity, and other research. === Collaboration with other scientists === In addition to longtime collaborators Leopold Infeld, Nathan Rosen, Peter Bergmann and others, Einstein also had some one-shot collaborations with various scientists. ==== Einstein–de Haas experim" +534,"pp. v–vi. . Foreword originally written in April 1953. * The chasing a light beam thought experiment is described on pages 48–51. == See also == * Bern Historical Museum (Einstein Museum) * Einstein notation * Frist Campus Center at Princeton University room 302 is associated with Einstein. The center was once the Palmer Physical Laboratory. * Heinrich Burkhardt * Heinrich Zangger * History of gravitational theory * List of coupled cousins * List of German inventors and discoverers * List of Jewish Nobel laureates * List of peace activists * Relativity priority dispute * Sticky bead argument == Notes == == References == === Works cited === * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * == Further reading == * * * * * * * * , or * * * * * == External links == * * * * Einstein's Personal Correspondence: Religion, Politics, The Holocaust, and Philosophy Shapell Manuscript Foundation * Federal Bureau of Investigation file on Albert Einstein * Einstein and his love of music" +535,"Afghanistan, officially the Islamic Emirate of Afghanistan, is a landlocked country located at the crossroads of Central Asia and South Asia. It is bordered by Pakistan to the east and south, Iran to the west, Turkmenistan to the northwest, Uzbekistan to the north, Tajikistan to the northeast, and China to the northeast and east. Occupying of land, the country is predominantly mountainous with plains in the north and the southwest, which are separated by the Hindu Kush mountain range. Kabul is the country's capital and largest city. According to the World Population review, , Afghanistan's population is 43 million. The National Statistics Information Authority of Afghanistan estimated the population to be 32.9 million . Human habitation in Afghanistan dates to the Middle Paleolithic era. Popularly referred to as the graveyard of empires, the land has witnessed numerous military campaigns, including those by the Persians, Alexander the Great, the Maurya Empire, Arab Muslims, the Mongols" +536,"city of Mundigak (near Kandahar in the south of the country) was a center of the Helmand culture. More recent findings established that the Indus Valley Civilization stretched up towards modern-day Afghanistan. An Indus Valley site has been found on the Oxus River at Shortugai in northern Afghanistan.Kenoyer, Jonathan Mark (1998). Ancient cities of the Indus Valley Civilisation. pp.96 After 2000 BCE successive waves of semi-nomadic people from Central Asia began moving south into Afghanistan; among them were many Indo-European-speaking Indo-Iranians. These tribes later migrated further into South Asia, Western Asia, and toward Europe via the area north of the Caspian Sea. The region at the time was referred to as Ariana.Bryant, Edwin F. (2001) The quest for the origins of Vedic culture: the Indo-Aryan migration debate Oxford University Press, . By the middle of the 6th century BCE, the Achaemenids overthrew the Medes and incorporated Arachosia, Aria, and Bactria within its eastern boun" +537,"Kandahar under the Safavids, and established his own kingdom. Mirwais died in 1715, and was succeeded by his brother Abdul Aziz, who was soon killed by Mirwais's son Mahmud for possibly planning to sign a peace with the Safavids. Mahmud led the Afghan army in 1722 to the Persian capital of Isfahan, and captured the city after the Battle of Gulnabad and proclaimed himself King of Persia. The Afghan dynasty was ousted from Persia by Nader Shah after the 1729 Battle of Damghan. In 1738, Nader Shah and his forces captured Kandahar in the siege of Kandahar, the last Hotak stronghold, from Shah Hussain Hotak. Soon after, the Persian and Afghan forces invaded India, Nader Shah had plundered Delhi, alongside his 16-year-old commander, Ahmad Shah Durrani who had assisted him on these campaigns. Nader Shah was assassinated in 1747. ===Durrani Empire=== After the death of Nader Shah in 1747, Ahmad Shah Durrani had returned to Kandahar with a contingent of 4,000 Pashtuns. The Abdalis had unanimous" +538,"rule in Herat, Punjab and Kashmir were lost to Ranjit Singh, ruler of the Sikh Empire, who invaded Khyber Pakhtunkhwa in March 1823 and captured the city of Peshawar following the Battle of Nowshera. In 1834, Dost Mohammad Khan led numerous campaigns, firstly campaigning to Jalalabad, and then allying with his rival brothers in Kandahar to defeat Shah Shuja Durrani and the British in the Expedition of Shuja ul-Mulk. In 1837, Dost Mohammad Khan attempted to conquer Peshawar and sent a large force under his son Wazir Akbar Khan, leading to the Battle of Jamrud. Akbar Khan and the Afghan army failed to capture the Jamrud Fort from the Sikh Khalsa Army, but killed Sikh Commander Hari Singh Nalwa, thus ending the Afghan-Sikh Wars. By this time the British were advancing from the east, capitalizing off of the decline of the Sikh Empire after it had its own period of turbulence following the death of Ranjit Singh, which engaged the Emirate of Kabul in the first major conflict during The Great" +539,"cousin, defeated and killed Kalakani in October 1929, and was declared King Nadir Shah. He abandoned the reforms of King Amanullah in favor of a more gradual approach to modernization, but was assassinated in 1933 by Abdul Khaliq. Mohammed Zahir Shah succeeded to the throne and reigned as king from 1933 to 1973. During the tribal revolts of 1944–1947, King Zahir's reign was challenged by Zadran, Safi, Mangal, and Wazir tribesmen led by Mazrak Zadran, Salemai, and Mirzali Khan, among others – many of whom were Amanullah loyalists. Afghanistan joined the League of Nations in 1934. The 1930s saw the development of roads, infrastructure, the founding of a national bank, and increased education. Road links in the north played a large part in a growing cotton and textile industry. The country built close relationships with the Axis powers, with Nazi Germany having the largest share in Afghan development at the time. Until 1946 King Zahir ruled with the assistance of his uncle, who held the p" +540,"enforcement of their interpretation of Islamic sharia law, which resulted in the brutal treatment of many Afghans, especially women.* * During their rule, the Taliban and their allies committed massacres against Afghan civilians, denied UN food supplies to starving civilians and conducted a policy of scorched earth, burning vast areas of fertile land and destroying tens of thousands of homes. After the fall of Kabul to the Taliban, Ahmad Shah Massoud and Abdul Rashid Dostum formed the Northern Alliance, later joined by others, to resist the Taliban. Dostum's forces were defeated by the Taliban during the Battles of Mazar-i-Sharif in 1997 and 1998; Pakistan's Chief of Army Staff, Pervez Musharraf, began sending thousands of Pakistanis to help the Taliban defeat the Northern Alliance. By 2000, the Northern Alliance only controlled 10% of territory, cornered in the northeast. On 9 September 2001, Massoud was assassinated by two Arab suicide attackers in Panjshir Valley. Around 400,000 Afg" +541,"Guterres received a letter from acting minister of foreign affairs Amir Khan Muttaqi to formally claim Afghanistan's seat as a member state for their official spokesman in Doha, Suhail Shaheen. The United Nations did not recognize the previous Taliban government and chose to work with the then government-in-exile instead. Western nations suspended most of their humanitarian aid to Afghanistan following the Taliban's August 2021 takeover of the country; the World Bank and International Monetary Fund also halted their payments. More than half of Afghanistan's 39 million people faced an acute food shortage in October 2021. Human Rights Watch reported on 11 November 2021 that Afghanistan was facing widespread famine due to an economic and banking crisis. The Taliban have significantly tackled corruption, now being placed as 150th on the corruption watchdog perception index. The Taliban have also reportedly reduced bribery and extortion in public service areas. At the same time, the human r" +542,"arid in the summers, with most rainfall falling between December and April. The lower areas of northern and western Afghanistan are the driest, with precipitation more common in the east. Although proximate to India, Afghanistan is mostly outside the monsoon zone, except the Nuristan Province which occasionally receives summer monsoon rain. ===Biodiversity=== Several types of mammals exist throughout Afghanistan. Snow leopards, Siberian tigers and brown bears live in the high elevation alpine tundra regions. The Marco Polo sheep exclusively live in the Wakhan Corridor region of north-east Afghanistan. Foxes, wolves, otters, deer, wild sheep, lynx and other big cats populate the mountain forest region of the east. In the semi-desert northern plains, wildlife include a variety of birds, hedgehogs, gophers, and large carnivores such as jackals and hyenas. Gazelles, wild pigs and jackals populate the steppe plains of the south and west, while mongoose and cheetahs exist in the semi-desert" +543,"nearly 400 provincial districts, each of which normally covers a city or several villages. Each district is represented by a district governor. The provincial governors are now appointed by the Prime Minister of Afghanistan, and the district governors are selected by the provincial governors. The provincial governors are representatives of the central government in Kabul and are responsible for all administrative and formal issues within their provinces. There are also provincial councils that are elected through direct and general elections for four years. The functions of provincial councils are to take part in provincial development planning and to participate in the monitoring and appraisal of other provincial governance institutions. According to article 140 of the constitution and the presidential decree on electoral law, mayors of cities should be elected through free and direct elections for a four-year term. In practice however, mayors are appointed by the government. The 34 p" +544,"in more than $1.5 billion in telecom investment and created more than 100,000 jobs since 2003. Afghan rugs are becoming popular again, allowing many carpet dealers around the country to hire more workers; in 2016–17 it was the fourth most exported group of items. Afghanistan is a member of WTO, SAARC, ECO, and OIC. It holds an observer status in SCO. In 2018, a majority of imports come from either Iran, China, Pakistan and Kazakhstan, while 84% of exports are to Pakistan and India. Since the Taliban's takeover of the country in August 2021, the United States has frozen about $9 billion in assets belonging to the Afghan central bank, blocking the Taliban from accessing billions of dollars held in US bank accounts. The GDP of Afghanistan is estimated to have dropped by 20% following the Taliban return to power. Following this, after months of free-fall, the Afghan economy began stabilizing, as a result of the Taliban's restrictions on smuggled imports, limits on banking transactions, and" +545,"majority of electricity production is via hydropower, helped by the amount of rivers and streams that flow from the mountains. However electricity is not always reliable and blackouts happen, including in Kabul. In recent years an increasing number of solar, biomass and wind power plants have been constructed. Currently under development are the CASA-1000 project which will transmit electricity from Kyrgyzstan and Tajikistan, and the Turkmenistan-Afghanistan-Pakistan-India (TAPI) gas pipeline. Power is managed by the Da Afghanistan Breshna Sherkat (DABS, Afghanistan Electricity Company). Important dams include the Kajaki Dam, Dahla Dam, and the Sardeh Band Dam. ===Tourism=== Tourism is a small industry in Afghanistan due to security issues. Nevertheless, some 20,000 foreign tourists visit the country annually as of 2016. In particular an important region for domestic and international tourism is the picturesque Bamyan Valley, which includes lakes, canyons and historical sites, helped b" +546,"million or so Afghans are temporarily housed in neighboring Pakistan and Iran, most of whom were born and raised in those two countries. As of 2013, Afghanistan was the largest refugee-producing country in the world, a title held for 32 years. The current population growth rate is 2.37%, one of the highest in the world outside of Africa. This population is expected to reach 82 million by 2050 if current population trends continue. The population of Afghanistan increased steadily until the 1980s, when civil war caused millions to flee to other countries such as Pakistan. Millions have since returned and the war conditions contribute to the country having the highest fertility rate outside Africa. Afghanistan's healthcare has recovered since the turn of the century, causing falls in infant mortality and increases in life expectancy, although it has the lowest life expectance of any country outside Africa. This (along with other factors such as returning refugees) caused rapid population" +547,"returning to secondary schools. Over 174,000 students are enrolled in different universities around the country. About 21% of these are females. Former Education Minister Ghulam Farooq Wardak had stated that construction of 8,000 schools is required for the remaining children who are deprived of formal learning. As of 2018 the literacy rate of the population age 15 and older is 43.02% (males 55.48% and females 29.81%). The top universities in Afghanistan are the American University of Afghanistan (AUAF) followed by Kabul University (KU), both of which are located in Kabul. The National Military Academy of Afghanistan, modeled after the United States Military Academy at West Point, was a four-year military development institution dedicated to graduating officers for the Afghan Armed Forces. The Afghan Defense University was constructed near Qargha in Kabul. Major universities outside of Kabul include Kandahar University in the south, Herat University in the northwest, Balkh University a" +548,"most well known are the Great Mosque of Herat, the Blue Mosque, the Minaret of Jam, the Chil Zena, the Qala-i Bost in Lashkargah, the ancient Greek city of Ai-Khanoum. However, many of its historic monuments have been damaged in modern times due to the civil wars.G.V. Brandolini. Afghanistan cultural heritage. Orizzonte terra, Bergamo. 2007. p. 64. The two famous Buddhas of Bamiyan were destroyed by the Taliban, who regarded them as idolatrous. As there was no colonialism in the modern era in Afghanistan, European-style architecture is rare but does exist: the Victory Arch at Paghman and the Darul Aman Palace in Kabul were built in this style in the 1920s. Afghan architecture also ranges deep into India such as the city of Agra, and the tomb of Sher Shah Suri, an Afghan Emperor of India. ===Art and ceramics=== Carpet weaving is an ancient practice in Afghanistan, and many of these are still handmade by tribal and nomadic people today. Carpets have been produced in the region for thousa" +549,"Khan, Saif Ali Khan, Aamir Khan, Feroz Khan, Kader Khan, Naseeruddin Shah, Zarine Khan, Celina Jaitly, and a number of others. Several Bollywood films have been shot inside Afghanistan, including Dharmatma, Khuda Gawah, Escape from Taliban, and Kabul Express. ===Cuisine=== Afghan cuisine is largely based upon the nation's chief crops, such as wheat, maize, barley and rice. Accompanying these staples are native fruits and vegetables as well as dairy products such as milk, yogurt, and whey. Kabuli palaw is the national dish of Afghanistan. The nation's culinary specialties reflect its ethnic and geographic diversity. Afghanistan is known for its high-quality pomegranates, grapes, and sweet melons. Tea is a favorite drink among Afghans. A typical Afghan diet consists of naan, yogurt, rice, and meat. ===Holidays and festivals=== Afghanistan's official New Year starts with Nowruz, an ancient tradition that started as a Zoroastrian celebration in present-day Iran, and with which it shares th" +550,"Albania ( ; or ), officially the Republic of Albania (), is a country in Southeast Europe. It is in the Balkans, on the Adriatic and Ionian Seas within the Mediterranean Sea, and shares land borders with Montenegro to the northwest, Kosovo to the northeast, North Macedonia to the east and Greece to the south. With an area of , it has a varied range of climatic, geological, hydrological and morphological conditions. Albania's landscapes range from rugged snow-capped mountains in the Albanian Alps and the Korab, Skanderbeg, Pindus and Ceraunian Mountains, to fertile lowland plains extending from the Adriatic and Ionian seacoasts. Tirana is the capital and largest city in the country, followed by Durrës, Vlorë, and Shkodër. In ancient times, the Illyrians inhabited northern and central regions of Albania, whilst Epirotes inhabited the south. Several important ancient Greek colonies were also established on the coast. The Illyrian kingdom centered in what is now Albania was the dominant po" +551,"from the early 5th millennium BC. First evidence of its overseas trade export comes from Neolithic and Bronze Age southern Italy. The high-quality bitumen of Selenicë has been exploited throughout all the historical ages since the Late Neolithic era until today. The Indo-Europeanization of Albania in the context of the IE-ization of the western Balkans began after 2800 BC. The presence of the Early Bronze Age tumuli in the vicinity of later Apollonia dates to 2679±174 calBC (2852-2505 calBC). These burial mounds belong to the southern expression of the Adriatic-Ljubljana culture (related to later Cetina culture) which moved southwards along the Adriatic from the northern Balkans. The same community built similar mounds in Montenegro (Rakića Kuće) and northern Albania (Shtoj). The first archaeogenetic find related to the IE-ization of Albania involves a man with predominantly Yamnaya ancestry buried in a tumulus of northeastern Albania which dates to 2663–2472 calBC. During the Middle B" +552,"Epirus. The kingdom claimed all of central Albania territory from Dyrrhachium along the Adriatic Sea coast down to Butrint. A catholic political structure was a basis for the papal plans of spreading Catholicism in the Balkan Peninsula. This plan found also the support of Helen of Anjou, a cousin of Charles of Anjou. Around 30 Catholic churches and monasteries were built during her rule mainly in northern Albania. Internal power struggles within the Byzantine Empire in the 14th century enabled Serbs' most powerful medieval ruler, Stefan Dusan, to establish a short-lived empire that included all of Albania except Durrës. In 1367, various Albanian rulers established the Despotate of Arta. During that time, several Albanian principalities were created, notably the Principality of Albania, Principality of Kastrioti, Lordship of Berat and Principality of Dukagjini. In the first half of the 15th century, the Ottoman Empire invaded most of Albania, and the League of Lezhë was held under Skand" +553,"Prizren. The league had initially the assistance of the Ottoman authorities whose position was based on the religious solidarity of Muslim people and landlords connected with the Ottoman administration. They favoured and protected the Muslim solidarity and called for defence of Muslim lands simultaneously constituting the reason for titling the league Committee of the Real Muslims. Approximately 300 Muslims participated in the assembly composed by delegates from Bosnia, the administrator of the Sanjak of Prizren as representatives of the central authorities and no delegates from Vilayet of Scutari. Signed by only 47 Muslim deputies, the league issued the Kararname that contained a proclamation that the people from northern Albania, Epirus and Bosnia and Herzegovina are willing to defend the territorial integrity of the Ottoman Empire by all possible means against the troops of Bulgaria, Serbia and Montenegro. Ottomans authorities cancelled their assistance when the league, under Abdyl" +554,"modern Albanian history. Hoxha's regime embraced Marxist–Leninist ideologies and implemented authoritarian policies, including prohibition of religious practices, severe restrictions on travel, and abolition of private property rights. It was also defined by a persistent pattern of purges, extensive repression, instances of betrayal, and hostility to external influences. Any form of opposition or resistance to his rule was met with expeditious and severe consequences, such as internal exile, extended imprisonment, and execution. The regime confronted a multitude of challenges, including widespread poverty, illiteracy, health crises and gender inequality. In response, Hoxha initiated a modernization initiative aimed at attaining economic and social liberation and transforming Albania into an industrial society. The regime placed a high priority on the diversification of the economy through Soviet-style industrialization, comprehensive infrastructure development such as the introduction" +555,"administered in Albania was 1,280,239 doses. On 21 September 2024, it was reported that Prime Minister Rama was planning to create the Sovereign State of the Bektashi Order, a sovereign microstate for the Order within Tirana. == Environment == === Geography === Albania lies along the Mediterranean Sea on the Balkan Peninsula in South and Southeast Europe, and has an area of . It is bordered by the Adriatic Sea to the west, Montenegro to the northwest, Kosovo to the northeast, North Macedonia to the east, Greece to the south, and the Ionian Sea to the southwest. It is between latitudes 42° and 39° N and longitudes 21° and 19° E. Geographic coordinates include Vërmosh at 42° 35' 34 northern latitude as the northernmost point, Konispol at 39° 40' 0 northern latitude as the southernmost, Sazan at 19° 16' 50 eastern longitude as the westernmost, and Vërnik at 21° 1' 26 eastern longitude as the easternmost. Mount Korab, rising at above the Adriatic, is the highest point, while the Mediterran" +556,"management of biological diversity. Since 1994, the country is a party to the Convention on Biological Diversity (CBD) and its associated Cartagena and Nagoya Protocols. To uphold these commitments, it has developed and implemented a comprehensive National Biodiversity Strategy and Action Plan (NBSAP). Furthermore, Albania has established a partnership with the International Union for Conservation of Nature (IUCN), advancing its conservation efforts on both national and international scales. Guided by the IUCN, the country has made substantial progress in the foundation of protected areas within its boundaries, encompassing 12 national parks among others Butrint, Karaburun-Sazan, Llogara, Prespa and Vjosa. As a signatory to the Ramsar Convention, Albania has granted special recognition upon four wetlands, designating them as Wetlands of International Importance, including Buna-Shkodër, Butrint, Karavasta and Prespa. The country's dedication to protection extends further into the sphere" +557,"isolation during the communist era. The country reached a major achievement in its foreign policy by securing membership in the North Atlantic Treaty Organization (NATO) in 2009. Since obtaining candidate status in 2014, the country has also embarked on a comprehensive reform agenda to align itself with European Union (EU) accession standards, with the objective of becoming an EU member state. Albania and Kosovo maintain a fraternal relationship strengthened by their substantial cultural, ethnical and historical ties. Both countries foster enduring diplomatic ties, with Albania actively supporting Kosovo's development and international integration efforts. Its fundamental contribution to Kosovo's path to independence is underscored by its early recognition of Kosovo's sovereignty in 2008. Furthermore, both governments hold annual joint meetings, displayed by the inaugural meeting in 2014, which serves as an official platform to enhance bilateral cooperation and reinforce their joint co" +558,"years as the government has embarked on an ambitious programme to improve the business climate through fiscal and legislative reforms. === Primary sector === Agriculture in the country is based on small to medium-sized family-owned dispersed units. It remains a significant sector of the economy of Albania. It employs 41% of the population, and about 24.31% of the land is used for agricultural purposes. One of the earliest farming sites in Europe has been found in the southeast of the country. As part of the pre-accession process of Albania to the European Union, farmers are being aided through IPA funds to improve Albanian agriculture standards. Albania produces significant amounts of fruits (apples, olives, grapes, oranges, lemons, apricots, peaches, cherries, figs, sour cherries, plums, and strawberries), vegetables (potatoes, tomatoes, maize, onions, and wheat), sugar beets, tobacco, meat, honey, dairy products, traditional medicine and aromatic plants. Further, the country is a wor" +559,"of the Adriatic–Ionian Corridor as well as the Pan-European Corridor VIII and connects Fier with Vlorë. The Autostrada 3 (A3) is under construction and after its completion will connect Tirana and Elbasan with the Pan-European Corridor VIII. When all three corridors are completed, Albania will have an estimated of highway, linking it with all neighboring countries. Durrës is the busiest and largest seaport in the country, followed by Vlorë, Shëngjin and Sarandë. , it is as one of the largest passenger ports on the Adriatic Sea, with annual passenger volume of about 1.5 million. The principal ports serve a system of ferries connecting Albania with islands and coastal cities in Croatia, Greece, and Italy. The rail network is administered by the national railway company Hekurudha Shqiptare, which was extensively promoted by Hoxha. There has been considerable increase in private car ownership and bus usage while rail use decreased since the end of communism. A new railway line from Tirana" +560,"to showing motion pictures was built in 1912 in Shkodër. During the Peoples Republic of Albania, Albanian cinema developed rapidly with the inauguration of the Kinostudio Shqipëria e Re in Tirana. In 1953, the Albanian-Soviet epic film, the Great Warrior Skanderbeg, was released chronicling the life and fight of the medieval Albanian hero Skanderbeg. It went on to win the international prize at the 1954 Cannes Film Festival. In 2003, the Tirana International Film Festival was established, the largest film festival in the country. The Durrës Amphitheatre is host to the Durrës International Film Festival, the second largest film festival. === Technology === After the fall of communism in 1991, human resources in sciences and technology in Albania have drastically decreased. As of various reports, during 1991 to 2005 approximately 50% of the professors and scientists of the universities and science institutions in the country have left Albania. In 2009, the government approved the Nationa" +561,"south where its speakers are concentrated. Other languages spoken by ethnic minorities in Albania include Aromanian, Serbian, Macedonian, Bosnian, Bulgarian, Gorani, and Roma. Macedonian is official in the Pustec Municipality in East Albania. According to the 2011 population census, 2,765,610 or 98.8% of the population declared Albanian as their mother tongue. Because of large migration flows from Albania, over half of Albanians during their life learn a second language. The main foreign language known is English with 40.0%, followed by Italian with 27.8% and Greek with 22.9%. The English speakers were mostly young people, the knowledge of Italian is stable in every age group, while there is a decrease of the speakers of Greek in the youngest group. Among young people aged 25 or less, English, German and Turkish have seen rising interest after 2000. Italian and French have had a stable interest, while Greek has lost much of its previous interest. The trends are linked with cultural and" +562,"You Albania, give me honor, give me the name Albanian , is the national motto of Albania. It finds its foundation in the legacy of national poet Naim Frashëri, who held a renowned role during the Albanian Renaissance. The national anthem of Albania, , was composed by Asdreni and adopted as such following Albania's independence in 1912. === Clothing === Albanian traditional clothing, with its contrasting variations for the northern Gheg and southern Tosk Albanians, is a testament to Albania's history, cultural diversity and ethnic identity. Gheg men wear a light-colored shirt paired with wide loose-fitting trousers referred to as the Tirq. These trousers are securely fastened by a wide woven belt, the Brez. An integral component of their costume is the Xhamadan, a woolen red velvet vest adorned with folkloric motifs and gold patterns. They also wear a distinctive dome-shaped hat known as the Qeleshe, crafted from wool, with its origins tracing back to the Illyrians. Conversely, Tosk men" +563,"such as basil, lavender, mint, oregano, rosemary, and thyme are widely used, as are vegetables such as garlic, onions, peppers, potatoes, tomatoes, as well as legumes of all types. With a coastline along the Adriatic and Ionian in the Mediterranean Sea, fish, crustaceans, and seafood are a popular and an integral part of the Albanian diet. Otherwise, lamb is the traditional meat for different holidays and religious festivals for both Christians and Muslims, although poultry, beef, and pork are also in plentiful supply. Tavë kosi ( soured milk casserole ) is the national dish of Albania, consisting of lamb and rice baked under a thick, tart veil of yoghurt. Fërgesë is another national dish, made up of peppers, tomatoes, and cottage cheese. Pite is also popular, a baked pastry with a filling of a mixture of spinach and gjizë (curd) or mish (ground meat). Petulla, a traditional fried dough, is also a popular speciality, and is served with powdered sugar or feta cheese and different sorts" +564,"won a total of 43 (8 gold, 17 silver and 18 bronze) medals from 1987 to 2013. Popular sports in Albania include football, weightlifting, basketball, volleyball, tennis, swimming, rugby union and gymnastics. Football is by far the most popular sport in Albania. It is governed by the Football Association of Albania (, F.SH.F.), which has membership in FIFA and UEFA. The Albania national football team, ranking 51st in the World in 2017 (highest 22nd on 22 August 2015) have won the 1946 Balkan Cup and the Malta Rothmans International Tournament 2000, but had never participated in any major UEFA or FIFA tournament, until UEFA Euro 2016, Albania's first ever appearance at the continental tournament and at a major men's football tournament. Albania scored their first ever goal in a major tournament and secured their first ever win in European Championship when they beat Romania by 1–0 in a UEFA Euro 2016 match on 19 June 2016. The most successful football clubs are Skënderbeu, KF Tirana, Dina" +565,"Allah (; Allah . Random House Webster's Unabridged Dictionary. ) is the Arabic word for God, particularly the God of Abraham. Outside of the Middle East, it is principally associated with Islam, but the term was used in pre-Islamic Arabia and continues to be used today by Arabic-speaking adherents of any of the Abrahamic religions, including Judaism and Christianity. Islam and Christianity , Encyclopedia of Christianity (2001): Arabic-speaking Christians and Jews also refer to God as Allāh. It is thought to be derived by contraction from al-ilāh (, ) and is linguistically related to God's names in other Semitic languages, such as Aramaic ( ) and Hebrew ( ). The word Allah now implies the superiority or sole existence of one God, but among the pre-Islamic Arabs, Allah was a supreme deity and was worshipped alongside lesser deities in a pantheon. When Muhammad founded Islam, he used Allah to refer to the same unitary God who met Abraham, according to the Bible and the Quran. Many Jews, C" +566,"battle cry Ya La Ibad Allah (O slaves of Allah) to invoke each other into battle.Irfan Shahîd, Byzantium and the Arabs in the Fourth Century, Dumbarton Oaks Trustees for Harvard University-Washington DC, page 418. According to Shahid, on the authority of 10th-century Muslim scholar Al-Marzubani, Allah was also mentioned in pre-Islamic Christian poems by some Ghassanid and Tanukhid poets in Syria and Northern Arabia.Irfan Shahîd, Byzantium and the Arabs in the Fourth Century, Dumbarton Oaks Trustees for Harvard University-Washington DC, Page: 452A. Amin and A. Harun, Sharh Diwan Al-Hamasa (Cairo, 1951), Vol. 1, Pages: 478-480Al-Marzubani, Mu'jam Ash-Shu'araa, Page: 302 Different theories have been proposed regarding the role of Allah in pre-Islamic polytheistic cults. According to the Quran exegete Ibn Kathir, Arab pagans considered Allah as an unseen God who created and controlled the Universe. Pagans believed worship of humans or animals who had lucky events in their life brought them" +567,"children. The concept correlates to the Tawhid, where chapter 112 of the Qur'an (Al-'Ikhlās, The Sincerity) reads:Arabic script in Unicode symbol for a Quran verse, U+06DD, page 3, Proposal for additional Unicode characters :۝ Say, God is one God; :۝ the eternal God: :۝ He begetteth not, neither is He begotten: :۝ and there is not any one like unto Him.Sale, G AlKoran In a Sufi practice known as dhikr Allah (Arabic: ذكر الله, lit. Remembrance of God ), the Sufi repeats and contemplates the name Allah or other associated divine names to Him while controlling his or her breath.Carl W. Ernst, Bruce B. Lawrence, Sufi Martyrs of Love: The Chishti Order in South Asia and Beyond, Macmillan, p. 29 === Present day === ==== Islam ==== The Islamic tradition to use Allah as the personal name of God became disputed in contemporary scholarship, including the question, whether or not the word Allah should be translated as God.Andreas Görke and Johanna Pink Tafsir and Islamic Intellectual History Expl" +568,"John (2002–12). The Biblical World, Oxford, UK: Routledge. .North, Eric McCoy; Eugene Albert Nida ((2nd Edition) 1972). The Book of a Thousand Tongues, London: United Bible Societies.), which was printed in the Netherlands in 1629. Then he translated the Gospel of Mark, published in 1638. The government of Malaysia in 2007 outlawed usage of the term in any other but Muslim contexts, but the Malayan High Court in 2009 revoked the law, ruling it unconstitutional. While had been used for the Christian God in Malay for more than four centuries, the contemporary controversy was triggered by usage of by the Roman Catholic newspaper The Herald. The government appealed the court ruling, and the High Court suspended implementation of its verdict until the hearing of the appeal. In October 2013 the court ruled in favor of the government's ban. In early 2014 the Malaysian government confiscated more than 300 bibles for using the word to refer to the Christian God in Peninsular Malaysia. However," +569,"Algorithms is a monthly peer-reviewed open-access scientific journal of mathematics, covering design, analysis, and experiments on algorithms. The journal is published by MDPI and was established in 2008. The founding editor-in-chief was Kazuo Iwama (Kyoto University). From May 2014 to September 2019, the editor-in-chief was Henning Fernau (Universität Trier). The current editor-in-chief is Frank Werner (Otto-von-Guericke-Universität Magdeburg). ==Abstracting and indexing== According to the Journal Citation Reports, the journal has a 2022 impact factor of 2.3. The journal is abstracted and indexed in: ==See also== Journals with similar scope include: *ACM Transactions on Algorithms *Algorithmica *Journal of Algorithms (Elsevier) ==References== ==External links== * Category:Theoretical computer science journals Category:Open access journals Category:MDPI academic journals Category:English-language journals Category:Academic journals established in 2008 Category:Monthly journals" +570,"Azerbaijan, officially the Republic of Azerbaijan, is a transcontinental country at the boundary of Eastern Europe and West Asia.While often politically aligned with Europe, Azerbaijan is generally considered to be at least mostly in Southwest Asia geographically with its northern part bisected by the standard Asia–Europe divide, the Greater Caucasus. The United Nations classification of world regions places Azerbaijan in Western Asia; the CIA World Factbook places it mostly in Southwest Asia and Merriam-Webster's Collegiate Dictionary places it in both; NationalGeographic.com, and Encyclopædia Britannica also place Georgia in Asia. Conversely, some sources place Azerbaijan in Europe such as Worldatlas.com. It is a part of the South Caucasus region and is bounded by the Caspian Sea to the east, Russia's republic of Dagestan to the north, Georgia to the northwest, Armenia and Turkey to the west, and Iran to the south. Baku is the capital and largest city. The territory of what is now Az" +571,"Sasanian marzban (military governor) held most civil, religious, and military authority. In the first half of the 7th century, Caucasian Albania, as a vassal of the Sasanians, came under nominal Muslim rule with the Muslim conquest of Persia. The Umayyad Caliphate repulsed both the Sasanians and Byzantines from the South Caucasus and turned Caucasian Albania into a vassal state after Christian resistance led by King Juansher was suppressed in 667. The power vacuum left by the decline of the Abbasid Caliphate was filled by numerous local dynasties such as the Sallarids, Sajids, and Shaddadids. At the beginning of the 11th century, the territory was gradually seized by the waves of migrating Oghuz Turks from Central Asia, who adopted a Turkoman ethnonym at the time.Barthold, V.V. Sochineniya; p. 558: Whatever the former significance of the Oghuz people in Eastern Asia, after the events of the 8th and 9th centuries, it focuses more and more on the West, on the border of the Pre-Asian cult" +572,"and Central Asia 2003 Taylor and Francis, 2003. p. 104 After the collapse of the Russian Empire during World War I, the short-lived Transcaucasian Democratic Federative Republic was declared, constituting the present-day republics of Azerbaijan, Georgia, and Armenia. It was followed by the March Days massacres that took place between 30 March and 2 April 1918 in Baku and adjacent areas of the Baku Governorate. When the republic dissolved in May 1918, the leading Musavat party declared independence as the Azerbaijan Democratic Republic (ADR), adopting the name of Azerbaijan , a name that prior to the proclamation of the ADR was solely used to refer to the adjacent northwestern region of contemporary Iran. The ADR was the first modern parliamentary republic in the Muslim world.Schulze, Reinhard. A Modern History of the Islamic World. I.B.Tauris, 2000. . Among the important accomplishments of the Parliament was the extension of suffrage to women, making ADR the first Muslim nation to gran" +573,"died in 2003. He was re-elected to a third term as president in October 2013. In April 2018, Aliyev secured his fourth consecutive term in the election that was boycotted by the main opposition parties as fraudulent. On 27 September 2020, clashes in the unresolved Nagorno-Karabakh conflict resumed along the Nagorno-Karabakh Line of Contact. Both the armed forces of Azerbaijan and Armenia reported military and civilian casualties. The Nagorno-Karabakh ceasefire agreement and the end of the six-week war between Azerbaijan and Armenia was widely celebrated in Azerbaijan, as they made significant territorial gains. Despite the much improved economy, particularly with the exploitation of the Azeri–Chirag–Guneshli oil field and Shah Deniz gas field, the Aliyev family rule has been criticized with election fraud, high levels of economic inequality and domestic corruption. In September 2023, Azerbaijan launched an offensive against the breakaway Republic of Artsakh in Nagorno-Karabakh that res" +574,"Karabakh horse, a mountain-steppe racing and riding horse endemic to Azerbaijan. The Karabakh horse has a reputation for its good temper, speed, elegance, and intelligence. It is one of the oldest breeds, with ancestry dating to the ancient world, but today the horse is an endangered species. Azerbaijan's flora consists of more than 4,500 species of higher plants. Due to the unique climate in Azerbaijan, the flora is much richer in the number of species than the flora of the other republics of the South Caucasus. Sixty-six percent of the species growing in the whole Caucasus can be found in Azerbaijan. The country lies within four ecoregions: Caspian Hyrcanian mixed forests, Caucasus mixed forests, Eastern Anatolian montane steppe, and Azerbaijan shrub desert and steppe. Azerbaijan had a 2018 Forest Landscape Integrity Index mean score of 6.55/10, ranking it 72nd globally out of 172 countries. Forest cover is around 14.% of the total land area, equivalent to 1,131,770 hectares (ha) of" +575,"and bilateral relations; strengthening of defense capability; promotion of security by domestic policy means; strengthening of democracy; preservation of ethnic and religious tolerance; scientific, educational, and cultural policy and preservation of moral values; economic and social development; enhancing internal and border security; and migration, energy, and transportation security policy. Azerbaijan is an active member of international coalitions fighting international terrorism, and was one of the first countries to offer support after the September 11 attacks. The country is an active member of NATO's Partnership for Peace program, contributing to peacekeeping efforts in Kosovo, Afghanistan and Iraq. Azerbaijan is also a member of the Council of Europe since 2001 and maintains good relations with the European Union. The country may eventually apply for EU membership. On 1 July 2021, the US Congress advanced legislation that will have an impact on the military aid that Washington" +576,"2014. Some of this money went to politicians and journalists, as part of an international lobbying operation to deflect criticism of Azerbaijan's president, Ilham Aliyev, and to promote a positive image of his oil-rich country. There was no suggestion that all recipients were aware of the source of the money as it arrived via a disguised route. ===Administrative divisions=== There are 14 economic regions; 66 rayons (, singular ) and 11 cities (, singular ) under the direct authority of the republic. Moreover, Azerbaijan includes the Autonomous Republic () of Nakhchivan. The President of Azerbaijan appoints the governors of these units, while the government of Nakhchivan is elected and approved by the parliament of Nakhchivan Autonomous Republic. ==Economy== After gaining independence in 1991, Azerbaijan became a member of the International Monetary Fund, the World Bank, the European Bank for Reconstruction and Development, the Islamic Development Bank, and the Asian Development Bank. T" +577,"Rank | Passport Index 2017. Passportindex.org. Retrieved 1 July 2017. E-visaHome Page | The Electronic Visa System of Azerbaijan Republic . Evisa.gov.az. Retrieved 1 July 2017. – for a visit of foreigners of visa-required countries to the Republic of Azerbaijan. According to the Travel and Tourism Competitiveness Report 2015 of the World Economic Forum, Azerbaijan holds 84th place.Crotti, Robert and Misrahi, Tiffany(2015) Chapter 1.1 The Travel & Tourism Competitiveness Index 2015: T&T as a Resilient Contribution to National Development in The Travel & Tourism Competitiveness Index 2015. World Economic Forum According to a report by the World Travel and Tourism Council, Azerbaijan was among the top ten countries showing the strongest growth in visitor exports between 2010 and 2016, In addition, Azerbaijan placed first (46.1%) among countries with the fastest-developing travel and tourism economies, with strong indicators for inbound international visitor spending in 2016. ===Transporta" +578,"in Nagorno-Karabakh) are still spoken in Azerbaijan. Each is the mother tongue of around 1.5% of the national population. In 1989, Armenian was the majority language in the region of Nagorno-Karabakh, spoken by about 76% of the regional population. After the first Nagorno-Karabakh war, native speakers of Armenian composed around 95% of the regional population. A dozen other minority languages are spoken natively, including Avar, Budukh, Georgian, Juhuri, Khinalug, Kryts, Lezgin, Rutul, Talysh, Tat, Tsakhur, and Udi. All these are spoken only by small minority populations, some of which are tiny and decreasing.Clifton, John M., editor. 2002 (vol. 1), 2003 (vol. 2). Studies in languages of Azerbaijan. Baku, Azerbaijan and Saint Petersburg, Russia: Institute of International Relations, Academy of Sciences of Azerbaijan and North Eurasian Group, SIL International. ===Religion=== Azerbaijan is considered the most secular Muslim-majority country. Around 97% of the population are Muslims. Aro" +579,"singers have to transform their emotions into singing and music. In contrast to the mugham traditions of Central Asian countries, Azerbaijani mugham is more free-form and less rigid; it is often compared to the improvised field of jazz. UNESCO proclaimed the Azerbaijani mugham tradition a Masterpiece of the Oral and Intangible Heritage of Humanity. Meykhana is a kind of traditional Azerbaijani distinctive folk unaccompanied song, usually performed by several people improvising on a particular subject. Ashiq combines poetry, storytelling, dance, and vocal and instrumental music into a traditional performance art that stands as a symbol of Azerbaijani culture. It is a mystic troubadour or traveling bard who sings and plays the saz. This tradition has its origin in the shamanistic beliefs of ancient Turkic peoples. ashik, shaman – European University Institute, Florence, Italy (retrieved 10 August 2006). Ashiqs' songs are semi-improvised around common bases. Azerbaijan's ashiq art was inc" +580,"consists of two manuscripts copied in the 16th century,Michael E. Meeker, The Dede Korkut Ethic , International Journal of Middle East Studies, Vol. 24, No. 3 (Aug. 1992), 395–417. excerpt: The Book of Dede Korkut is an early record of oral Turkic folktales in Anatolia, and as such, one of the mythic charters of Turkish nationalist ideology. The oldest versions of the Book of Dede Korkut consist of two manuscripts copied in the 16th century. The twelve stories that are recorded in these manuscripts are believed to be derived from a cycle of stories and songs circulating among Turkic peoples living in northeastern Anatolia and northwestern Azerbaijan. According to Lewis (1974), an older substratum of these oral traditions dates to conflicts between the ancient Oghuz and their Turkish rivals in Central Asia (the Pecheneks and the Kipchaks), but this substratum has been clothed in references to the 14th-century campaigns of the Akkoyunlu Confederation of Turkic tribes against the Georgian" +581,"with ground meat and spices). ===Sport=== Freestyle wrestling has been traditionally regarded as Azerbaijan's national sport, in which Azerbaijan has won fourteen medals, including four golds, since joining the International Olympic Committee. The most popular sports are football and wrestling. The Association of Football Federations of Azerbaijan, with 9,122 registered players, is the largest sporting association in the country. The national football team demonstrates relatively low performance in the international arena compared to the nation football clubs. The most successful clubs are Neftçi, Qarabağ, and Gabala. In 2012, Neftchi Baku became the first Azerbaijani team to advance to the group stage of a European competition. In 2014, Qarabağ became the second Azerbaijani club advancing to the group stage of UEFA Europa League. In 2017, after beating Copenhagen 2–2 (a) in the play-off round of the UEFA Champions League, Qarabağ became the first Azerbaijani club to reach the group st" +582,"Amateur astronomy is a hobby where participants enjoy observing or imaging celestial objects in the sky using the unaided eye, binoculars, or telescopes. Even though scientific research may not be their primary goal, some amateur astronomers make contributions in doing citizen science, such as by monitoring variable stars, double stars, sunspots, or occultations of stars by the Moon or asteroids, or by discovering transient astronomical events, such as comets, galactic novae or supernovae in other galaxies. Amateur astronomers do not use the field of astronomy as their primary source of income or support, and usually have no professional degree in astrophysics or advanced academic training in the subject. Most amateurs are hobbyists, while others have a high degree of experience in astronomy and may often assist and work alongside professional astronomers. Many astronomers have studied the sky throughout history in an amateur framework; however, since the beginning of the twentieth cen" +583,"star chart, many others are so faint or inconspicuous that technical means are necessary to locate them. Although many methods are used in amateur astronomy, most are variations of a few specific techniques. === Star hopping === Star hopping is a method often used by amateur astronomers with low-tech equipment such as binoculars or a manually driven telescope. It involves the use of maps (or memory) to locate known landmark stars, and hopping between them, often with the aid of a finderscope. Because of its simplicity, star hopping is a very common method for finding objects that are close to naked-eye stars. More advanced methods of locating objects in the sky include telescope mounts with setting circles, which allow pointing to targets in the sky using celestial coordinates, and GOTO telescopes, which are fully automated telescopes that are capable of locating objects on demand (having first been calibrated). ===Mobile apps=== The advent of mobile applications for use in smartphones" +584,"of the Pacific annually gives Amateur Achievement Awards for significant contributions to astronomy by amateurs. The majority of scientific contributions by amateur astronomers are in the area of data collection. In particular, this applies where large numbers of amateur astronomers with small telescopes are more effective than the relatively small number of large telescopes that are available to professional astronomers. Several organizations, such as the American Association of Variable Star Observers and the British Astronomical Association, exist to help coordinate these contributions. Amateur astronomers often contribute toward activities such as monitoring the changes in brightness of variable stars and supernovae, helping to track asteroids, and observing occultations to determine both the shape of asteroids and the shape of the terrain on the apparent edge of the Moon as seen from Earth. With more advanced equipment, but still cheap in comparison to professional setups, amateur" +585,"Aikido ( , , , ) is a modern Japanese martial art which is split into many different styles including Iwama Ryu, Iwama Shin Shin Aiki Shuren Kai, Shodokan Aikido, Yoshinkan, Renshinkai, Aikikai, and Ki Aikido. Aikido is now practiced in around 140 countries. It was originally developed by Morihei Ueshiba, as a synthesis of his martial studies, philosophy and religious beliefs. Ueshiba's goal was to create an art which practitioners could use to defend themselves while also protecting their attackers from injury. Aikido is often translated as the way of unifying (with) life energy or as the way of harmonious spirit . According to the founder's philosophy, the primary goal in the practice of aikido is to overcome oneself instead of cultivating violence or aggressiveness. Morihei Ueshiba used the phrase to refer to this principle. Aikido's fundamental principles include: (entering), , (breathing control), (triangular principle), and (turning) movements which redirect the opponent's attack" +586,"was followed by Tadashi Abe in 1952, who came as the official Aikikai Hombu representative, remaining in France for seven years. Kenji Tomiki toured with a delegation of various martial arts through 15 continental states of the United States in 1953. Later in the same year, Koichi Tohei was sent by Aikikai Hombu to Hawaii for a full year, where he set up several dōjō. The trip was followed by several visits and is considered the formal introduction of aikido to the United States. The United Kingdom followed in 1955; Italy in 1964 by Hiroshi Tada; and Germany in 1965 by Katsuaki Asai. Designated the Official Delegate for Europe and Africa by Morihei Ueshiba, Masamichi Noro arrived in France in September 1961. Seiichi Sugano was appointed to introduce aikido to Australia in 1965. Today there are aikido dōjō throughout the world. Steven Seagal's film debut Above the Law (1988) is regarded as the first American film to feature aikido in fight sequences. ===Proliferation of independent orga" +587,"that of , are considered essential to aikido training. Both are studying aikido principles of blending and adaptation. learns to blend with and control attacking energy, while learns to become calm and flexible in the disadvantageous, off-balance positions in which places them. This receiving of the technique is called . continuously seeks to regain balance and cover vulnerabilities (e.g., an exposed side), while uses position and timing to keep off-balance and vulnerable. In more advanced training, will sometimes apply to regain balance and pin or throw . refers to the act of receiving a technique. Good involves attention to the technique, the partner, and the immediate environment—it is considered an active part of the process of learning aikido. The method of falling itself is also important, and is a way for the practitioner to receive an aikido technique safely and minimize risk of injury. ===Initial attacks=== Aikido techniques are usually a defense against an attack, so students" +588,"off in are called , and techniques performed with standing and sitting are called (). From these few basic techniques, there are numerous of possible implementations. For example, can be applied to an opponent moving forward with a strike (perhaps with an type of movement to redirect the incoming force), or to an opponent who has already struck and is now moving back to reestablish distance (perhaps an version). Specific aikido are typically referred to with the formula attack-technique(-modifier) ; , for example, refers to any technique executed when is holding one wrist. This could be further specified as (referring to any forward-moving technique from that grab). are strikes (or feints) employed during an aikido technique. Some view as attacks against vital points meant to cause damage in and of themselves. For instance, Gozo Shioda described using in a brawl to quickly down a gang's leader. Others consider , especially to the face, to be methods of distraction meant to enable other" +589,"issue by practicing in a competitive format. Such adaptations are debated between styles, with some maintaining that there is no need to adjust their methods because either the criticisms are unjustified, or that they are not training for self-defense or combat effectiveness, but spiritual, fitness or other reasons. Reasons for the difference and diversity of teachings, intention, and forms of aikido can be traced to the shift in training focus after the end of Ueshiba's seclusion in Iwama from 1942 to the mid-1950s, as he increasingly emphasized the spiritual and philosophical aspects of aikido. As a result, strikes to vital points by , entering () and initiation of techniques by , the distinction between (front side) and (back side) techniques, and the use of weapons, were all de-emphasized or eliminated from practice. Conversely, some styles of aikido place less importance on the spiritual practices which were emphasized by Ueshiba. According to Minoru Shibata of Aikido Journal: O-S" +590,"Art is a diverse range of human activity and its resulting product that involves creative or imaginative talent, generally expressive of technical proficiency, beauty, emotional power, or conceptual ideas. There is no generally agreed definition of what constitutes art, and its interpretation has varied greatly throughout history and across cultures. In the Western tradition, the three classical branches of visual art are painting, sculpture, and architecture. Theatre, dance, and other performing arts, as well as literature, music, film and other media such as interactive media, are included in a broader definition of the arts . Until the 17th century, art referred to any skill or mastery and was not differentiated from crafts or sciences. In modern usage after the 17th century, where aesthetic considerations are paramount, the fine arts are separated and distinguished from acquired skills in general, such as the decorative or applied arts. The nature of art and related concepts, such" +591,"roots in the philosophy of Aristotle. Leo Tolstoy identified art as a use of indirect means to communicate from one person to another.Jerrold Levinson, The Oxford Handbook of Aesthetics, Oxford University Press, 2003, p. 5. Benedetto Croce and R. G. Collingwood advanced the idealist view that art expresses emotions, and that the work of art therefore essentially exists in the mind of the creator.Jerrold Levinson, The Oxford Handbook of Aesthetics, Oxford University Press, 2003, p. 16. R.G. Collingwood's view, expressed in The Principles of Art, is considered in Wollheim, op. cit. 1980 pp. 36–43 The theory of art as form has its roots in the philosophy of Kant, and was developed in the early 20th century by Roger Fry and Clive Bell. More recently, thinkers influenced by Martin Heidegger have interpreted art as the means by which a community develops for itself a medium for self-expression and interpretation.Martin Heidegger, The Origin of the Work of Art , in Poetry, Language, Thought," +592,"depictions of physical and rational certainties of the clockwork universe, as well as politically revolutionary visions of a post-monarchist world, such as Blake's portrayal of Newton as a divine geometer, or David's propagandistic paintings. This led to Romantic rejections of this in favor of pictures of the emotional side and individuality of humans, exemplified in the novels of Goethe. The late 19th century then saw a host of artistic movements, such as academic art, Symbolism, impressionism and fauvism among others.Gombrich, pp. 394–395, 519–527, 573–575. The history of 20th-century art is a narrative of endless possibilities and the search for new standards, each being torn down in succession by the next. Thus the parameters of Impressionism, Expressionism, Fauvism, Cubism, Dadaism, Surrealism, etc. cannot be maintained very much beyond the time of their invention. Increasing global interaction during this time saw an equivalent influence of other cultures into Western art. Thus," +593,"can be defined as an act of expressing feelings, thoughts, and observations. . There is an understanding that is reached with the material as a result of handling it, which facilitates one's thought processes. A common view is that the epithet art, particular in its elevated sense, requires a certain level of creative expertise by the artist, whether this be a demonstration of technical ability, an originality in stylistic approach, or a combination of these two. Traditionally skill of execution was viewed as a quality inseparable from art and thus necessary for its success; for Leonardo da Vinci, art, neither more nor less than his other endeavors, was a manifestation of skill. Rembrandt's work, now praised for its ephemeral virtues, was most admired by his contemporaries for its virtuosity. At the turn of the 20th century, the adroit performances of John Singer Sargent were alternately admired and viewed with skepticism for their manual fluency, yet at nearly the same time the artist" +594,"as scientific illustration, are a form of art as communication. Maps are another example. However, the content need not be scientific. Emotions, moods and feelings are also communicated through art.[Art is a set of] artefacts or images with symbolic meanings as a means of communication. – Steve MithenSteve Mithen. The Prehistory of the Mind: The Cognitive Origins of Art, Religion and Science. 1999 # Art as entertainment. Art may seek to bring about a particular emotion or mood, for the purpose of relaxing or entertaining the viewer. This is often the function of the art industries of motion pictures and video games. # The Avant-Garde. Art for political change. One of the defining functions of early 20th-century art has been to use visual images to bring about political change. Art movements that had this goal—Dadaism, Surrealism, Russian constructivism, and Abstract Expressionism, among others—are collectively referred to as the avant-garde arts.By contrast, the realistic attitude, ins" +595,"directly on the walls of his apartment in Spain, and most likely never discussed them with anyone.* Licht, Fred. Goya: The Origins of the Modern Temper in Art. Universe Books, 1979. The Beatles stated drugs such as LSD and cannabis influenced some of their greatest hits, such as Revolver. Trial and error are considered an integral part of the creation process. ===Appreciation=== The last step is art appreciation, which has the sub-topic of critique. In one study, over half of visual arts students agreed that reflection is an essential step of the art process. According to education journals, the reflection of art is considered an essential part of the experience. However an important aspect of art is that others may view and appreciate it as well. While many focus on whether those viewing/listening/etc. believe the art to be good/successful or not, art has profound value beyond its commercial success as a provider of information and health in society. Art enjoyment can bring about a wi" +596,"to the masses. (The Metropolitan Museum of Art in New York City, for example, was created by John Taylor Johnston, a railroad executive whose personal art collection seeded the museum.) But despite all this, at least one of the important functions of art in the 21st century remains as a marker of wealth and social status. There have been attempts by artists to create art that can not be bought by the wealthy as a status object. One of the prime original motivators of much of the art of the late 1960s and 1970s was to create art that could not be bought and sold. It is necessary to present something more than mere objects said the major post war German artist Joseph Beuys. This time period saw the rise of such things as performance art, video art, and conceptual art. The idea was that if the artwork was a performance that would leave nothing behind, or was an idea, it could not be bought and sold. Democratic precepts revolving around the idea that a work of art is a commodity impelled t" +597,"1843. London: Smith, Elder and Co. The definition and evaluation of art has become especially problematic since the 20th century. Richard Wollheim distinguishes three approaches to assessing the aesthetic value of art: the Realist, whereby aesthetic quality is an absolute value independent of any human view; the Objectivist, whereby it is also an absolute value, but is dependent on general human experience; and the Relativist position, whereby it is not an absolute value, but depends on, and varies with, the human experience of different humans.Wollheim 1980, Essay VI. pp. 231–239. ===Arrival of Modernism=== The arrival of Modernism in the late 19th century led to a radical break in the conception of the function of art,Griselda Pollock, Differencing the Canon. Routledge, London & New York, 1999. and then again in the late 20th century with the advent of postmodernism. Clement Greenberg's 1960 article Modernist Painting defines modern art as the use of characteristic methods of a disci" +598,"on, the work of art itself. === Linguistic turn and its debate=== The end of the 20th century fostered an extensive debate known as the linguistic turn controversy, or the innocent eye debate in the philosophy of art. This debate discussed the encounter of the work of art as being determined by the relative extent to which the conceptual encounter with the work of art dominates over the perceptual encounter with the work of art.Philosophy for Architecture, Branco Mitrovic, 2012. Decisive for the linguistic turn debate in art history and the humanities were the works of yet another tradition, namely the structuralism of Ferdinand de Saussure and the ensuing movement of poststructuralism. In 1981, the artist Mark Tansey created a work of art titled The Innocent Eye as a criticism of the prevailing climate of disagreement in the philosophy of art during the closing decades of the 20th century. Influential theorists include Judith Butler, Luce Irigaray, Julia Kristeva, Michel Foucault and" +599,"fitting social and political outrage. Thus, the debate continues as to what mode of aesthetic satisfaction, if any, is required to define 'art'. The assumption of new values or the rebellion against accepted notions of what is aesthetically superior need not occur concurrently with a complete abandonment of the pursuit of what is aesthetically appealing. Indeed, the reverse is often true, that the revision of what is popularly conceived of as being aesthetically appealing allows for a re-invigoration of aesthetic sensibility, and a new appreciation for the standards of art itself. Countless schools have proposed their own ways to define quality, yet they all seem to agree in at least one point: once their aesthetic choices are accepted, the value of the work of art is determined by its capacity to transcend the limits of its chosen medium to strike some universal chord by the rarity of the skill of the artist or in its accurate reflection in what is termed the zeitgeist. Art is often i" +600,"Theories and Documents of Contemporary Art. Berkeley: University of California Press, 1986 * Kleiner, Gardner, Mamiya and Tansey. Art Through the Ages, Twelfth Edition (2 volumes) Wadsworth, 2004. (vol 1) and (vol 2) * Richard Wollheim, Art and its Objects: An introduction to aesthetics. New York: Harper & Row, 1968. * Will Gompertz. What Are You Looking At?: 150 Years of Modern Art in the Blink of an Eye. New York: Viking, 2012. * Władysław Tatarkiewicz, A History of Six Ideas: an Essay in Aesthetics, translated from the Polish by Christopher Kasparek, The Hague, Martinus Nijhoff, 1980 ==External links== * Art and Play from the Dictionary of the History of ideas * In-depth directory of art * Art and Artist Files in the Smithsonian Libraries Collection (2005) Smithsonian Digital Libraries * Visual Arts Data Service (VADS) – online collections from UK museums, galleries, universities * RevolutionArt – Art magazines with worldwide exhibitions, callings and competitions * Category:Concept" +601,"Agnostida are an order of extinct arthropods which have classically been seen as a group of highly modified trilobites, though some recent research has doubted this placement. Regardless, they appear to be close relatives as part of the Artiopoda. They are present in the Lower Cambrian fossil record along with trilobites from the Redlichiida, Corynexochida, and Ptychopariida orders, and were highly diverse throughout the Cambrian. Agnostidan diversity severely declined during the Cambrian-Ordovician transition, and the last agnostidans went extinct in the Late Ordovician. ==Systematics== The Agnostida are divided into two suborders — Agnostina and Eodiscina — which are then subdivided into a number of families. As a group, agnostids are isopygous, meaning their pygidium is similar in size and shape to their cephalon. Most agnostid species were eyeless. The systematic position of the order Agnostida within the class Trilobita remains uncertain, and there has been continuing debate wheth" +602,"Abortion is the termination of a pregnancy by removal or expulsion of an embryo or fetus. An abortion that occurs without intervention is known as a miscarriage or spontaneous abortion ; these occur in approximately 30% to 40% of all pregnancies. When deliberate steps are taken to end a pregnancy, it is called an induced abortion, or less frequently induced miscarriage . The unmodified word abortion generally refers to an induced abortion. The most common reasons women give for having an abortion are for birth-timing and limiting family size. Other reasons reported include maternal health, an inability to afford a child, domestic violence, lack of support, feeling they are too young, wishing to complete education or advance a career, and not being able or willing to raise a child conceived as a result of rape or incest. When done legally in industrialized societies, induced abortion is one of the safest procedures in medicine. Unsafe abortions—those performed by people lacking the nece" +603,"these spontaneous abortions happen in the first trimester. The most common cause of spontaneous abortion during the first trimester is chromosomal abnormalities of the embryo or fetus, accounting for at least 50% of sampled early pregnancy losses. Other causes include vascular disease (such as lupus), diabetes, other hormonal problems, infection, and abnormalities of the uterus. Advancing maternal age and a woman's history of previous spontaneous abortions are the two leading factors associated with a greater risk of spontaneous abortion. A spontaneous abortion can also be caused by accidental trauma; intentional trauma or stress to cause miscarriage is considered induced abortion or feticide. ==Methods== ===Medical=== Medical abortions are those induced by abortifacient pharmaceuticals. Medical abortion became an alternative method of abortion with the availability of prostaglandin analogs in the 1970s and the antiprogestogen mifepristone (also known as RU-486) in the 1980s. The most" +604,"extraction method. Unlike D&E, labor-induced abortions after 18 weeks may be complicated by the occurrence of brief fetal survival, which may be legally characterized as live birth. For this reason, labor-induced abortion is legally risky in the United States. ===Other methods=== Historically, a number of herbs reputed to possess abortifacient properties have been used in folk medicine. Such herbs include tansy, pennyroyal, black cohosh, and the now-extinct silphium. In 1978, one woman in Colorado died and another developed organ damage when they attempted to terminate their pregnancies by taking pennyroyal oil. Because the indiscriminant use of herbs as abortifacients can cause serious—even lethal—side effects, such as multiple organ failure, such use is not recommended by physicians. Abortion is sometimes attempted by causing trauma to the abdomen. The degree of force, if severe, can cause serious internal injuries without necessarily succeeding in inducing miscarriage. In Southeast" +605,"adverse psychological outcomes. However, women seeking abortion who are denied access to abortion have an increase in anxiety after the denial. Although some studies show negative mental-health outcomes in women who choose abortions after the first trimester because of fetal abnormalities, more rigorous research would be needed to show this conclusively. Some proposed negative psychological effects of abortion have been referred to by anti-abortion advocates as a separate condition called post-abortion syndrome , but this is not recognized by medical or psychological professionals in the United States. A 2020 long term-study among US women found that about 99% of women felt that they made the right decision five years after they had an abortion. Relief was the primary emotion with few women feeling sadness or guilt. Social stigma was a main factor predicting negative emotions and regret years later. The researchers also stated: These results add to the scientific evidence that emotions" +606,"having been performed in 2008. The abortion rate worldwide was 28 per 1000 women per year, though it was 24 per 1000 women per year for developed countries and 29 per 1000 women per year for developing countries. The same 2012 study indicated that in 2008, the estimated abortion percentage of known pregnancies was at 21% worldwide, with 26% in developed countries and 20% in developing countries. On average, the incidence of abortion is similar in countries with restrictive abortion laws and those with more liberal access to abortion. Restrictive abortion laws are associated with increases in the percentage of abortions performed unsafely. The unsafe abortion rate in developing countries is partly attributable to lack of access to modern contraceptives; according to the Guttmacher Institute, providing access to contraceptives would result in about 14.5 million fewer unsafe abortions and 38,000 fewer deaths from unsafe abortion annually worldwide. The rate of legal, induced abortion vari" +607,"advance a career. Additional reasons include not being able or willing to raise a child conceived as a result of rape or incest. ===Societal=== Some abortions are undergone as the result of societal pressures. These might include the preference for children of a specific sex or race, disapproval of single or early motherhood, stigmatization of people with disabilities, insufficient economic support for families, lack of access to or rejection of contraceptive methods, or efforts toward population control (such as China's one-child policy). These factors can sometimes result in compulsory abortion or sex-selective abortion. In cultures where there is a preference for male children, some women have sex selective abortions, which have partially replaced the earlier practice of female infanticide. === Maternal health === Some abortions are performed due to concerns over maternal health. In 1990s, women cited maternal health as their main motivating factor in about a third of abortions in t" +608,"21:22-23). While Judaism encourages people to be fruitful and multiply by having children, abortion is allowed and is deemed necessary when a pregnant woman's life is in danger. Several religions, including Judaism, which disagree that human life begins at conception, support the legality of abortion on religious freedom grounds. In Islam, abortion is traditionally permitted until a point in time when Muslims believe the soul enters the fetus, considered by various theologians to be at conception, 40 days after conception, 120 days after conception, or at quickening. Abortion is largely heavily restricted or forbidden in areas of high Islamic faith such as the Middle East and North Africa. Some medical scholars and abortion opponents have suggested that the Hippocratic Oath forbade physicians in Ancient Greece from performing abortions; other scholars disagree with this interpretation, and state that the medical texts of Hippocratic Corpus contain descriptions of abortive techniques ri" +609,"to legalize abortion after Lenin insisted that no woman be forced to give birth. Iceland (1935) and Sweden (1938) would follow suit to legalize certain or all forms of abortion. In Nazi Germany (1935), a law permitted abortions for those deemed hereditarily ill , while women considered of German stock were specifically prohibited from having abortions.For sources describing abortion policy in Nazi Germany, see: * * * * Beginning in the second half of the 20th century, abortion was legalized in a greater number of countries. In Japan, abortion was first legalized by the 1948 Eugenics Protection Law meant to prevent the births of inferior humans. , due to Japan's continuing strongly patriarchal culture and traditional views on women's societal roles, women who want an abortion must normally get written permission from their partner. ==Society and culture== ===Abortion debate=== Induced abortion has long been the source of considerable debate. Ethical, moral, philosophical, biological, re" +610,"Organization and UNICEF, along with other United Nations agencies, have found that measures to restrict access to abortion in an effort to reduce sex-selective abortions have unintended negative consequences, largely stemming from the fact that women may seek or be coerced into seeking unsafe, extralegal abortions. On the other hand, measures to reduce gender inequality can reduce the prevalence of such abortions without attendant negative consequences. ===Anti-abortion violence=== Abortion providers and facilities have been subjected to violence, including murder, assault, arson, and bombing. Some scholars consider anti-abortion violence to be within the definition of terrorism, a view shared by some governments. In the U.S. and Canada, over 8,000 incidents of violence, trespassing, and death threats have been recorded by providers since 1977, including over 200 bombings/arsons and hundreds of assaults. Abortion clinics have also been targeted by acid attacks, invasions, and vandalism" +611,"In law, an abstract is a brief statement that contains the most important points of a long legal document or of several related legal papers. ==Types of legislation == The abstract of title, used in real estate transactions, is the more common form of abstract. An abstract of title lists all the owners of a piece of land, a house, or a building before it came into possession of the present owner. The abstract also records all deeds, wills, mortgages, and other documents that affect ownership of the property. An abstract describes a chain of transfers from owner to owner and any agreements by former owners that are binding on later owners. ==Patent law== In the context of patent law and specifically in prior art searches, searching through abstracts is a common way to find relevant prior art document to question to novelty or inventive step (or non-obviousness in United States patent law) of an invention. Under United States patent law, the abstract may be called Abstract of the Disclos" +612,"The American Revolutionary War (April 19, 1775 – September 3, 1783), also known as the Revolutionary War or American War of Independence, was an armed conflict that was part of the broader American Revolution, in which American Patriot forces organized as the Continental Army and commanded by George Washington defeated the British Army. The conflict was fought in North America, the Caribbean, and the Atlantic Ocean. The war ended with the Treaty of Paris (1783), which resulted in Great Britain ultimately recognizing the independence and sovereignty of the United States. After the British Empire gained dominance in North America with victory over the French in the Seven Years' War in 1763, tensions and disputes arose between Great Britain and the Thirteen Colonies over a variety of issues, including the Stamp and Townshend Acts. The resulting British military occupation led to the Boston Massacre in 1770. Among further tensions, the British Parliament imposed the Intolerable Acts in mid" +613,"Act, which imposed additional taxes on the colonies to pay for defending the western frontier.Watson and Clark 1960, pp. 183–184 In July 1765, the Whigs formed the First Rockingham ministry, which repealed the Stamp Act and reduced tax on foreign molasses to help the New England economy, but re-asserted Parliamentary authority in the Declaratory Act.Watson and Clark 1960, pp. 116, 187 However, this did little to end the discontent; in 1768, a riot started in Boston when the authorities seized the sloop Liberty on suspicion of smuggling.Morgan 2012, p. 40 Tensions escalated further in March 1770 when British troops fired on rock-throwing civilians, killing five in what became known as the Boston Massacre.Ferling 2007, p. 23 The Massacre coincided with the partial repeal of the Townshend Acts by the Tory-based North Ministry, which came to power in January 1770 and remained in office until 1781. North insisted on retaining duty on tea to enshrine Parliament's right to tax the colonies; t" +614,"combined with the lack of activity by Gage, opposition to the use of foreign troops allowed the Patriots to take control of the legislatures.Miller, J. 1959, pp. 410–412 ===Declaration of Independence=== Support for independence was boosted by Thomas Paine's pamphlet Common Sense, which was published on January 10, 1776, and argued for American self-government and was widely reprinted.Maier 1998, pp. 33–34 To draft the Declaration of Independence, the Second Continental Congress appointed the Committee of Five, consisting of Thomas Jefferson, John Adams, Benjamin Franklin, Roger Sherman, and Robert Livingston.McCullough 2005, pp. 119–122 The declaration was written almost exclusively by Jefferson, who wrote it largely in isolation between June 11 and June 28, 1776, in a three-story residence at 700 Market Street in Philadelphia. The Declaration House Through Time , National Park Services Identifying inhabitants of the Thirteen Colonies as one people , the declaration simultaneously dis" +615,"and Commander in Chief of the army of the United Colonies. Chernow, 2010, p. 187 He assumed command on July 3, preferring to fortify Dorchester Heights outside Boston rather than assaulting it.McCullough 2005, p. 53 In early March 1776, Colonel Henry Knox arrived with heavy artillery acquired in the Capture of Fort Ticonderoga.Frothingham 1903, pp. 100–101 Under cover of darkness, on March 5, Washington placed these on Dorchester Heights,Ferling 2003, p. 183 from where they could fire on the town and British ships in Boston Harbor. Fearing another Bunker Hill, Howe evacuated the city on March 17 without further loss and sailed to Halifax, Nova Scotia, while Washington moved south to New York City.Alden 1969, pp. 188–190 Beginning in August 1775, American privateers raided towns in Nova Scotia, including Saint John, Charlottetown, and Yarmouth. In 1776, John Paul Jones and Jonathan Eddy attacked Canso and Fort Cumberland respectively. British officials in Quebec began negotiating with t" +616,"the East River to Manhattan on flat-bottomed freight boats without any losses in men or ordnance, leaving General Thomas Mifflin's regiments as a rearguard.McCullough 2005, pp. 184–186 Howe met with a delegation from the Second Continental Congress at the September Staten Island Peace Conference, but it failed to conclude peace, largely because the British delegates only had the authority to offer pardons and could not recognize independence.McGuire 2011, pp. 165–166 On September 15, Howe seized control of New York City when the British landed at Kip's Bay and unsuccessfully engaged the Americans at the Battle of Harlem Heights the following day.Fischer, D. 2004, pp. 102–107 On October 18, Howe failed to encircle the Americans at the Battle of Pell's Point, and the Americans withdrew. Howe declined to close with Washington's army on October 28 at the Battle of White Plains, and instead attacked a hill that was of no strategic value.Fischer, D. 2004, pp. 102–111 Washington's retreat iso" +617,"Ticonderoga on July 5. As General Horatio Gates retreated, his troops blocked roads, destroyed bridges, dammed streams, and stripped the area of food.Ketchum 1997, pp. 244–249 This slowed Burgoyne's progress and forced him to send out large foraging expeditions; on one of these, more than 700 British troops were captured at the Battle of Bennington on August 16.Gabriel 2012, p. x St Leger moved east and besieged Fort Stanwix; despite defeating an American relief force at the Battle of Oriskany on August 6, he was abandoned by his Indian allies and withdrew to Quebec on August 22.Ketchum 1997, p. 332 Now isolated and outnumbered by Gates, Burgoyne continued onto Albany rather than retreating to Fort Ticonderoga, reaching Saratoga on September 13. He asked Clinton for support while constructing defenses around the town.Ketchum 1997, pp. 337–339 Morale among his troops rapidly declined, and an unsuccessful attempt to break past Gates at the Battle of Freeman Farms on September 19 resulted" +618,"insist on nothing short of complete independence.Stockley 2001, pp. 11–14 On February 6, 1778, France and the United States signed the Treaty of Amity and Commerce regulating trade between the two countries, followed by a defensive military alliance against Britain, the Treaty of Alliance. In return for French guarantees of American independence, Congress undertook to defend their interests in the West Indies, while both sides agreed not to make a separate peace; conflict over these provisions would lead to the 1798 to 1800 Quasi-War. Charles III of Spain was invited to join on the same terms but refused, largely due to concerns over the impact of the Revolution on Spanish colonies in the Americas. Spain had complained on multiple occasions about encroachment by American settlers into Louisiana, a problem that could only get worse once the United States replaced Britain. Although Spain ultimately made important contributions to American success, in the Treaty of Aranjuez, Charles agree" +619,"363–380 In June 1780, Clinton sent 6,000 men under Wilhelm von Knyphausen to retake New Jersey, but they were halted by local militia at the Battle of Connecticut Farms; although the Americans withdrew, Knyphausen felt he was not strong enough to engage Washington's main force and retreated.Fleming 2005 [1973], pp. 174–175 A second attempt two weeks later ended in a British defeat at the Battle of Springfield, effectively ending their ambitions in New Jersey.Fleming 2005 [1973], pp. 232, 302 In July, Washington appointed Benedict Arnold commander of West Point; his attempt to betray the fort to the British failed due to incompetent planning, and the plot was revealed when his British contact John André was captured and later executed.Palmer 2010, pp. 340–342 Arnold escaped to New York and switched sides, an action justified in a pamphlet addressed To the Inhabitants of America ; the Patriots condemned his betrayal, while he found himself almost as unpopular with the British.Palmer 2010" +620,"at the cost of losses they could not replace. Despite halting Greene's advance at the Battle of Eutaw Springs on September 8, Cornwallis withdrew to Charleston with little to show for his campaign.Pancake 1985, p. 221 ===Western campaign=== From the beginning of the war, Bernardo de Gálvez, the Governor of Spanish Louisiana, allowed the Americans to import supplies and munitions into New Orleans, then ship them to Pittsburgh.Narrett 2015, p. 81 This provided an alternative transportation route for the Continental Army, bypassing the British blockade of the Atlantic Coast.Chavez 2002, p. 108 The trade was organized by Oliver Pollock, a successful merchant in Havana and New Orleans, who was appointed U.S. commercial agent.Narrett 2015, p. 71 It also helped support the American campaign in the west. In February 1778, an expedition of militia to destroy British military supplies in settlements along the Cuyahoga River was halted by adverse weather.Nester 2004, p. 194 Later in the year, a s" +621,"August 31, a Royal Navy fleet under Thomas Graves left New York for Yorktown.Middleton 2014, pp. 29–43 After landing troops and munitions for the besiegers on August 30, de Grasse remained in Chesapeake Bay and intercepted him on September 5; although the Battle of the Chesapeake was indecisive in terms of losses, Graves was forced to retreat, leaving Cornwallis isolated.Black 1992, p. 110 An attempted breakout over York River at Gloucester Point failed due to bad weather.Dale 2005, pp. 36–37 Under heavy bombardment with dwindling supplies, Cornwallis felt his situation was hopeless and on October 16 sent emissaries to General Washington to negotiate their surrender; after twelve hours of negotiations, the terms of surrender were finalized the following day.Ferling 2007, pp. 534–535 Responsibility for defeat was the subject of fierce public debate between Cornwallis, Clinton, and Germain. Despite criticism from his junior officers, Cornwallis retained the confidence of his peers and la" +622,"be the origin of the modern United States Army, and appointed Washington as its commander-in-chief. However, it suffered significantly from the lack of an effective training program and from largely inexperienced officers.Ferling 2007, pp. 286–287 Each state legislature appointed officers for both county and state militias and their regimental Continental line officers; although Washington was required to accept Congressional appointments, he was permitted to choose and command his own generals, such as Greene; his chief of artillery, Knox; and Alexander Hamilton, the chief of staff.Higginbotham 1987, Chap. 3 One of Washington's most successful general officer recruits was Steuben, a veteran of the Prussian general staff who wrote the Revolutionary War Drill Manual. The development of the Continental Army was always a work in progress and Washington used both his regulars and state militias throughout the war; when properly employed, the combination allowed them to overwhelm smaller Br" +623,"established its military credibility. Battles such as the Battle of Bennington, the Battles of Saratoga, and even defeats such as the Battle of Germantown, proved decisive in gaining the support of powerful European nations, including France, Spain, and the Dutch Republic; the Dutch moved from covertly supplying the Americans with weapons and supplies to overtly supporting them.Trevelyan 1912a, p. 249 The decisive American victory at Saratoga convinced France, which was already a long-time rival of Britain, to offer the Americans the Treaty of Amity and Commerce. The two nations also agreed to a defensive Treaty of Alliance to protect their trade and also guaranteed American independence from Britain. To engage the United States as a French ally militarily, the treaty was conditioned on Britain initiating a war on France to stop it from trading with the U.S. Spain and the Dutch Republic were invited to join by both France and the United States in the treaty, but neither was responsive" +624,"debated; despite receiving large numbers of reinforcements, Bunker Hill seems to have permanently affected his self-confidence and lack of tactical flexibility meant he often failed to follow up opportunities.Fleming 2006, p. 44 Many of his decisions were attributed to supply problems, such as his failure to pursue Washington's beaten army.Davies, K. 1972, vol. 12 – 1776, 5:93, Howe to Germain, June 7 and July 7, 1776 Having lost the confidence of his subordinates, he was recalled after Burgoyne surrendered at Saratoga.O'Shaunessy 2013, p. 216 Following the failure of the Carlisle Commission, British policy changed from treating the Patriots as subjects who needed to be reconciled to enemies who had to be defeated.Hibbert 2000, pp. 160–161 In 1778, Howe was replaced by Sir Henry Clinton.O'Shaunessy 2013, p. Regarded as an expert on tactics and strategy, like his predecessors Clinton was handicapped by chronic supply issues.Davies, K. 1972, vol. 15 – 1778, 5:96, Clinton to Germain, Sept" +625,"turned to hard war during Clinton's command, neutral colonists in the Carolinas often allied with the Patriots.Black 2001 [1991], pp. 14–16 [16], 35, 38 Conversely, Loyalists gained support when Patriots intimidated suspected Tories by destroying property or tarring and feathering.Calhoon 1973, p. A Loyalist militia unit—the British Legion—provided some of the best troops in British service.Buchanan 1997, p. 327 It was commanded by Tarleton and gained a fearsome reputation in the colonies for brutality and needless slaughter .Bass 1957, pp. 548–550 ===Women=== Women played various roles during the Revolutionary War; they often accompanied their husbands when permitted. For example, throughout the war Martha Washington was known to visit and provide aid to her husband George at various American camps.Chernow, 2010, p. 215 Women often accompanied armies as camp followers to sell goods and perform necessary tasks in hospitals and camps, and numbered in the thousands during the war.Dunkerl" +626,"as a result of the American Revolutionary War. The Seneca, Onondaga, and Cayuga tribes sided with the British; members of the Mohawks fought on both sides; and many Tuscarora and Oneida sided with the Americans. To retaliate against raids on American settlement by Loyalists and their Indian allies, the Continental Army dispatched the Sullivan Expedition throughout New York to debilitate the Iroquois tribes that had sided with the British. Mohawk leaders Joseph Louis Cook and Joseph Brant sided with the Americans and the British respectively, which further exacerbated the split.Ferling 2007, pp. 200–203 In the western theater, conflicts between settlers and Native Americans led to lingering distrust.Reid, D. 2017, p. In the 1783 Treaty of Paris, Great Britain ceded control of the disputed lands between the Great Lakes and the Ohio River, but Native inhabitants were not a part of the peace negotiations.Carroll 2001, p. 24 Tribes in the Northwest Territory joined as the Western Confederac" +627,"establish an Indian barrier state below the Great Lakes as late as 1814 during the War of 1812. The formally acquired western American lands continued to be populated by Indigenous tribes that had mostly been British allies. In practice the British refused to abandon the forts on territory they formally transferred. Instead, they provisioned military allies for continuing frontier raids and sponsored the Northwest Indian War (1785–1795). British sponsorship of local warfare on the U.S. continued until the Anglo-American Jay Treaty, authored by Hamilton, went into effect on February 29, 1796.Benn 1993, p. 17 Of the European powers with American colonies adjacent to the newly created U.S., Spain was most threatened by American independence, and it was correspondingly the most hostile to it. Its territory adjacent to the U.S. was relatively undefended, so Spanish policy developed a combination of initiatives. Spanish soft power diplomatically challenged the British territorial cession wes" +628,"Historians have examined how the rising American legal profession adopted British common law to incorporate republicanism by selective revision of legal customs and by introducing more choices for courts.Ellen Holmes Pearson. Revising Custom, Embracing Choice: Early American Legal Scholars and the Republicanization of the Common Law , in Gould and Onuf, eds. Empire and Nation: The American Revolution in the Atlantic World (2005) pp. 93–113Anton-Hermann Chroust, Rise of the Legal Profession in America (1965) vol. 2. ===Revolutionary War commemoration stamps=== After the first U.S. postage stamp was issued in 1849, the U.S. Postal Service frequently issued commemorative stamps celebrating people and events of the Revolutionary War. The first such stamp was the Liberty Bell issue of 1926. File:150th Anniversary of the Liberty Bell, 1926 Issue-2c.jpg|upright=1|The Liberty Bell stamp, issued on the 150th anniversary of American independence in 1926 File:Saratoga 1777 Oriskany 1927 Issue-2c." +629,"seize hostages 'of every age and sex.' The following year, while serving as governor of Virginia, [Thomas] Jefferson ordered troops under the command of George Rogers Clark to enter the Ohio Valley and bring about the expulsion or 'extermination' of local Indians. (pp. 34–35.) * * * * * * * * * * * * * * * * Kwasny, Mark V. Washington's Partisan War, 1775–1783. Kent, Ohio: 1996. . Militia warfare. * * * * Library of Congress * * May, Robin. The British Army in North America 1775–1783 (1993). Short (48pp), very well illustrated descriptions. * * * * * * National Institute of Health * Neimeyer, Charles Patrick. America Goes to War: A Social History of the Continental Army (1995) * * * * * * * * Royal Navy Museum * * * * * * * Stoker, Donald, Kenneth J. Hagan, and Michael T. McMaster, eds. Strategy in the American War of Independence: a global approach (Routledge, 2009) excerpt. * Symonds, Craig L. A Battlefield Atlas of the American Revolution (1989), newly drawn maps emphasizing the mov" +630,"The ampere ( , ; symbol: A), often shortened to amp,SI supports only the use of symbols and deprecates the use of abbreviations for units. is the unit of electric current in the International System of Units (SI). One ampere is equal to 1 coulomb (C) moving past a point per second. It is named after French mathematician and physicist André-Marie Ampère (1775–1836), considered the father of electromagnetism along with Danish physicist Hans Christian Ørsted. As of the 2019 revision of the SI, the ampere is defined by fixing the elementary charge to be exactly , which means an ampere is an electric current equivalent to elementary charges moving every seconds or elementary charges moving in a second. Prior to the redefinition the ampere was defined as the current passing through two parallel wires 1 metre apart that produces a magnetic force of newtons per metre. The earlier CGS system has two units of current, one structured similarly to the SI's and the other using Coulomb's law as a fu" +631,"in the context of electrical engineering and electrical appliances, but are defined independently of the ampere, notably the hertz, joule, watt, candela, lumen, and lux. == SI prefixes == Like other SI units, the ampere can be modified by adding a prefix that multiplies it by a power of 10. == See also == * * * * * * * == References == == External links == * The NIST Reference on Constants, Units, and Uncertainty * NIST Definition of ampere and μ0 Category:SI base units Category:Units of electric current" +632,"In mathematics and computer science, an algorithm () is a finite sequence of mathematically rigorous instructions, typically used to solve a class of specific problems or to perform a computation. Algorithms are used as specifications for performing calculations and data processing. More advanced algorithms can use conditionals to divert the code execution through various routes (referred to as automated decision-making) and deduce valid inferences (referred to as automated reasoning). In contrast, a heuristic is an approach to solving problems that do not have well-defined correct or optimal results.David A. Grossman, Ophir Frieder, Information Retrieval: Algorithms and Heuristics, 2nd edition, 2004, For example, although social media recommender systems are commonly called algorithms , they actually rely on heuristics as there is no truly correct recommendation. As an effective method, an algorithm can be expressed within a finite amount of space and time Any classical mathematical a" +633,"of Charles Babbage and Ada Lovelace in the mid-19th century.Bolter 1984:33–34, 204–206. Lovelace designed the first algorithm intended for processing on a computer, Babbage's analytical engine, which is the first device considered a real Turing-complete computer instead of just a calculator. Although a full implementation of Babbage's second device was not realized for decades after her lifetime, Lovelace has been called history's first programmer . ==== Electromechanical relay ==== Bell and Newell (1971) write that the Jacquard loom, a precursor to Hollerith cards (punch cards), and telephone switching technologies led to the development of the first computers.Bell and Newell diagram 1971:39, cf. Davis 2000 By the mid-19th century, the telegraph, the precursor of the telephone, was in use throughout the world. By the late 19th century, the ticker tape () was in use, as were Hollerith cards (c. 1890). Then came the teleprinter () with its punched-paper use of Baudot code on tape. Telep" +634,"(like digital cameras and medical equipment) to consume less power. == Design == Algorithm design is a method or mathematical process for problem-solving and engineering algorithms. The design of algorithms is part of many solution theories, such as divide-and-conquer or dynamic programming within operation research. Techniques for designing and implementing algorithm designs are also called algorithm design patterns, with examples including the template method pattern and the decorator pattern. One of the most important aspects of algorithm design is resource (run-time, memory usage) efficiency; the big O notation is used to describe e.g., an algorithm's run-time growth as the size of its input increases. === Structured programming === Per the Church–Turing thesis, any algorithm can be computed by any Turing complete model. Turing completeness only requires four instruction types—conditional GOTO, unconditional GOTO, assignment, HALT. However, Kemeny and Kurtz observe that, while undi" +635,"algorithms make some choices randomly (or pseudo-randomly). They find approximate solutions when finding exact solutions may be impractical (see heuristic method below). For some problems the fastest approximations must involve some randomness.For instance, the volume of a convex polytope (described using a membership oracle) can be approximated to high accuracy by a randomized polynomial time algorithm, but not by a deterministic one: see Whether randomized algorithms with polynomial time complexity can be the fastest algorithm for some problems is an open question known as the P versus NP problem. There are two large classes of such algorithms: # Monte Carlo algorithms return a correct answer with high probability. E.g. RP is the subclass of these that run in polynomial time. # Las Vegas algorithms always return the correct answer, but their running time is only probabilistically bound, e.g. ZPP. ; Reduction of complexity : This technique transforms difficult problems into better-kno" +636,"algorithms * List of algorithm general topics * Medium is the message * Regulation of algorithms * Theory of computation ** Computability theory ** Computational complexity theory == Notes == == Bibliography == * * Bell, C. Gordon and Newell, Allen (1971), Computer Structures: Readings and Examples, McGraw–Hill Book Company, New York. . * Includes a bibliography of 56 references. * , * : cf. Chapter 3 Turing machines where they discuss certain enumerable sets not effectively (mechanically) enumerable . * * Campagnolo, M.L., Moore, C., and Costa, J.F. (2000) An analog characterization of the subrecursive functions. In Proc. of the 4th Conference on Real Numbers and Computers, Odense University, pp. 91–109 * Reprinted in The Undecidable, p. 89ff. The first expression of Church's Thesis . See in particular page 100 (The Undecidable) where he defines the notion of effective calculability in terms of an algorithm , and he uses the word terminates , etc. * Reprinted in The Undecidable, p. 11" +637,"An annual plant is a plant that completes its life cycle, from germination to the production of seeds, within one growing season, and then dies. Globally, 6% of all plant species and 15% of herbaceous plants (excluding trees and shrubs) are annuals. The annual life cycle has independently emerged in over 120 different plant families throughout the entire angiosperm phylogeny. == The evolutionary and ecological drivers of the annual life cycle == Traditionally, there has been a prevailing assumption that annuals have evolved from perennial ancestors. However, recent research challenges this notion, revealing instances where perennials have evolved from annual ancestors. Intriguingly, models propose that transition rates from an annual to a perennial life cycle are twice as fast as the reverse transition. The life-history theory posits that annual plants are favored when adult mortality is higher than seedling (or seed) mortality, i.e., annuals will dominate environments with disturbance" +638,"The anthophytes are a paraphyletic grouping of plant taxa bearing flower-like reproductive structures. The group, once thought to be a clade, contained the angiosperms – the extant flowering plants, such as roses and grasses – as well as the Gnetales and the extinct Bennettitales. Detailed morphological and molecular studies have shown that the group is not actually monophyletic, with proposed floral homologies of the gnetophytes and the angiosperms having evolved in parallel. This makes it easier to reconcile molecular clock data that suggests that the angiosperms diverged from the gymnosperms around 320-300 mya. Some more recent studies have used the word anthophyte to describe a hypothetical group which includes the angiosperms and a variety of extinct seed plant groups (with various suggestions including at least some of the following groups: glossopterids, corystosperms, Petriellales Pentoxylales, Bennettitales and Caytoniales), but not the Gnetales. ==References== Category:Histor" +639,"An atlas is a collection of maps. Atlas may also refer to: ==Arts, entertainment and media== ===Fictional characters=== * Atlas (DC Comics), several fictional characters * Atlas (Teen Titans) * Atlas, an Astro Boy (1980) character * Atlas (BioShock) * Atlas, a BattleMech in the BattleTech universe * Atlas, an antagonist in Mega Man ZX Advent * Atlas, a Portal 2 character * Atlas, a PS238 character * Erik Josten, a.k.a. Atlas, a Marvel Comics supervillain * The Atlas, a strong driving force from No Man's Sky ===Literature=== * Atlas, a photography book by Gerhard Richter * The Atlas (novel), by William T. Vollmann * Atlas (magazine) * The Atlas (newspaper), published in England from 1826 to 1869 ===Music=== ====Bands==== * Atlas (band), a New Zealand rock band ====Albums==== * Atlas (Kinky album) * Atlas (Laurel Halo album) * Atlas (Parkway Drive album) * Atlas (Real Estate album) * Atlas (RÜFÜS album) * Atlas (The Score album) ====Opera==== * Atlas (opera), 1991, by Meredith Monk **Atl" +640,"cervical spine * Atlas personality, the personality of someone whose childhood was characterized by excessive responsibilities ====Animals and plants==== * Atlas bear * Atlas beetle * Atlas cedar * Atlas moth * Atlas pied flycatcher, a bird * Atlas turtle * Atlas, a book about flora and/or fauna of a region, such as atlases of the flora and fauna of Britain and Ireland ==Sport== * Atlas Delmenhorst, a German association football club * Atlas F.C., a Mexican professional football club * Club Atlético Atlas, an Argentine amateur football club * KK Atlas, a former Serbian men's professional basketball club ==Transport== ===Aerospace=== * Atlas (rocket family) * SM-65 Atlas intercontinental ballistic missile (ICBM) * AeroVelo Atlas, a human-powered helicopter * Birdman Atlas, an ultralight aircraft * La Mouette Atlas, a French hang glider design ===Automotive=== * Atlas (1951 automobile), a French mini-car * Atlas (light trucks), a Greek motor vehicle manufacturer * Atlas (Pittsburgh autom" +641,"Mouthwash, mouth rinse, oral rinse, or mouth bath is a liquid which is held in the mouth passively or swirled around the mouth by contraction of the perioral muscles and/or movement of the head, and may be gargled, where the head is tilted back and the liquid bubbled at the back of the mouth. Usually mouthwashes are antiseptic solutions intended to reduce the microbial load in the mouth, although other mouthwashes might be given for other reasons such as for their analgesic, anti-inflammatory or anti-fungal action. Additionally, some rinses act as saliva substitutes to neutralize acid and keep the mouth moist in xerostomia (dry mouth). Cosmetic mouthrinses temporarily control or reduce bad breath and leave the mouth with a pleasant taste. Rinsing with water or mouthwash after brushing with a fluoride toothpaste can reduce the availability of salivary fluoride. This can lower the anti-cavity re-mineralization and antibacterial effects of fluoride. Fluoridated mouthwash may mitigate this" +642,"microscopist, discovered living organisms (living, because they were mobile) in deposits on the teeth (what we now call dental plaque). He also found organisms in water from the canal next to his home in Delft. He experimented with samples by adding vinegar or brandy and found that this resulted in the immediate immobilization or killing of the organisms suspended in water. Next he tried rinsing the mouth of himself and somebody else with a mouthwash containing vinegar or brandy and found that living organisms remained in the dental plaque. He concluded—correctly—that the mouthwash either did not reach, or was not present long enough, to kill the plaque organisms. In 1892, German Richard Seifert invented mouthwash product Odol, which was produced by company founder Karl August Lingner (1861–1916) in Dresden.Lingner-Archiv (German) That remained the state of affairs until the late 1960s when Harald Loe (at the time a professor at the Royal Dental College in Aarhus, Denmark) demonstrated" +643,"effects of chlorhexidine. Chlorhexidine mouthwash can also cause taste disturbance or alteration. Chlorhexidine is rarely associated with other issues like overgrowth of enterobacteria in persons with leukemia, desquamation, irritation, and stomatitis of oral mucosa, salivary gland pain and swelling, and hypersensitivity reactions including anaphylaxis. Hexetidine also has anti-plaque, analgesic, astringent and anti-malodor properties, but is considered an inferior alternative to chlorhexidine. ===Chlorine dioxide=== In dilute concentrations, chlorine dioxide is an ingredient that acts as an antiseptic agent in some mouthwashes. ===Edible oils=== In traditional Ayurvedic medicine, the use of oil mouthwashes is called Kavala ( oil swishing ) or Gandusha , and this practice has more recently been re-marketed by the complementary and alternative medicine industry as oil pulling . Its promoters claim it works by pulling out toxins , which are known as ama in Ayurvedic medicine, and thereby" +644,"has a mechanical cleansing action and an antiseptic action, as it is a hypertonic solution in relation to bacteria, which undergo lysis. The heat of the solution produces a therapeutic increase in blood flow (hyperemia) to the surgical site, promoting healing. Hot saltwater mouthwashes also encourage the draining of pus from dental abscesses. In contrast, if heat is applied on the side of the face (e.g., hot water bottle) rather than inside the mouth, it may cause a dental abscess to drain extra-orally, which is later associated with an area of fibrosis on the face (see cutaneous sinus of dental origin). Saltwater mouthwashes are also routinely used after oral surgery, to keep food debris out of healing wounds and to prevent infection. Some oral surgeons consider saltwater mouthwashes the mainstay of wound cleanliness after surgery. In dental extractions, hot saltwater mouthbaths should start about 24 hours after a dental extraction. The term mouth bath implies that the liquid is passi" +645,"Alexander III of Macedon (; 20/21 July 356 BC – 10/11 June 323 BC), most commonly known as Alexander the Great, was a king of the ancient Greek kingdom of Macedon. He succeeded his father Philip II to the throne in 336 BC at the age of 20 and spent most of his ruling years conducting a lengthy military campaign throughout Western Asia, Central Asia, parts of South Asia, and Egypt. By the age of 30, he had created one of the largest empires in history, stretching from Greece to northwestern India.Bloom, Jonathan M.; Blair, Sheila S. (2009) The Grove Encyclopedia of Islamic Art and Architecture: Mosul to Zirid, Volume 3. (Oxford University Press Incorporated, 2009), 385; [Khojand, Tajikistan]; As the easternmost outpost of the empire of Alexander the Great, the city was renamed Alexandria Eschate ( furthest Alexandria ) in 329 BCE. Golden, Peter B. Central Asia in World History (Oxford University Press, 2011), 25; [...] his campaigns in Central Asia brought Khwarazm, Sogdia and Bactria u" +646,"the World, burnt down. This led Hegesias of Magnesia to say that it had burnt down because Artemis was away, attending the birth of Alexander., Such legends may have emerged when Alexander was king, and possibly at his instigation, to show that he was superhuman and destined for greatness from conception. In his early years, Alexander was raised by a nurse, Lanike, sister of Alexander's future general Cleitus the Black. Later in his childhood, Alexander was tutored by the strict Leonidas, a relative of his mother, and by Lysimachus of Acarnania. Alexander was raised in the manner of noble Macedonian youths, learning to read, play the lyre, ride, fight, and hunt. When Alexander was ten years old, a trader from Thessaly brought Philip a horse, which he offered to sell for thirteen talents. The horse refused to be mounted, and Philip ordered it away. Alexander, however, detecting the horse's fear of its own shadow, asked to tame the horse, which he eventually managed. Plutarch stated that" +647,"and announced his plans to attack the Persian Empire. ===Exile and return=== When Philip returned to Pella, he fell in love with and married Cleopatra Eurydice in 338 BC, the niece of his general Attalus. The marriage made Alexander's position as heir less secure, since any son of Cleopatra Eurydice would be a fully Macedonian heir, while Alexander was only half-Macedonian. During the wedding banquet, a drunken Attalus publicly prayed to the gods that the union would produce a legitimate heir. In 337 BC, Alexander fled Macedon with his mother, dropping her off with her brother, King Alexander I of Epirus in Dodona, capital of the Molossians. He continued to Illyria where he sought refuge with one or more Illyrian kings, perhaps with Glaucias, and was treated as a guest, despite having defeated them in battle a few years before.A History of Macedonia: Volume III: 336–167 B.C. By N. G. L. Hammond, F. W. Walbank However, it appears Philip never intended to disown his politically and milit" +648,"his victory at the Battle of Chaeronea (338 BC), Philip II began the work of establishing himself as hēgemṓn () of a league which according to Diodorus was to wage a campaign against the Persians for the sundry grievances Greece suffered in 480 and free the Greek cities of the western coast and islands from Achaemenid rule. In 336 he sent Parmenion, Amyntas, Andromenes, Attalus, and an army of 10,000 men into Anatolia to make preparations for an invasion. The Greek cities on the western coast of Anatolia revolted until the news arrived that Philip had been murdered and had been succeeded by his young son Alexander. The Macedonians were demoralized by Philip's death and were subsequently defeated near Magnesia by the Achaemenids under the command of the mercenary Memnon of Rhodes. Taking over the invasion project of Philip II, Alexander's army crossed the Hellespont in 334 BC with approximately 48,100 soldiers, 6,100 cavalry, and a fleet of 120 ships with crews numbering 38,000 drawn fr" +649,"of the battle of Gaugamela Darius once more fled the field, and Alexander chased him as far as Arbela. Gaugamela would be the final and decisive encounter between the two. Darius fled over the mountains to Ecbatana (modern Hamadan) while Alexander captured Babylon. Babylonian astronomical diaries say that the king of the world, Alexander sent his scouts with a message to the people of Babylon before entering the city: I shall not enter your houses . ===Persia=== thumb|Site of the Persian Gate in modern-day Iran; the road was built in the 1990s. From Babylon, Alexander went to Susa, one of the Achaemenid capitals, and captured its treasury. He sent the bulk of his army to the Persian ceremonial capital of Persepolis via the Persian Royal Road. Alexander himself took selected troops on the direct route to the city. He then stormed the pass of the Persian Gates (in the modern Zagros Mountains) which had been blocked by a Persian army under Ariobarzanes and then hurried to Persepolis befor" +650,"failing to alert Alexander. The death of the son necessitated the death of the father, and thus Parmenion, who had been charged with guarding the treasury at Ecbatana, was assassinated at Alexander's command, to prevent attempts at vengeance. Most infamously, Alexander personally killed the man who had saved his life at Granicus, Cleitus the Black, during a violent drunken altercation at Maracanda (modern day Samarkand in Uzbekistan), in which Cleitus accused Alexander of several judgmental mistakes and especially of having forgotten the Macedonian ways in favour of a corrupt oriental lifestyle. Later, in the Central Asian campaign, a second plot against his life was revealed. This one was instigated by his own royal pages. His official historian, Callisthenes of Olynthus, was implicated in the plot, and in the Anabasis of Alexander, Arrian states that Callisthenes and the pages were then tortured on the rack as punishment, and likely died soon after. It remains unclear if Callisthenes" +651,"(323 BC), as well as in the subsequent partition of the provinces at Triparadisus, 321 BC. In the winter of 327/326 BC, Alexander personally led a campaign against the Aspasioi of the Kunar Valley, the Guraeans of the Guraeus Valley, and the Assakenoi of the Swat and Buner Valleys. A fierce contest ensued with the Aspasioi in which Alexander was wounded in the shoulder by a dart, but eventually the Aspasioi lost. Alexander then faced the Assakenoi who fought against him from the strongholds of Massaga, Ora, and Aornos. The fort of Massaga was reduced after days of bloody fighting in which Alexander was seriously wounded in the ankle. According to Curtius, Not only did Alexander slaughter the entire population of Massaga, but also did he reduce its buildings to rubble. A similar slaughter followed at Ora. In the aftermath of Massaga and Ora, numerous Assakenians fled to the fortress of Aornos. Alexander followed close behind and captured the strategic hill-fort after four bloody days. A" +652,"silently waved at them. In the second account, Diodorus recounts that Alexander was struck with pain after downing a large bowl of unmixed wine in honour of Heracles followed by 11 days of weakness; he did not develop a fever, instead dying after some agony. Arrian also mentioned this as an alternative, but Plutarch specifically denied this claim. Given the propensity of the Macedonian aristocracy to assassination, foul play featured in multiple accounts of his death. Diodorus, Plutarch, Arrian and Justin all mentioned the theory that Alexander was poisoned. Justin stated that Alexander was the victim of a poisoning conspiracy, Plutarch dismissed it as a fabrication, while both Diodorus and Arrian noted that they mentioned it only for the sake of completeness. The accounts were nevertheless fairly consistent in designating Antipater, recently removed as Macedonian viceroy, replaced by Craterus, as the head of the alleged plot. Perhaps taking his summons to Babylon as a death sentence a" +653,"cavalry, in front of witnesses, thereby nominating him. Perdiccas initially did not claim power, instead suggesting that Roxane's baby would be king, if male, with himself, Craterus, Leonnatus, and Antipater as guardians. However, the infantry, under the command of Meleager, rejected this arrangement since they had been excluded from the discussion. Instead, they supported Alexander's half-brother Philip Arrhidaeus. Eventually, the two sides reconciled, and after the birth of Alexander IV, he and Philip III were appointed joint kings, albeit in name only. Dissension and rivalry soon affected the Macedonians. The satrapies handed out by Perdiccas at the Partition of Babylon became power bases each general used to bid for power. After the assassination of Perdiccas in 321 BC, Macedonian unity collapsed, and 40 years of war between The Successors (Diadochi) ensued before the Hellenistic world settled into three stable power blocs: Ptolemaic Egypt, Seleucid Syria and East, and Antigonid Ma" +654,"for its naturalism, as opposed to a stiffer, more static pose, is thought to be the most faithful depiction. Curtius Rufus, a Roman historian from the first century AD, who wrote the Histories of Alexander the Great, gives this account of Alexander sitting on the throne of Darius III: Both Curtius and Diodorus report a story that when Darius III's mother, Sisygambis, first met Alexander and Hephaestion, she assumed that the latter was Alexander because he was the taller and more handsome of the two. The Greek biographer Plutarch () discusses the accuracy of his depictions: Historians have understood the detail of the pleasant fragrance attributed to Alexander as stemming from a belief in ancient Greece that pleasant scents are characteristic of gods and heroes. The Alexander Mosaic and contemporary coins portray Alexander with a straight nose, a slightly protruding jaw, full lips and eyes deep set beneath a strongly pronounced forehead . He is also described as having a slight upward t" +655,"daughter of Darius III and the latter a daughter of Artaxerxes III, for political reasons. He apparently had two sons, Alexander IV of Macedon by Roxana and, possibly, Heracles of Macedon from his mistress Barsine. He lost another child when Roxana miscarried at Babylon. Alexander also had a close relationship with his friend, general, and bodyguard Hephaestion, the son of a Macedonian noble. Hephaestion's death devastated Alexander. This event may have contributed to Alexander's failing health and detached mental state during his final months. === Sexuality === Alexander's sexuality has been the subject of speculation and controversy in modern times. The Roman era writer Athenaeus says, based on the scholar Dicaearchus, who was Alexander's contemporary, that the king was quite excessively keen on boys , and that Alexander kissed the eunuch Bagoas in public. This episode is also told by Plutarch, probably based on the same source. None of Alexander's contemporaries, however, are known" +656,"in the northwest of the Indian subcontinent directly gave rise to one of the most powerful Indian dynasties in history, the Maurya Empire. Taking advantage of this power vacuum, Chandragupta Maurya (referred to in Greek sources as Sandrokottos ), of relatively humble origin, took control of the Punjab, and with that power base proceeded to conquer the Nanda Empire. ===Founding of cities=== Over the course of his conquests, Alexander founded many cities that bore his name, most of them east of the Tigris. The first, and greatest, was Alexandria in Egypt, which would become one of the leading Mediterranean cities. The cities' locations reflected trade routes as well as defensive positions. At first, the cities must have been inhospitable, little more than defensive garrisons. Following Alexander's death, many Greeks who had settled there tried to return to Greece. However, a century or so after Alexander's death, many of the Alexandrias were thriving, with elaborate public buildings and" +657,"be seen as far as Patna, especially with the Pataliputra capital, dated to the 3rd century BC. The Corinthian order is also heavily represented in the art of Gandhara, especially through Indo-Corinthian capitals. ===Influence on Rome=== Alexander and his exploits were admired by many Romans, especially generals, who wanted to associate themselves with his achievements. Polybius began his Histories by reminding Romans of Alexander's achievements, and thereafter Roman leaders saw him as a role model. Pompey the Great adopted the epithet Magnus and even Alexander's anastole-type haircut, and searched the conquered lands of the east for Alexander's 260-year-old cloak, which he then wore as a sign of greatness. Julius Caesar dedicated a Lysippean equestrian bronze statue, but replaced Alexander's head with his own, while Octavian visited Alexander's tomb in Alexandria and temporarily changed his seal from a sphinx to Alexander's profile. The emperor Trajan also admired Alexander, as did Ner" +658,"medieval European, to modern Greek. Alexander features prominently in modern Greek folklore, more than any other ancient figure. The colloquial form of his name in modern Greek ( O Megalexandros ) is a household name, and he is the only ancient hero to appear in the Karagiozis shadow play. One well-known fable among Greek seamen involves a solitary mermaid who would grasp a ship's prow during a storm and ask the captain, Is King Alexander alive? The answer should be He is alive and well and rules the world! causing the mermaid to vanish and the sea to calm. Any other answer would cause the mermaid to turn into a raging Gorgon who would drag the ship to the bottom of the sea, all hands aboard. In pre-Islamic Middle Persian (Zoroastrian) literature, Alexander is referred to by the epithet gujastak, meaning accursed , and is accused of destroying temples and burning the sacred texts of Zoroastrianism. In Islamic Persia, under the influence of the Alexander Romance (in Iskandarnameh), a mo" +659,"exploration of Asia * Horns of Alexander * List of biblical figures identified in extra-biblical sources * List of people known as The Great * Gates of Alexander * Military tactics of Alexander the Great * Ptolemaic cult of Alexander the Great * Theories about Alexander the Great in the Quran ==References== ===Notes=== ===Citations=== ===Sources=== ====Primary sources==== * * * * * * ====Secondary sources==== * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ==Further reading== * * * * * * * * * * * * * * * * * * , also (1974) New York: E. P. Dutton and (1986) London: Penguin Books. * * * * * * * * * * * * * * * * * == External links == * * Part 1, Part 2, Part 3, Part 4, Part 5, Part 6. * Alexander the Great: An annotated list of primary sources. Livius. Archived from the original on 4 December 2016. Retrieved 26 March 2020. * In Our Time: Alexander the Great BBC discussion with Paul" +660,"Alfred Habdank Skarbek Korzybski (; ; July 3, 1879 – March 1, 1950) was a Polish-American independent scholar who developed a field called general semantics, which he viewed as both distinct from, and more encompassing than, the field of semantics. He argued that human knowledge of the world is limited both by the human nervous system and the languages humans have developed, and thus no one can have direct access to reality, given that the most we can know is that which is filtered through the brain's responses to reality. His best known dictum is The map is not the territory . ==Early life and career== Born in Warsaw, Vistula Country, which was then part of the Russian Empire, Korzybski belonged to an aristocratic Polish family whose members had worked as mathematicians, scientists, and engineers for generations. He learned the Polish language at home and the Russian language in schools, and having a French and German governess, he became fluent in four languages as a child. Korzybski" +661,"Two of them wanted to vomit, put their hands in front of their mouths, and ran out of the lecture hall to the toilet. You see, Korzybski remarked, I have just demonstrated that people don't just eat food, but also words, and that the taste of the former is often outdone by the taste of the latter. R. Diekstra, Haarlemmer Dagblad, 1993, cited by L. Derks & J. Hollander, Essenties van NLP (Utrecht: Servire, 1996), p. 58. William Burroughs went to a Korzybski workshop in the autumn of 1939. He was 25 years old, and paid $40. His fellow students—there were 38 in all—included young Samuel I. Hayakawa (later to become a Republican member of the U.S. Senate) and Wendell Johnson (founder of the Monster Study). == Influence == Korzybski's work received positive reactions from a variety of individuals in the 1940s and 1950s. Robert A. Heinlein named a character after him in his 1940 short story Blowups Happen , and science fiction writer A. E. van Vogt in his novel The World of Null-A , publishe" +662,"Asteroids is a space-themed multidirectional shooter arcade video game designed by Lyle Rains and Ed Logg released in November 1979 by Atari, Inc. The player controls a single spaceship in an asteroid field which is periodically traversed by flying saucers. The object of the game is to shoot and destroy the asteroids and saucers, while not colliding with either, or being hit by the saucers' counter-fire. The game becomes harder as the number of asteroids increases. Asteroids was conceived during a meeting between Logg and Rains, who decided to use hardware developed by Howard Delman previously used for Lunar Lander. Asteroids was based on an unfinished game titled Cosmos; its physics model, control scheme, and gameplay elements were derived from Spacewar!, Computer Space, and Space Invaders and refined through trial and error. The game is rendered on a vector display in a two-dimensional view that wraps around both screen axes. Asteroids was one of the first major hits of the golden ag" +663,"to a monitor. ===Implementation=== Logg modeled the player's ship, the five-button control scheme, and the game physics after Spacewar!, which he had played as a student at the University of California, Berkeley, but made several changes to improve playability. The ship was programmed into the hardware and rendered by the monitor, and it was configured to move with thrust and inertia. The hyperspace button was not placed near Logg's right thumb, which he was dissatisfied with, as he had a problem tak[ing] his hand off the thrust button . Drawings of asteroids in various shapes were incorporated into the game. Logg copied the idea of a high score table with initials from Exidy's Star Fire. The two saucers were formulated to be different from each other. A steadily decreasing timer shortens intervals between saucer attacks to keep the player from not shooting asteroids and saucers. A heartbeat soundtrack quickens as the game progresses. The game does not have a sound chip. Delman created" +664,"games for the Atari 2600 in 2013. It was added to the Museum of Modern Art's collection of video games. In 2021, The Guardian listed Asteroids as the second greatest video game of the 1970s, just below Galaxian (1979). By contrast, in March 1983 the Atari 8-bit port of Asteroids won sixth place in Softlines Dog of the Year awards for badness in computer games , Atari division, based on reader submissions. Usage of the names of Saturday Night Live characters Mr. Bill and Sluggo to refer to the saucers in an Esquire article about the game led to Logg receiving a cease and desist letter from a lawyer with the Mr. Bill Trademark . ==Legacy== ===Arcade sequels=== Released in 1981, Asteroids Deluxe was the first sequel to Asteroids. Dave Shepperd edited the code and made enhancements to the game without Logg's involvement. The onscreen objects are tinted blue, and hyperspace is replaced by a shield that depletes when used. The asteroids rotate, and new killer satellite enemies break into sma" +665,"Asparagales (asparagoid lilies) is an order of plants in modern classification systems such as the Angiosperm Phylogeny Group (APG) and the Angiosperm Phylogeny Web. The order takes its name from the type family Asparagaceae and is placed in the monocots amongst the lilioid monocots. The order has only recently been recognized in classification systems. It was first put forward by Huber in 1977 and later taken up in the Dahlgren system of 1985 and then the APG in 1998, 2003 and 2009. Before this, many of its families were assigned to the old order Liliales, a very large order containing almost all monocots with colorful tepals and lacking starch in their endosperm. DNA sequence analysis indicated that many of the taxa previously included in Liliales should actually be redistributed over three orders, Liliales, Asparagales, and Dioscoreales. The boundaries of the Asparagales and of its families have undergone a series of changes in recent years; future research may lead to further chang" +666,"the order, such as Lomandra and Aphyllanthes, have the same type of secondary growth but confined to their underground stems. * Microsporogenesis (part of pollen formation) distinguishes some members of Asparagales from Liliales. Microsporogenesis involves a cell dividing twice (meiotically) to form four daughter cells. There are two kinds of microsporogenesis: successive and simultaneous (although intermediates exist). In successive microsporogenesis, walls are laid down separating the daughter cells after each division. In simultaneous microsporogenesis, there is no wall formation until all four cell nuclei are present. Liliales all have successive microsporogenesis, which is thought to be the primitive condition in monocots. It seems that when the Asparagales first diverged they developed simultaneous microsporogenesis, which the 'lower' Asparagales families retain. However, the 'core' Asparagales (see Phylogenetics ) have reverted to successive microsporogenesis. * The Asparagales" +667,"(Liliales and Asparagales respectively). The Liliaceae would be reduced to a small 'core' represented by the tribe Tulipae, while large groups such Scilleae and Asparagae would become part of Asparagales either as part of the Amaryllidaceae or as separate families. While of the Amaryllidaceae, the Agaveae would be part of Asparagaceae but the Alstroemeriae would become a family within the Liliales. The number of known genera (and species) continued to grow and by the time of the next major British classification, that of the Bentham & Hooker system in 1883 (published in Latin) several of Lindley's other families had been absorbed into the Liliaceae. They used the term 'series' to indicate suprafamilial rank, with seven series of monocotyledons (including Glumaceae), but did not use Lindley's terms for these. However, they did place the Liliaceous and Amaryllidaceous genera into separate series. The Liliaceae were placed in series Coronariae, while the Amaryllideae were placed in series" +668,"monocots), the remaining clades (lilioid and commelinid monocots) have been referred to as the core monocots . The relationship between the orders (with the exception of the two sister orders) is pectinate, that is diverging in succession from the line that leads to the commelinids. Numbers indicate crown group (most recent common ancestor of the sampled species of the clade of interest) divergence times in mya (million years ago). === Subdivision === A phylogenetic tree for the Asparagales, generally to family level, but including groups which were recently and widely treated as families but which are now reduced to subfamily rank, is shown below. The tree shown above can be divided into a basal paraphyletic group, the 'lower Asparagales (asparagoids)', from Orchidaceae to Asphodelaceae, and a well-supported monophyletic group of 'core Asparagales' (higher asparagoids), comprising the two largest families, Amaryllidaceae sensu lato and Asparagaceae sensu lato. Two differences between" +669,"based on APWeb , is somewhat uncertain. ===Evolution=== Several studies have attempted to date the evolution of the Asparagales, based on phylogenetic evidence. Earlier studies generally give younger dates than more recent studies, which have been preferred in the table below. Approx. date inMillions of Years Ago Event 133-120 Origin of Asparagales, i.e. first divergence from other monocots 93 Split between Asphodelaceae and the 'core group' Asparagales 91–89 Origin of Alliodeae and Asparagoideae 47 Divergence of Agavoideae and Nolinoideae A 2009 study suggests that the Asparagales have the highest diversification rate in the monocots, about the same as the order Poales, although in both orders the rate is little over half that of the eudicot order Lamiales, the clade with the highest rate. === Comparison of family structures === The taxonomic diversity of the monocotyledons is described in detail by Kubitzki. Up-to-date information on the Asparagales can be found on the Angiosperm Phy" +670,"The Alismatales (alismatids) are an order of flowering plants including about 4,500 species. Plants assigned to this order are mostly tropical or aquatic. Some grow in fresh water, some in marine habitats. Perhaps the most important food crop in the order is the taro plant, Colocasia esculenta. ==Description== The Alismatales comprise herbaceous flowering plants of often aquatic and marshy habitats, and the only monocots known to have green embryos other than the Amaryllidaceae. They also include the only marine angiosperms growing completely submerged, the seagrasses. The flowers are usually arranged in inflorescences, and the mature seeds lack endosperm. Both marine and freshwater forms include those with staminate flowers that detach from the parent plant and float to the surface. There they can pollinate carpellate flowers floating on the surface via long pedicels. In others, pollination occurs underwater, where pollen may form elongated strands, increasing chance of success. Most" +671,"The Apiales are an order of flowering plants. The families are those recognized in the APG III system. This is typical of the newer classifications, though there is some slight variation and in particular, the Torriceliaceae may also be divided. Under this definition, well-known members include carrots, celery, parsley, and Hedera helix (English ivy). The order Apiales is placed within the asterid group of eudicots as circumscribed by the APG III system. Within the asterids, Apiales belongs to an unranked group called the campanulids, and within the campanulids, it belongs to a clade known in phylogenetic nomenclature as Apiidae. In 2010, a subclade of Apiidae named Dipsapiidae was defined to consist of the three orders: Apiales, Paracryphiales, and Dipsacales. ==Taxonomy== Under the Cronquist system, only the Apiaceae and Araliaceae were included here, and the restricted order was placed among the rosids rather than the asterids. The Pittosporaceae were placed within the Rosales, and" +672,"Asterales () is an order of dicotyledonous flowering plants that includes the large family Asteraceae (or Compositae) known for composite flowers made of florets, and ten families related to the Asteraceae. While asterids in general are characterized by fused petals, composite flowers consisting of many florets create the false appearance of separate petals (as found in the rosids). The order is cosmopolitan (plants found throughout most of the world including desert and frigid zones), and includes mostly herbaceous species, although a small number of trees (such as the Lobelia deckenii, the giant lobelia, and Dendrosenecio, giant groundsels) and shrubs are also present. Asterales are organisms that seem to have evolved from one common ancestor. Asterales share characteristics on morphological and biochemical levels. Synapomorphies (a character that is shared by two or more groups through evolutionary development) include the presence in the plants of oligosaccharide inulin, a nutrient" +673,"An asteroid is a minor planet—an object that is neither a true planet nor an identified comet— that orbits within the inner Solar System. They are rocky, metallic, or icy bodies with no atmosphere, classified as C-type (carbonaceous), M-type (metallic), or S-type (silicaceous). The size and shape of asteroids vary significantly, ranging from small rubble piles under a kilometer across and larger than meteoroids, to Ceres, a dwarf planet almost 1000 km in diameter. A body is classified as a comet, not an asteroid, if it shows a coma (tail) when warmed by solar radiation, although recent observations suggest a continuum between these types of bodies. Of the roughly one million known asteroids, the greatest number are located between the orbits of Mars and Jupiter, approximately 2 to 4 AU from the Sun, in a region known as the main asteroid belt. The total mass of all the asteroids combined is only 3% that of Earth's Moon. The majority of main belt asteroids follow slightly elliptical, st" +674,"largest object in the asteroid belt has been placed in this category: Ceres, at about across. == History of observations == Despite their large numbers, asteroids are a relatively recent discovery, with the first one—Ceres—only being identified in 1801. Only one asteroid, 4 Vesta, which has a relatively reflective surface, is normally visible to the naked eye in dark skies when it is favorably positioned. Rarely, small asteroids passing close to Earth may be briefly visible to the naked eye. , the Minor Planet Center had data on 1,199,224 minor planets in the inner and outer Solar System, of which about 614,690 had enough information to be given numbered designations. ===Discovery of Ceres=== In 1772, German astronomer Johann Elert Bode, citing Johann Daniel Titius, published a numerical procession known as the Titius–Bode law (now discredited). Except for an unexplained gap between Mars and Jupiter, Bode's formula seemed to predict the orbits of the known planets. He wrote the followi" +675,"Max Wolf pioneered the use of astrophotography to detect asteroids, which appeared as short streaks on long-exposure photographic plates. This dramatically increased the rate of detection compared with earlier visual methods: Wolf alone discovered 248 asteroids, beginning with 323 Brucia, whereas only slightly more than 300 had been discovered up to that point. It was known that there were many more, but most astronomers did not bother with them, some calling them vermin of the skies , a phrase variously attributed to Eduard Suess and Edmund Weiss. Even a century later, only a few thousand asteroids were identified, numbered and named. === 19th and 20th centuries === In the past, asteroids were discovered by a four-step process. First, a region of the sky was photographed by a wide-field telescope or astrograph. Pairs of photographs were taken, typically one hour apart. Multiple pairs could be taken over a series of days. Second, the two films or plates of the same region were viewed u" +676,"asteroids orbit within the asteroid belt between the orbits of Mars and Jupiter, generally in relatively low-eccentricity (i.e. not very elongated) orbits. This belt is estimated to contain between 1.1 and 1.9 million asteroids larger than in diameter, and millions of smaller ones. These asteroids may be remnants of the protoplanetary disk, and in this region the accretion of planetesimals into planets during the formative period of the Solar System was prevented by large gravitational perturbations by Jupiter. Contrary to popular imagery, the asteroid belt is mostly empty. The asteroids are spread over such a large volume that reaching an asteroid without aiming carefully would be improbable. Nonetheless, hundreds of thousands of asteroids are currently known, and the total number ranges in the millions or more, depending on the lower size cutoff. Over 200 asteroids are known to be larger than 100 km, and a survey in the infrared wavelengths has shown that the asteroid belt has betwee" +677,"the 25th Annual Lunar and Planetary Science Conference, held in Houston, TX, 14–18 March 1994, p. 293 The high porosity of the interior of Phobos (based on the density of 1.88 g/cm3, voids are estimated to comprise 25 to 35 percent of Phobos's volume) is inconsistent with an asteroidal origin. Observations of Phobos in the thermal infrared suggest a composition containing mainly phyllosilicates, which are well known from the surface of Mars. The spectra are distinct from those of all classes of chondrite meteorites, again pointing away from an asteroidal origin. Both sets of findings support an origin of Phobos from material ejected by an impact on Mars that reaccreted in Martian orbit, similar to the prevailing theory for the origin of Earth's moon. == Characteristics == === Size distribution === Asteroids vary greatly in size, from almost for the largest down to rocks just 1 meter across, below which an object is classified as a meteoroid. The three largest are very much like miniatu" +678,"This suggests that most asteroids with a diameter over 100 meters are rubble piles formed through the accumulation of debris after collisions between asteroids. === Color === Asteroids become darker and redder with age due to space weathering. However evidence suggests most of the color change occurs rapidly, in the first hundred thousand years, limiting the usefulness of spectral measurement for determining the age of asteroids. === Surface features === Except for the big four (Ceres, Pallas, Vesta, and Hygiea), asteroids are likely to be broadly similar in appearance, if irregular in shape. 253 Mathilde is a rubble pile saturated with craters with diameters the size of the asteroid's radius. Earth-based observations of 511 Davida, one of the largest asteroids after the big four, reveal a similarly angular profile, suggesting it is also saturated with radius-size craters. Medium-sized asteroids such as Mathilde and 243 Ida, that have been observed up close, also reveal a deep regolith" +679,"may have even brought life itself to Earth (an event called panspermia ). In August 2011, a report, based on NASA studies with meteorites found on Earth, was published suggesting DNA and RNA components (adenine, guanine and related organic molecules) may have been formed on asteroids and comets in outer space. In November 2019, scientists reported detecting, for the first time, sugar molecules, including ribose, in meteorites, suggesting that chemical processes on asteroids can produce some fundamentally essential bio-ingredients important to life, and supporting the notion of an RNA world prior to a DNA-based origin of life on Earth, and possibly, as well, the notion of panspermia. == Classification == Asteroids are commonly categorized according to two criteria: the characteristics of their orbits, and features of their reflectance spectrum. === Orbital classification === Many asteroids have been placed in groups and families based on their orbital characteristics. Apart from the bro" +680,"information about the shapes and compositions of asteroids can be inferred from their light curves (variation in brightness during rotation) and their spectral properties. Sizes can be estimated by timing the lengths of star occultations (when an asteroid passes directly in front of a star). Radar imaging can yield good information about asteroid shapes and orbital and rotational parameters, especially for near-Earth asteroids. Spacecraft flybys can provide much more data than any ground or space-based observations; sample-return missions gives insights about regolith composition. === Ground-based observations === As asteroids are rather small and faint objects, the data that can be obtained from ground-based observations (GBO) are limited. By means of ground-based optical telescopes the visual magnitude can be obtained; when converted into the absolute magnitude it gives a rough estimate of the asteroid's size. Light-curve measurements can also be made by GBO; when collected over a lo" +681,"now on an extended mission and expected to arrive at a new target in 2031. NASA launched the OSIRIS-REx in 2016, a sample return mission to asteroid 101955 Bennu. In 2021, the probe departed the asteroid with a sample from its surface. Sample was delivered to Earth in September 2023. The spacecraft continues its extended mission, designated OSIRIS-APEX, to explore near-Earth asteroid Apophis in 2029. In 2021, NASA launched Double Asteroid Redirection Test (DART), a mission to test technology for defending Earth against potential hazardous objects. DART deliberately crashed into the minor-planet moon Dimorphos of the double asteroid Didymos in September 2022 to assess the potential of a spacecraft impact to deflect an asteroid from a collision course with Earth. In October, NASA declared DART a success, confirming it had shortened Dimorphos' orbital period around Didymos by about 32 minutes. NASA's Lucy, launched in 2021, is a multiple-asteroid flyby probe focused on flying by 7 Jupiter" +682,"in space at the same time, or more correctly when some point on Earth's surface intersects the impactor's orbit when the impactor arrives. Since the Earth is approximately 12,750 km in diameter and moves at approx. 30 km per second in its orbit, it travels a distance of one planetary diameter in about 425 seconds, or slightly over seven minutes. Delaying, or advancing the impactor's arrival by times of this magnitude can, depending on the exact geometry of the impact, cause it to miss the Earth. Project Icarus was one of the first projects designed in 1967 as a contingency plan in case of collision with 1566 Icarus. The plan relied on the new Saturn V rocket, which did not make its first flight until after the report had been completed. Six Saturn V rockets would be used, each launched at variable intervals from months to hours away from impact. Each rocket was to be fitted with a single 100-megaton nuclear warhead as well as a modified Apollo Service Module and uncrewed Apollo Command" +683,"An allocution, or allocutus, is a formal statement made to a court by the defendant who has been found guilty before being sentenced. It is part of the criminal procedure in some jurisdictions using common law. == Concept == An allocution allows the defendant to explain why the sentence should be lenient. In plea bargains, an allocution may be required of the defendant. The defendant explicitly admits specifically and in detail the actions and their reasons in exchange for a reduced sentence. In principle, that removes any doubt as to the exact nature of the defendant's guilt in the matter. The term allocution is used generally only in jurisdictions in the United States, but there are vaguely similar processes in other common law countries. In many other jurisdictions, it is for the defense lawyer to mitigate on their client's behalf, and the defendant rarely has the opportunity to speak. ==Australia== In Australia, the term allocutus is used by the Clerk of Arraigns or another formal" +684,"An ( ; Medieval Latin for he has declared under oath ) is a written statement voluntarily made by an affiant or deponent under an oath or affirmation which is administered by a person who is authorized to do so by law. Such a statement is witnessed as to the authenticity of the affiant's signature by a taker of oaths, such as a notary public or commissioner of oaths. An affidavit is a type of verified statement or showing, or in other words, it contains a verification, which means that it is made under oath on penalty of perjury, and this serves as evidence for its veracity and is required in court proceedings. ==Definition== An affidavit is typically defined as a written declaration or statement that is sworn or affirmed before a person who has authority to administer an oath. There is no general defined form for an affidavit, although for some proceedings an affidavit must satisfy legal or statutory requirements in order to be considered. An affidavit may include, * a commencement wh" +685,"be accepted by the court unless accompanied by an independent sworn statement or other evidence in support of the need for the motion. In such a case, a court will accept an affidavit from the filing attorney in support of the motion, as certain assumptions are made, to wit: The affidavit in place of sworn testimony promotes judicial economy. The lawyer is an officer of the court and knows that a false swearing by them, if found out, could be grounds for severe penalty up to and including disbarment. The lawyer if called upon would be able to present independent and more detailed evidence to prove the facts set forth in his affidavit. Affidavits should not be confused with unsworn declarations under penalty of perjury. In federal courts and about 20 states as of 2006, unsworn declarations under penalty of perjury are authorized by statute as acceptable in lieu of affidavits. The key differences are that an unsworn declaration does not bear the jurat of a notary public and the declarant" +686,"Aries is one of the constellations of the zodiac. It is located in the Northern celestial hemisphere between Pisces to the west and Taurus to the east. The name Aries is Latin for ram. Its old astronomical symbol is (♈︎). It is one of the 48 constellations described by the 2nd century astronomer Ptolemy, and remains one of the 88 modern constellations. It is a mid-sized constellation ranking 39th in overall size, with an area of 441 square degrees (1.1% of the celestial sphere). Aries has represented a ram since late Babylonian times. Before that, the stars of Aries formed a farmhand. Different cultures have incorporated the stars of Aries into different constellations including twin inspectors in China and a porpoise in the Marshall Islands. Aries is a relatively dim constellation, possessing only four bright stars: Hamal (Alpha Arietis, second magnitude), Sheratan (Beta Arietis, third magnitude), Mesarthim (Gamma Arietis, fourth magnitude), and 41 Arietis (also fourth magnitude). The" +687,"behind itself. The obsolete constellations Apes, Vespa, Lilium, and Musca Borealis all centred on the same four stars, now known as 33, 35, 39, and 41 Arietis. In 1612, Petrus Plancius introduced Apes, a constellation representing a bee. In 1624, the same stars were used by Jakob Bartsch for Vespa, representing a wasp. In 1679, Augustin Royer used these stars for his constellation Lilium, representing the fleur-de-lis. None of these constellations became widely accepted. Johann Hevelius renamed the constellation Musca in 1690 in his Firmamentum Sobiescianum. To differentiate it from Musca, the southern fly, it was later renamed Musca Borealis but it did not gain acceptance and its stars were ultimately officially reabsorbed into Aries. In 1922, the International Astronomical Union defined its recommended three-letter abbreviation, Ari . The official boundaries of Aries were defined in 1930 by Eugène Delporte as a polygon of 12 segments. Its right ascension is between 1h 46.4m and 3h 29" +688,"a double star by Robert Hooke in 1664, one of the earliest such telescopic discoveries. The primary, γ1 Arietis, is an Alpha² Canum Venaticorum variable star that has a range of 0.02 magnitudes and a period of 2.607 days. It is unusual because of its strong silicon emission lines. The constellation is home to several double stars, including Epsilon, Lambda, and Pi Arietis. ε Arietis is a binary star with two white components. The primary is of magnitude 5.2 and the secondary is of magnitude 5.5. The system is 290 light-years from Earth. Its overall magnitude is 4.63, and the primary has an absolute magnitude of 1.4. Its spectral class is A2. The two components are separated by 1.5 arcseconds. λ Arietis is a wide double star with a white-hued primary and a yellow-hued secondary. The primary is of magnitude 4.8 and the secondary is of magnitude 7.3. The primary is 129 light-years from Earth. It has an absolute magnitude of 1.7 and a spectral class of F0. The two components are separated" +689,"130 million light-years. NGC 678 is an edge-on spiral galaxy that is 4.5 by 0.8 arcminutes. NGC 680, an elliptical galaxy with an asymmetrical boundary, is the brighter of the two at magnitude 12.9; NGC 678 has a magnitude of 13.35. Both galaxies have bright cores, but NGC 678 is the larger galaxy at a diameter of 171,000 light-years; NGC 680 has a diameter of 72,000 light-years. NGC 678 is further distinguished by its prominent dust lane. NGC 691 itself is a spiral galaxy slightly inclined to our line of sight. It has multiple spiral arms and a bright core. Because it is so diffuse, it has a low surface brightness. It has a diameter of 126,000 light-years and is 124 million light-years away. NGC 877 is the brightest member of an 8-galaxy group that also includes NGC 870, NGC 871, and NGC 876, with a magnitude of 12.53. It is 2.4 by 1.8 arcminutes and is 178 million light-years away with a diameter of 124,000 light-years. Its companion is NGC 876, which is about 103,000 light-years fro" +690,"Aquarius is an equatorial constellation of the zodiac, between Capricornus and Pisces. Its name is Latin for water-carrier or cup-carrier , and its old astronomical symbol is (♒︎), a representation of water. Aquarius is one of the oldest of the recognized constellations along the zodiac (the Sun's apparent path). It was one of the 48 constellations listed by the 2nd century astronomer Ptolemy, and it remains one of the 88 modern constellations. It is found in a region often called the Sea due to its profusion of constellations with watery associations such as Cetus the whale, Pisces the fish, and Eridanus the river. At apparent magnitude 2.9, Beta Aquarii is the brightest star in the constellation. ==History and mythology== Aquarius is identified as The Great One in the Babylonian star catalogues and represents the god Ea himself, who is commonly depicted holding an overflowing vase. The Babylonian star-figure appears on entitlement stones and cylinder seals from the second millennium." +691,"Earth. α Aquarii, also known as Sadalmelik, has apparent It is distant from Earth, and is around 6.5 times as massive as the Sun, and 3000 times as luminous. It is 53 million years old. γ Aquarii, also called Sadachbia, is a white main sequence star of spectral type star of spectral type A0V that is between 158 and 315 million years old and is around 2.5 times the Sun's mass (), and double its radius. Its magnitude is 3.85, and it is away, hence its luminosity is . The name Sadachbia comes from the Arabic for lucky stars of the tents , sa'd al-akhbiya. δ Aquarii, also known as Skat or Scheat is a blue-white spectral type A2 star with apparent magnitude 3.27 and luminosity . ε Aquarii, also known as Albali, is a blue-white spectral type A1 star with apparent magnitude 3.77, absolute magnitude 1.2, and a luminosity of . ζ Aquarii is a spectral type F2 double star; both stars are white. In combination, they appear to be magnitude 3.6 with luminosity . The primary has magnitude 4.53 and th" +692,"may have liquid water on their surfaces. The discovery of the TRAPPIST-1 system is seen by astronomers as a significant step toward finding life beyond Earth. ===Deep sky objects=== Because of its position away from the galactic plane, the majority of deep-sky objects in Aquarius are galaxies, globular clusters, and planetary nebulae. Aquarius contains three deep sky objects that are in the Messier catalog: the globular clusters Messier 2, Messier 72, and the asterism Messier 73. While M73 was originally catalogued as a sparsely populated open cluster, modern analysis indicates the 6 main stars are not close enough together to fit this definition, reclassifying M73 as an asterism. Two well-known planetary nebulae are also located in Aquarius: the Saturn Nebula (NGC 7009), to the southeast of μ Aquarii; and the famous Helix Nebula (NGC 7293), southwest of δ Aquarii. M2, also catalogued as NGC 7089, is a rich globular cluster located approximately 37,000 light-years from Earth. At magnit" +693,"is hand-drawn and computer-generated animation originating from Japan. Outside Japan and in English, anime refers specifically to animation produced in Japan. However, in Japan and Japanese, (a term derived from a shortening of the English word animation) describes all animated works, regardless of style or origin. Many works of animation with a similar style to Japanese animation are also produced outside Japan. Video games sometimes also feature themes and art styles that are sometimes labelled as anime. The earliest commercial Japanese animation dates to 1917. A characteristic art style emerged in the 1960s with the works of cartoonist Osamu Tezuka and spread in following decades, developing a large domestic audience. Anime is distributed theatrically, through television broadcasts, directly to home media, and over the Internet. In addition to original works, anime are often adaptations of Japanese comics (manga), light novels, or video games. It is classified into numerous genres t" +694,"allow him to produce material on a tight schedule with inexperienced staff, many of his limited animation practices came to define the medium's style. Three Tales (1960) was the first anime film broadcast on television; the first anime television series was Instant History (1961–64). An early and influential success was Astro Boy (1963–66), a television series directed by Tezuka based on his manga of the same name. Many animators at Tezuka's Mushi Production later established major anime studios (including Madhouse, Sunrise, and Pierrot). The 1970s saw growth in the popularity of manga, many of which were later animated. Tezuka's work—and that of other pioneers in the field—inspired characteristics and genres that remain fundamental elements of anime today. The giant robot genre (also known as mecha ), for instance, took shape under Tezuka, developed into the super robot genre under Go Nagai and others, and was revolutionized at the end of the decade by Yoshiyuki Tomino, who developed" +695,"before the voice acting, contrary to American animation which does the voice acting first. === Characters === The body proportions of human anime characters tend to accurately reflect the proportions of the human body in reality. The height of the head is considered by the artist as the base unit of proportion. Head to height ratios vary drastically by art style, with most anime characters falling between 5 and 8 heads tall. Anime artists occasionally make deliberate modifications to body proportions to produce super deformed characters that feature a disproportionately small body compared to the head; many super deformed characters are two to four heads tall. Some anime works like Crayon Shin-chan completely disregard these proportions, in such a way that they resemble caricatured Western cartoons. A common anime character design convention is exaggerated eye size. The animation of characters with large eyes in anime can be traced back to Osamu Tezuka, who was deeply influenced by suc" +696,"Yggdrasil in Ah! My Goddess. Genre crossing in anime is also prevalent, such as the blend of fantasy and comedy in Dragon Half, and the incorporation of slapstick humor in the crime anime film Castle of Cagliostro. Other subgenres found in anime include magical girl, harem, sports, martial arts, literary adaptations, medievalism, and war. === Formats === Early anime works were made for theatrical viewing, and required played musical components before sound and vocal components were added to the production. In 1958, Nippon Television aired Mogura no Abanchūru ( Mole's Adventure ), both the first televised and first color anime to debut. It was not until the 1960s when the first televised series were broadcast and it has remained a popular medium since. Works released in a direct-to-video format are called original video animation (OVA) or original animation video (OAV); and are typically not released theatrically or televised prior to home media release. The emergence of the Internet ha" +697,"top ten anime titles having previously aired on Cartoon Network. As a part of localization, some editing of cultural references may occur to better follow the references of the non-Japanese culture. The cost of English localization averages US$10,000 per episode. The industry has been subject to both praise and condemnation for fansubs, the addition of unlicensed and unauthorized subtitled translations of anime series or films. Fansubs, which were originally distributed on VHS bootlegged cassettes in the 1980s, have been freely available and disseminated online since the 1990s. Since this practice raises concerns for copyright and piracy issues, fansubbers tend to adhere to an unwritten moral code to destroy or no longer distribute an anime once an official translated or subtitled version becomes licensed. They also try to encourage viewers to buy an official copy of the release once it comes out in English, although fansubs typically continue to circulate through file-sharing networks" +698,"popularizing anime in the Western world during the early 1990s, before anime was further popularized by television shows such as Pokémon and Dragon Ball Z in the late 1990s. By 1997, Japanese anime was the fastest-growing genre in the American video industry. The growth of the Internet later provided international audiences with an easy way to access Japanese content. Early on, online piracy played a major role in this, through over time many legal alternatives appeared which significantly reduced illegal practices. Since the 2010s streaming services have become increasingly involved in the production, licensing and distribution of anime for the international markets. This is especially the case with net services such as Netflix and Crunchyroll which have large catalogs in Western countries, although until 2020 anime fans in multiple developing countries, such as India and the Philippines, had fewer options for obtaining access to legal content, and therefore would still turn to online" +699,"to anime and manga and include elements like cosplay contests and industry talk panels. Cosplay, a portmanteau of costume play , is not unique to anime and has become popular in contests and masquerades at anime conventions. Japanese culture and words have entered English usage through the popularity of the medium, including otaku, an unflattering Japanese term commonly used in English to denote an obsessive fan of anime and/or manga. Another word that has arisen describing obsessive fans in the United States is wapanese meaning 'white individuals who want to be Japanese, or later known as weeaboo or weeb, individuals who demonstrate an obsession with Japanese anime subculture, a term that originated from abusive content posted on the website 4chan.org. While originally derogatory, the terms Otaku and Weeb have been reappropriated by the anime fandom overtime and today are used by some fans to refer to themselves in a comedic and more positive way. Anime enthusiasts have produced fan f" +700,"any American-made anime has been marketed to Japan. It definitely usually works the other way around, and we're really pleased about that. === Media franchises === In Japanese culture and entertainment, media mix is a strategy to disperse content across multiple representations: different broadcast media, gaming technologies, cell phones, toys, amusement parks, and other methods.Henry Jenkins, Convergence Culture: Where Old and New Media Collide, p. 110 It is the Japanese term for a transmedia franchise.Marc Steinberg, Anime's Media Mix: Franchising Toys and Characters in Japan The term gained its circulation in late 1980s, but the origins of the strategy can be traced back to the 1960s with the proliferation of anime, with its interconnection of media and commodity goods.Steinberg, p. vi A number of anime and manga media franchises such as Demon Slayer: Kimetsu no Yaiba, Dragon Ball and Gundam have gained considerable global popularity, and are among the world's highest-grossing media" +701,"Ankara ( , ; ), historically known as Ancyra and Angora, is the capital of Turkey. Located in the central part of Anatolia, the city has a population of 5.1 million in its urban center and 5.8 million in Ankara Province, making it Turkey's second-largest city after Istanbul, but the first by urban area (4,130 km2). Serving as the capital of the ancient Celtic state of Galatia (280–64 BC), and later of the Roman province with the same name (25 BC–7th century), Ankara has various Hattian, Hittite, Lydian, Phrygian, Galatian, Greek, Persian, Roman, Byzantine, and Ottoman archeological sites. The Ottomans made the city the capital first of the Anatolia Eyalet (1393 – late 15th century) and then the Angora Eyalet (1827–1864) and the Angora Vilayet (1867–1922). The historical center of Ankara is a rocky hill rising over the left bank of the Ankara River, a tributary of the Sakarya River. The hill remains crowned by the ruins of Ankara Castle. Although few of its outworks have survived, there" +702,"a polis and made it the capital city of the Roman province of Galatia. Ankara is famous for the Monumentum Ancyranum (Temple of Augustus and Rome) which contains the official record of the Acts of Augustus, known as the Res Gestae Divi Augusti, an inscription cut in marble on the walls of this temple. The ruins of Ancyra still furnish today valuable bas-reliefs, inscriptions and other architectural fragments. Two other Galatian tribal centers, Tavium near Yozgat, and Pessinus (Balhisar) to the west, near Sivrihisar, continued to be reasonably important settlements in the Roman period, but it was Ancyra that grew into a grand metropolis. An estimated 200,000 people lived in Ancyra in good times during the Roman Empire, a far greater number than was to be the case from after the fall of the Roman Empire until the early 20th century. The small Ankara River ran through the center of the Roman town. It has now been covered and diverted, but it formed the northern boundary of the old town du" +703,"(r. 802–811) strengthened its fortifications, a fact which probably saved it from sack during the large-scale invasion of Anatolia by Caliph Harun al-Rashid in the next year. Arab sources report that Harun and his successor al-Ma'mun (r. 813–833) took the city, but this information is later invention. In 838, however, during the Amorium campaign, the armies of Caliph al-Mu'tasim (r. 833–842) converged and met at the city; abandoned by its inhabitants, Ancara was razed to the ground, before the Arab armies went on to besiege and destroy Amorium reaching as far as Smyrna. In 859, Emperor Michael III (r. 842–867) came to the city during a campaign against the Arabs, and ordered its fortifications restored. In 872, the city was menaced, but not taken, by the Paulicians under Chrysocheir. The last Arab raid to reach the city was undertaken in 931, by the Abbasid governor of Tarsus, Thamal al-Dulafi, but the city again was not captured. === Ecclesiastical history === Early Christian martyrs" +704,"Seljuk decline, a semi-religious cast of craftsmen and trade people named Ahiler chose Angora as their independent city-state in 1290. Orhan, the second Bey of the Ottoman Empire, captured the city in 1356. Timur defeated Bayezid I at the Battle of Ankara in 1402 and took the city, but in 1403 Angora was again under Ottoman control. The Levant Company maintained a factory in the town from 1639 to 1768. In the 19th century, its population was estimated at 20,000 to 60,000. It was sacked by Egyptians under Ibrahim Pasha in 1832. From 1867 to 1922, the city served as the capital of the Angora Vilayet, which included most of ancient Galatia. Prior to World War I, the town had a British consulate and a population of around 28,000, roughly of whom were Christian. === Turkish republican capital === Following the Ottoman defeat in World War I, the Ottoman capital Constantinople (modern Istanbul) and much of Anatolia was occupied by the Allies, who planned to share these lands between Armenia," +705,"Province also has the highest percentage of tertiary education graduates in Turkey with 29.08% of the population having either an undergraduate, master's or doctor's degree. == Economy and infrastructure == Ankara has long been a productive agricultural region in Anatolia. In the Ottoman period, Ankara was well known for producing grain, cotton, and fruits. The city has exported mohair (from the Angora goat) and Angora wool (from the Angora rabbit) internationally for centuries. In the 19th century, the city also exported substantial amounts of goat and cat skins, gum, wax, honey, berries, and madder root. It was connected to Istanbul by railway before the First World War, continuing to export mohair, wool, berries, and grain. The Central Anatolia Region is one of the primary locations of grape and wine production in Turkey, and Ankara is particularly famous for its Kalecik Karası and Muscat grapes; and its Kavaklıdere wine, which is produced in the Kavaklıdere neighborhood within the" +706,"amid allegations of systematic electoral fraud. With the Supreme Electoral Council and courts rejecting his appeals, Yavaş declared his intention to take the irregularities to the European Court of Human Rights. Although Gökçek was inaugurated for a fifth term, most election observers believe that Yavaş was the winner of the election. Gökçek resigned on 28 October 2017 and was replaced by the former mayor of Sincan district, Mustafa Tuna; who was succeeded by Mansur Yavaş of the CHP, the current mayor of Ankara, elected in 2019. == Main sights == ===Ancient/archeological sites=== ====Ankara Citadel==== The foundations of the Ankara castle and citadel were laid by the Galatians on a prominent lava outcrop (), and the rest was completed by the Romans. The Byzantines and Seljuks further made restorations and additions. The area around and inside the citadel, being the oldest part of Ankara, contains many fine examples of traditional architecture. There are also recreational areas to relax" +707,"Krippel in 1925 and was erected in 1927 at Ulus Square. The monument is made of marble and bronze and features an equestrian statue of Mustafa Kemal Atatürk, who wears a Republic era modern military uniform, with the rank Field Marshal.Ministry of Culture page . ====Statue of Atatürk==== Located at Zafer(Victory) Square (Turkish: Zafer Meydanı), the marble and bronze statue was crafted by the renowned Italian sculptor Pietro Canonica in 1927 and depicts a standing Atatürk who wears a Republic era modern military uniform, with the rank Field Marshal. ====Monument to a Secure, Confident Future==== This monument, located in Güvenpark near Kızılay Square, was erected in 1935 and bears Atatürk's advice to his people: Turk! Be proud, work hard, and believe in yourself. (There is debate on whether or not Atatürk actually said Use your mind (Turkish: öğün) instead of Be proud (Turkish: övün)) The monument was depicted on the reverse of the Turkish 5 lira banknote of 1937–1952Central Bank of th" +708,"==== The Turkish State Theatres also has its head office in Ankara and runs the following stages in the city: *125. Yıl Çayyolu Sahnesi *Büyük Tiyatro, *Küçük Tiyatro, *Şinasi Sahnesi, *Akün Sahnesi, *Altındağ Tiyatrosu, *İrfan Şahinbaş Atölye Sahnesi, *Oda Tiyatrosu, *Mahir Canova Sahnesi, *Muhsin Ertuğrul Sahnesi. In addition, the city is served by several private theater companies, among which Ankara Sanat Tiyatrosu, who have their own stage in the city center, is a notable example. === Museums === There are about 50 museums in the city. ====Museum of Anatolian Civilizations==== The Museum of Anatolian Civilizations (Anadolu Medeniyetleri Müzesi) is situated at the entrance of the Ankara Castle. It is an old 15th century bedesten (covered bazaar) that has been restored and now houses a collection of Paleolithic, Neolithic, Hatti, Hittite, Phrygian, Urartian and Roman works as well as a major section dedicated to Lydian treasures. ==== Anıtkabir ==== Anıtkabir is located on an imposi" +709,"parks and open spaces mainly established in the early years of the Republic and well maintained and expanded thereafter. The most important of these parks are: Gençlik Parkı (houses an amusement park with a large pond for rowing), the Botanical garden, Seğmenler Park, Anayasa Park, Kuğulu Park (famous for the swans received as a gift from the Chinese government), Abdi İpekçi Park, Esertepe Parkı, Güven Park (see above for the monument), Kurtuluş Park (has an ice-skating rink), Altınpark (also a prominent exposition/fair area), Harikalar Diyarı (claimed to be Biggest Park of Europe inside city borders) and Göksu Park. Dikmen Vadisi (Dikmen Valley) is a park and recreation area situated in Çankaya district. Gençlik Park was depicted on the reverse of the Turkish 100 lira banknotes of 1952–1976.Central Bank of the Republic of Turkey . Banknote Museum: 5. Emission Group – One Hundred Turkish Lira – I. Series , II. Series , III. Series , IV. Series , V. Series & VI. Series Atatürk Forest Fa" +710,"handled carefully. Grooming is necessary to prevent the fiber from matting and felting on the rabbit. A condition called wool block is common in Angora rabbits and should be treated quickly. Sometimes they are shorn in the summer as the long fur can cause the rabbits to overheat. ==International relations== ===Twin towns and sister cities=== Ankara is twinned with: * Seoul, South Korea (since 1971) * Islamabad, Pakistan (since 1982) * Kuala Lumpur, Malaysia (since 1984) * Beijing, China (since 1990) * Amman, Jordan (since 1992) * Bishkek, Kyrgyzstan (since 1992) * Budapest, Hungary (since 1992) * Khartoum, Sudan (since 1992) * Moscow, Russia (since 1992) * Sofia, Bulgaria (since 1992) * Havana, Cuba (since 1993) * Kyiv, Ukraine (since 1993) * Ashgabat, Turkmenistan (since 1994) * Kuwait City, Kuwait (since 1994) * Sarajevo, Bosnia and Herzegovina (since 1994) * Tirana, Albania (since 1995) * Tbilisi, Georgia (since 1996) * Ufa, Bashkortostan, Russia (since 1997) * Alanya, Turkey * Buch" +711,"Arabic (, or , or ) is a Central Semitic language of the Afroasiatic language family spoken primarily in the Arab world. The ISO assigns language codes to 32 varieties of Arabic, including its standard form of Literary Arabic, known as Modern Standard Arabic, which is derived from Classical Arabic. This distinction exists primarily among Western linguists; Arabic speakers themselves generally do not distinguish between Modern Standard Arabic and Classical Arabic, but rather refer to both as ( the eloquent Arabic ) or simply (). Arabic is the third most widespread official language after English and French, one of six official languages of the United Nations, and the liturgical language of Islam. Arabic is widely taught in schools and universities around the world and is used to varying degrees in workplaces, governments and the media. During the Middle Ages, Arabic was a major vehicle of culture and learning, especially in science, mathematics and philosophy. As a result, many European" +712,"a single 1st century CE inscription in Sabaic script at , in southern present-day Saudi Arabia. However, this inscription does not participate in several of the key innovations of the Arabic language group, such as the conversion of Semitic mimation to nunation in the singular. It is best reassessed as a separate language on the Central Semitic dialect continuum. It was also thought that Old Arabic coexisted alongside—and then gradually displaced—epigraphic Ancient North Arabian (ANA), which was theorized to have been the regional tongue for many centuries. ANA, despite its name, was considered a very distinct language, and mutually unintelligible, from Arabic . Scholars named its variant dialects after the towns where the inscriptions were discovered (Dadanitic, Taymanitic, Hismaic, Safaitic). However, most arguments for a single ANA language or language family were based on the shape of the definite article, a prefixed h-. It has been argued that the h- is an archaism and not a share" +713,"and phonological simplicity of vernacular Arabic compared to Classical and MSA. In around the 11th and 12th centuries in al-Andalus, the zajal and muwashah poetry forms developed in the dialectical Arabic of Cordoba and the Maghreb. === Nahda === The Nahda was a cultural and especially literary renaissance of the 19th century in which writers sought to fuse Arabic and European forms of expression. According to James L. Gelvin, Nahda writers attempted to simplify the Arabic language and script so that it might be accessible to a wider audience. In the wake of the industrial revolution and European hegemony and colonialism, pioneering Arabic presses, such as the Amiri Press established by Muhammad Ali (1819), dramatically changed the diffusion and consumption of Arabic literature and publications. Rifa'a al-Tahtawi proposed the establishment of Madrasat al-Alsun in 1836 and led a translation campaign that highlighted the need for a lexical injection in Arabic, to suit concepts of the ind" +714,"Xth form, or 'university', based on 'to gather, unite'; 'republic', based on 'multitude'). An earlier tendency was to redefine an older word although this has fallen into disuse (e.g., 'telephone' < 'invisible caller (in Sufism)'; 'newspaper' < 'palm-leaf stalk'). Colloquial or dialectal Arabic refers to the many national or regional varieties which constitute the everyday spoken language. Colloquial Arabic has many regional variants; geographically distant varieties usually differ enough to be mutually unintelligible, and some linguists consider them distinct languages. Arabic Language. Microsoft Encarta Online Encyclopedia 2009. However, research indicates a high degree of mutual intelligibility between closely related Arabic variants for native speakers listening to words, sentences, and texts; and between more distantly related dialects in interactional situations.Trentman, E. and Shiri, S., 2020. The Mutual Intelligibility of Arabic Dialects. Critical Multilingualism Studies, 8(1)" +715,"the Middle East and North Africa have become a badge of sophistication and modernity and ... feigning, or asserting, weakness or lack of facility in Arabic is sometimes paraded as a sign of status, class, and perversely, even education through a mélange of code-switching practises. Suleiman, p. 93 === As a foreign language === Arabic has been taught worldwide in many elementary and secondary schools, especially Muslim schools. Universities around the world have classes that teach Arabic as part of their foreign languages, Middle Eastern studies, and religious studies courses. Arabic language schools exist to assist students to learn Arabic outside the academic world. There are many Arabic language schools in the Arab world and other Muslim countries. Because the Quran is written in Arabic and all Islamic terms are in Arabic, millions of Muslims (both Arab and non-Arab) study the language. Software and books with tapes are an important part of Arabic learning, as many of Arabic learners" +716,"sherbet, sofa, sumac, tariff, and zenith. Other languages such as Maltese and Kinubi derive ultimately from Arabic, rather than merely borrowing vocabulary or grammatical rules. Terms borrowed range from religious terminology (like Berber taẓallit, prayer , from salat ( )), academic terms (like Uyghur mentiq, logic ), and economic items (like English coffee) to placeholders (like Spanish , so-and-so ), everyday terms (like Hindustani lekin, but , or Spanish and French , meaning cup ), and expressions (like Catalan , galore, in quantity ). Most Berber varieties (such as Kabyle), along with Swahili, borrow some numbers from Arabic. Most Islamic religious terms are direct borrowings from Arabic, such as (ṣalāh), prayer , and (imām), prayer leader . In languages not directly in contact with the Arab world, Arabic loanwords are often transferred indirectly via other languages rather than being transferred directly from Arabic. For example, most Arabic loanwords in Hindustani and Turkish ent" +717,"The others such as Algerian Arabic can be considered in between the two in terms of difficulty. ** Libyan Arabic, spoken in Libya and neighboring countries. ** Tunisian Arabic, spoken in Tunisia and north-eastern Algeria. ** Algerian Arabic, spoken in Algeria. ** Judeo-Algerian Arabic was spoken by Jews in Algeria until 1962. ** Moroccan Arabic, spoken in Morocco. ** Hassaniya Arabic (3 million speakers), spoken in Mauritania, Western Sahara, some parts of the Azawad in northern Mali, southern Morocco, and south-western Algeria. ** Andalusian Arabic, spoken in Spain until the 16th century. ** Siculo-Arabic (Sicilian Arabic), was spoken in Sicily and Malta between the end of the 9th century and the end of the 12th century and eventually evolved into the Maltese language. *** Maltese, spoken on the island of Malta, is the only fully separate standardized language to have originated from an Arabic dialect, the extinct Siculo-Arabic dialect, with independent literary norms. Maltese has evo" +718,"of Arabic has similarities with the grammar of other Semitic languages. Some of the typical differences between Standard Arabic () and vernacular varieties are a loss of morphological markings of grammatical case, changes in word order, a shift toward more analytic morphosyntax, loss of grammatical mood, and loss of the inflected passive voice. === Literary Arabic === As in other Semitic languages, Arabic has a complex and unusual morphology, i.e. method of constructing words from a basic root. Arabic has a nonconcatenative root-and-pattern morphology: A root consists of a set of bare consonants (usually three), which are fitted into a discontinuous pattern to form words. For example, the word for 'I wrote' is constructed by combining the root 'write' with the pattern 'I Xed' to form 'I wrote'. Other verbs meaning 'I Xed' will typically have the same pattern but with different consonants, e.g. 'I read', 'I ate', 'I went', although other patterns are possible, e.g. 'I drank', 'I said'," +719,"to colors and physical defects): Most of these forms are exclusively Classical Arabic Form Past Meaning Non-past Meaning I 'he wrote' 'he writes' II 'he made (someone) write' he makes (someone) write III 'he corresponded with, wrote to (someone)' 'he corresponds with, writes to (someone)' IV 'he dictated' 'he dictates' V nonexistent nonexistent VI 'he corresponded (with someone, esp. mutually)' 'he corresponds (with someone, esp. mutually)' VII 'he subscribed' 'he subscribes' VIII 'he copied' 'he copies' IX 'he turned red' 'he turns red' X 'he asked (someone) to write' 'he asks (someone) to write' Form II is sometimes used to create transitive denominative verbs (verbs built from nouns); Form V is the equivalent used for intransitive denominatives. The associated participles and verbal nouns of a verb are the primary means of forming new lexical nouns in Arabic. This is similar to the process by which, for example, the English gerund meeting (similar to a verbal noun) has turned into a" +720,"y; i/ee; ei/ai th th ṯ ṯ ṯ ç ṯ c _t s/th ~~ j j j ǧ ǧ ŷ j j ^g j/g/dj ḩ ḥ ḥ ḥ ḥ ḥ ḥ H .h 7 kh kh ḵ ḫ ẖ j x K _h kh/7'/5 dh dh ḏ ḏ ḏ ḏ đ z' _d z/dh/th sh sh š š š š š x ^s sh/ch ş ṣ ṣ ṣ ṣ ṣ ṣ S .s s/9 ḑ ḍ ḍ ḍ ḍ ḍ ḍ D .d d/9' ţ ṭ ṭ ṭ ṭ ṭ ṭ T .tu t/6 ~ z̧ ẓ ẓ ẓ ẓ ẓ đ̣ Z .z z/dh/6' ʿ ʿ ʿ ř E ' 3 gh gh ḡ ġ ġ g j g .g gh/3'/8 There are a number of different standards for the romanization of Arabic, i.e. methods of accurately and efficiently representing Arabic with the Latin script. There are various conflicting motivations involved, which leads to multiple systems. Some are interested in transliteration, i.e. representing the spelling of Arabic, while others focus on transcription, i.e. representing the pronunciation of Arabic. (They differ in that, for example, the same letter is used to represent both a consonant, as in you or yet , and a vowel, as in me or eat .) Some systems, e.g. for scholarly use, are intended to accurately and unambiguously represent the phonemes of Arabic, generally" +721,"As Sayid and Muhammad Azmi, two Egyptian intellectuals, agreed with Musa and supported the push for Romanization.Shrivtiel, p. 189 The idea that Romanization was necessary for modernization and growth in Egypt continued with Abd Al-Aziz Fahmi in 1944. He was the chairman for the Writing and Grammar Committee for the Arabic Language Academy of Cairo. This effort failed as the Egyptian people felt a strong cultural tie to the Arabic alphabet. In particular, the older Egyptian generations believed that the Arabic alphabet had strong connections to Arab values and history, due to the long history of the Arabic alphabet (Shrivtiel, 189) in Muslim societies. == Sample text == From Article 1 of the Universal Declaration of Human Rights Modern Standard Arabic, Arabic script ALA-LC transliteration English All human beings are born free and equal in dignity and rights. They are endowed with reason and conscience and should act towards one another in a spirit of brotherhood. == See also == * Arab" +722,"Sir Alfred Joseph Hitchcock (13 August 1899 – 29 April 1980) was an English film director. He is widely regarded as one of the most influential figures in the history of cinema. In a career spanning six decades, he directed over 50 feature films, many of which are still widely watched and studied today. Known as the Master of Suspense , Hitchcock became as well known as any of his actors thanks to his many interviews, his cameo appearances in most of his films, and his hosting and producing the television anthology Alfred Hitchcock Presents (1955–65). His films garnered 46 Academy Award nominations, including six wins, although he never won the award for Best Director, despite five nominations. Hitchcock initially trained as a technical clerk and copywriter before entering the film industry in 1919 as a title card designer. His directorial debut was the British–German silent film The Pleasure Garden (1925). His first successful film, The Lodger: A Story of the London Fog (1927), helped" +723,"deliberately enrolled as a ten-year-old because he was a year behind with his schooling. While biographer Gene Adair reports that Hitchcock was an average, or slightly above-average, pupil , Hitchcock said that he was usually among the four or five at the top of the class ; at the end of his first year, his work in Latin, English, French and religious education was noted.; He told Peter Bogdanovich: The Jesuits taught me organisation, control and, to some degree, analysis. Hitchcock's favourite subject was geography and he became interested in maps and the timetables of trains, trams and buses; according to John Russell Taylor, he could recite all the stops on the Orient Express. He had a particular interest in London trams. An overwhelming majority of his films include rail or tram scenes, in particular The Lady Vanishes, Strangers on a Train and Number Seventeen. A clapperboard shows the number of the scene and the number of takes, and Hitchcock would often take the two numbers on th" +724,"Virginia Valli, a co-production of Gainsborough and the German firm Emelka at the Geiselgasteig studio near Munich. Reville, by then Hitchcock's fiancée, was assistant director-editor. Although the film was a commercial flop, Balcon liked Hitchcock's work; a Daily Express headline called him the Young man with a master mind . In March 1926, the British film magazine Picturegoer ran an article entitled Alfred the Great by the film critic Cedric Belfrage, who praised Hitchcock for possessing such a complete grasp of all the different branches of film technique that he is able to take far more control of his production than the average director of four times his experience. Production of The Pleasure Garden encountered obstacles which Hitchcock would later learn from: on arrival to Brenner Pass, he failed to declare his film stock to customs and it was confiscated; one actress could not enter the water for a scene because she was on her period; budget overruns meant that he had to borrow" +725,"; this followed the rapid development of sound films in the United States, from the use of brief sound segments in The Jazz Singer (1927) to the first full sound feature Lights of New York (1928).; also see ; Blackmail began the Hitchcock tradition of using famous landmarks as a backdrop for suspense sequences, with the climax taking place on the dome of the British Museum. It also features one of his longest cameo appearances, which shows him being bothered by a small boy as he reads a book on the London Underground. In the PBS series The Men Who Made The Movies, Hitchcock explained how he used early sound recording as a special element of the film to create tension, with a gossipy woman (Phyllis Monkman) stressing the word knife in her conversation with the woman suspected of murder.; During this period, Hitchcock directed segments for a BIP revue, Elstree Calling (1930), and directed a short film, An Elastic Affair (1930), featuring two Film Weekly scholarship winners. An Elastic Af" +726,"Hitchcock to the larger film studios. Selznick made only a few films each year, as did fellow independent producer Samuel Goldwyn, so he did not always have projects for Hitchcock to direct. Goldwyn had also negotiated with Hitchcock on a possible contract, only to be outbid by Selznick. In a later interview, Hitchcock said: [Selznick] was the Big Producer. ... Producer was king. The most flattering thing Mr. Selznick ever said about me—and it shows you the amount of control—he said I was the 'only director' he'd 'trust with a film'. Hitchcock approached American cinema cautiously; his first American film was set in England in which the Americanness of the characters was incidental: Rebecca (1940) was set in a Hollywood version of England's Cornwall and based on a novel by English novelist Daphne du Maurier. Selznick insisted on a faithful adaptation of the book, and disagreed with Hitchcock with the use of humour. The film, starring Laurence Olivier and Joan Fontaine, concerns an unna" +727,"the director in a before-and-after advertisement for Reduco-Obesity Slayer . He told Truffaut in 1962: Hitchcock's typical dinner before his weight loss had been a roast chicken, boiled ham, potatoes, bread, vegetables, relishes, salad, dessert, a bottle of wine and some brandy. To lose weight, his diet consisted of black coffee for breakfast and lunch, and steak and salad for dinner, but it was hard to maintain; Donald Spoto wrote that his weight fluctuated considerably over the next 40 years. At the end of 1943, despite the weight loss, the Occidental Insurance Company of Los Angeles refused his application for life insurance. ====Wartime non-fiction films==== Hitchcock returned to the UK for an extended visit in late 1943 and early 1944. While there he made two short propaganda films, Bon Voyage (1944) and Aventure Malgache (1944), for the Ministry of Information. In June and July 1945, Hitchcock served as treatment advisor on a Holocaust documentary that used Allied Forces footage" +728,"Catholic priest. ===Peak years: 1954–1964=== ====Dial M for Murder and Rear Window==== I Confess was followed by three colour films starring Grace Kelly: Dial M for Murder (1954), Rear Window (1954) and To Catch a Thief (1955). In Dial M for Murder, Ray Milland plays the villain who tries to murder his unfaithful wife (Kelly) for her money. She kills the hired assassin in self-defence, so Milland manipulates the evidence to make it look like murder. Her lover, Mark Halliday (Robert Cummings), and Police Inspector Hubbard (John Williams) save her from execution. Hitchcock experimented with 3D cinematography for Dial M for Murder. Hitchcock moved to Paramount Pictures and filmed Rear Window (1954), starring James Stewart and Grace Kelly, as well as Thelma Ritter and Raymond Burr. Stewart's character is a photographer named Jeff (based on Robert Capa) who must temporarily use a wheelchair. Out of boredom, he begins observing his neighbours across the courtyard, then becomes convinced that" +729,"copied by many filmmakers. The film premiered at the San Sebastián International Film Festival, and Hitchcock won the Silver Seashell prize. Vertigo is considered a classic, but it attracted mixed reviews and poor box-office receipts at the time; the critic from Variety opined that the film was too slow and too long . Bosley Crowther of the New York Times thought it was devilishly far-fetched , but praised the cast performances and Hitchcock's direction. The picture was also the last collaboration between Stewart and Hitchcock. In the 2002 Sight & Sound polls, it ranked just behind Citizen Kane (1941); ten years later, in the same magazine, critics chose it as the best film ever made. ====North by Northwest and Psycho==== After Vertigo, the rest of 1958 was a difficult year for Hitchcock. During pre-production of North by Northwest (1959), which was a slow and agonising process, his wife Alma was diagnosed with cancer. While she was in hospital, Hitchcock kept himself occupied with his" +730,"said: [N]othing could have been more horrible for me than to arrive on that movie set and to see her being treated the way she was. While filming the attack scene in the attic — which took a week to film — she was placed in a caged room while two men wearing elbow-length protective gloves threw live birds at her. Toward the end of the week, to stop the birds' flying away from her too soon, one leg of each bird was attached by nylon thread to elastic bands sewn inside her clothes. She broke down after a bird cut her lower eyelid, and filming was halted on doctor's orders. ====Marnie==== In June 1962, Grace Kelly announced that she had decided against appearing in Marnie (1964). Hedren had signed an exclusive seven-year, $500-a-week contract with Hitchcock in October 1961,; and he decided to cast her in the lead role opposite Sean Connery. In 2016, describing Hedren's performance as one of the greatest in the history of cinema , Richard Brody called the film a story of sexual violence in" +731,"the 1980 New Year Honours. He was too ill to travel to London—he had a pacemaker and was being given cortisone injections for his arthritis—so on 3 January 1980 the British consul general presented him with the papers at Universal Studios. Asked by a reporter after the ceremony why it had taken the Queen so long, Hitchcock quipped, I suppose it was a matter of carelessness. Cary Grant, Janet Leigh and others attended a luncheon afterwards. His last public appearance was on 16 March 1980, when he introduced the next year's winner of the American Film Institute award. He died of kidney failure the following month, on 29 April, in his Bel Air home. Donald Spoto, one of Hitchcock's biographers, wrote that Hitchcock had declined to see a priest, but according to Jesuit priest Mark Henninger, he and another priest, Tom Sullivan, celebrated Mass at the filmmaker's home, and Sullivan heard his confession. Hitchcock was survived by his wife and daughter. His funeral was held at Good Shepherd Ca" +732,"viewers to identify with them. The director's strict childhood and Jesuit education may have led to his distrust of authority figures such as policemen and politicians; a theme which he has explored. Also, he used the MacGuffin —the use of an object, person or event to keep the plot moving along even if it was non-essential to the story. Hitchcock appears briefly in most of his own films. For example, he is seen struggling to get a double bass onto a train (Strangers on a Train), walking dogs out of a pet shop (The Birds), fixing a neighbour's clock (Rear Window), as a shadow (Family Plot), sitting at a table in a photograph (Dial M for Murder), and riding a bus (North by Northwest, To Catch a Thief). ===Representation of women=== Hitchcock's portrayal of women has been the subject of much scholarly debate. Bidisha wrote in The Guardian in 2010: There's the vamp, the tramp, the snitch, the witch, the slink, the double-crosser and, best of all, the demon mommy. Don't worry, they all get" +733,"the film was originally envisioned. He noted that the myth of storyboards in relation to Hitchcock, often regurgitated by generations of commentators on his films, was to a great degree perpetuated by Hitchcock himself or the publicity arm of the studios. For example, the celebrated crop-spraying sequence of North by Northwest was not storyboarded at all. After the scene was filmed, the publicity department asked Hitchcock to make storyboards to promote the film, and Hitchcock in turn hired an artist to match the scenes in detail. Even when storyboards were made, scenes that were shot differed from them significantly. Krohn's analysis of the production of Hitchcock classics like Notorious reveals that Hitchcock was flexible enough to change a film's conception during its production. Another example Krohn notes is the American remake of The Man Who Knew Too Much, whose shooting schedule commenced without a finished script and moreover went over schedule, something that, as Krohn notes," +734,"includes home movies, 16mm film shot on the set of Blackmail (1929) and Frenzy (1972), and the earliest known colour footage of Hitchcock. The Academy Film Archive has preserved many of his home movies. In 1984, Pat Hitchcock donated her father's papers to the academy's Margaret Herrick Library. The David O. Selznick and the Ernest Lehman collections housed at the Harry Ransom Humanities Research Center in Austin, Texas, contain material related to Hitchcock's work on the production of The Paradine Case, Rebecca, Spellbound, North by Northwest and Family Plot. ===Hitchcock portrayals=== * Anthony Hopkins in Hitchcock (2012) * Toby Jones in The Girl (2012) * Roger Ashton-Griffiths in Grace of Monaco (2014) * EpicLLOYD in the YouTube comedy series Epic Rap Battles of History (2014) ==Filmography== ===Films=== Silent films Sound films ==See also== * Alfred Hitchcock's unrealized projects * List of cameo appearances by Alfred Hitchcock * List of film director and actor collaborations *Rema" +735,"Anacondas or water boas are a group of large boas of the genus Eunectes. They are a semiaquatic group of snakes found in tropical South America. Three to five extant and one extinct species are currently recognized, including one of the largest snakes in the world, E. murinus, the green anaconda. ==Description== Although the name applies to a group of snakes, it is often used to refer only to one species, in particular, the common or green anaconda (Eunectes murinus), which is the largest snake in the world by weight, and the second longest after the reticulated python. ==Origin== The recent fossil record of Eunectes is relatively sparse compared to other vertebrates and other genera of snakes. The fossil record of this group is effected by an artifact called the Pull of the Recent. Fossils of recent ancestors are not known, so the living species 'pull' the historical range of the genus to the present. ==Etymology== The name Eunectes is derived from . The South American names anacaucho" +736,"the mating season female anacondas release pheromones to attract males for breeding, which can result in polyandrous breeding balls; these breeding balls have been observed in E. murinus, E. notaeus, and E. deschauenseei, and likely also occur in E. beniensis. In the green anaconda (E. murinus), up to 13 males have been observed in a breeding ball, which have been recorded to last two weeks on average. In anaconda breeding balls, several males coil around one female and attempt to position themselves as close to her cloaca as possible where they use their pelvic spurs to tickle and encourage her to allow penetration. Since there are often many males present and only one male can mate with the female at a time, the success of a male often depends on his persistence and endurance, because physical combat is not a part of the Eunectes mating ritual, apart from firmly pushing against other males in an attempt to secure the best position on the female. ===Sexual cannibalism=== Cannibalism i" +737,"The Altaic () languages consist of the Turkic, Mongolic and Tungusic language families, with some linguists including the Koreanic and Japonic families. These languages share agglutinative morphology, head-final word order and some vocabulary. The once-popular theory attributing these similarities to a common ancestry has long been rejected by most comparative linguists in favor of language contact, although it continues to be supported by a small but stable scholarly minority. Like the Uralic language family, which is named after the Ural Mountains, the group is named after the Altai mountain range in the center of Asia. The core grouping of Turkic, Mongolic and Tungusic is sometimes called Micro-Altaic , with the expanded group including Koreanic and Japonic labelled as Macro-Altaic or Transeurasian . The Altaic family was first proposed in the 18th century. It was widely accepted until the 1960s and is still listed in many encyclopedias and handbooks, and references to Altaic as a l" +738,"Ramer and Paul Sidwell (1997): The truth about Strahlenberg's classification of the languages of Northeastern Eurasia. Journal de la Société finno-ougrienne, volume 87, pages 139–160. Later proposals to include the Korean and Japanese languages into a Macro-Altaic family have always been controversial. The original proposal was sometimes called Micro-Altaic by retronymy. Most proponents of Altaic continue to support the inclusion of Korean, but fewer do for Japanese.Roger Blench and Mallam Dendo (2008): Stratification in the peopling of China: how far does the linguistic evidence match genetics and archaeology? In Alicia Sanchez-Mazas et al., eds. Human migrations in continental East Asia and Taiwan: genetic, linguistic and archaeological evidence, chapter 4. Taylor & Francis. Some proposals also included Ainuic but this is not widely accepted even among Altaicists themselves. A common ancestral Proto-Altaic language for the Macro family has been tentatively reconstructed by Sergei Sta" +739,"is neither a strong proof of common Proto-Altaic lexical items nor solid regular sound correspondences but, rather, only lexical and structural borrowings between languages of the Altaic typology, our results indirectly speak in favour of a “Paleo-Asiatic” origin of the Japonic and Koreanic languages. ===The Ainu language=== In 1962, John C. Street proposed an alternative classification, with Turkic-Mongolic-Tungusic in one grouping and Korean-Japanese-Ainu in another, joined in what he designated as the North Asiatic family.John C. Street (1962): Review of N. Poppe, Vergleichende Grammatik der altaischen Sprachen, Teil I (1960) . Language, volume 38, pages 92–98. The inclusion of Ainu was adopted also by James Patrie in 1982.James Tyrone Patrie (1978): The genetic relationship of the Ainu language. PhD thesis, University of Hawaii.James Tyrone Patrie (1982): The Genetic Relationship of the Ainu Language. University of Hawaii Press. The Turkic-Mongolic-Tungusic and Korean-Japanese-Ainu" +740,"*Claus Schönig (2003). *Stefan Georg (2004, 2005). *Alexander Vovin (2005, 2010, 2017). Formerly an advocate of Altaic (1994, 1995, 1997, 1999, 2000, 2001), later a critic. *Alexander Shcherbak. *Alexander B. M. Stiven (2008, 2010). ====Advocates of alternative hypotheses==== *James Patrie (1982) and Joseph Greenberg (2000–2002). Turkic–Mongolic–Tungusic and Korean–Japanese–Ainu, grouped in a common taxon (cf. John C. Street 1962). *J. Marshall Unger (1990). Tungusic–Korean–Japanese ( Macro-Tungusic ), with Turkic and Mongolic as separate language families. *Lars Johanson (2010). Agnostic, proponent of a Transeurasian verbal morphology not necessarily genealogically linked. === Transeurasian renaming=== In Robbeets and Johanson (2010), there was a proposal to replace the name Altaic with the name Transeurasian . While Altaic has sometimes included Japonic, Koreanic, and other languages or families, but only on the consideration of particular authors, Transeurasian was specifically inte" +741,"and Tungusic languages form a Sprachbund: a set of languages with similarities due to convergence through intensive borrowing and long contact, rather than common origin.Gerard Clauson (1956). The case against the Altaic theory . Central Asiatic Journal volume 2, pages 181–187Gerhard Doerfer (1963): Bemerkungen zur Verwandtschaft der sog. altaische Sprachen ('Remarks on the relationship of the so-called Altaic languages') In Gerhard Doerfer ed.: Türkische und mongolische Elemente im Neupersischen, Bd. I: Mongolische Elemente im Neupersischen, pages 51–105. Franz Steiner, WiesbadenAlexander Shcherbak (1963). Asya Pereltsvaig further observed in 2011 that, in general, genetically related languages and families tend to diverge over time: the earlier forms are more similar than modern forms. However, she claims that an analysis of the earliest written records of Mongolic and Turkic languages shows the opposite, suggesting that they do not share a common traceable ancestor, but rather have" +742,"Europa und Asia.... Stockholm. (Reprint: 1975. Studia Uralo-Altaica. Szeged and Amsterdam.) *Strahlenberg, P.J.T. von. 1738. Russia, Siberia and Great Tartary, an Historico-geographical Description of the North and Eastern Parts of Europe and Asia.... (Reprint: 1970. New York: Arno Press.) English translation of the previous. *Tekin, Talat. 1994. Altaic languages. In The Encyclopedia of Language and Linguistics, Vol. 1, edited by R.E. Asher. Oxford and New York: Pergamon Press. *Vovin, Alexander. 1993. About the phonetic value of the Middle Korean grapheme ᅀ. Bulletin of the School of Oriental and African Studies 56(2), 247–259. *Vovin, Alexander. 1994. Genetic affiliation of Japanese and methodology of linguistic comparison. Journal de la Société finno-ougrienne 85, 241–256. *Vovin, Alexander. 2001. Japanese, Korean, and Tungusic: evidence for genetic relationship from verbal morphology. Altaic Affinities (Proceedings of the 40th Meeting of PIAC, Provo, Utah, 1997), edited by David B." +743,"Austrian German (), Austrian Standard German (ASG), Standard Austrian German (), Austrian High German (), or simply just Austrian (), is the variety of Standard German written and spoken in Austria and South Tyrol. It has the highest sociolinguistic prestige locally, as it is the variation used in the media and for other formal situations. In less formal situations, Austrians use Bavarian and Alemannic dialects, which are traditionally spoken but rarely written in Austria. It has been standardized with the publishing of the Österreichisches Wörterbuch in 1951. ==History== Austrian German has its beginning in the mid-18th century, when Empress Maria Theresa and her son Joseph II introduced compulsory schooling in 1774, and several reforms of administration in their multilingual Habsburg Empire. At the time, the written standard was Oberdeutsche Schreibsprache (Upper German written language), which was highly influenced by the Bavarian and Alemannic dialects of Austria. Another option wa" +744,"February) along with , (this year) along with , (stairs) along with , (chimney) instead of , many administrative, legal and political terms, and many food terms, including the following:Otto Back, Erich Benedikt, Karl Blüml, et al.: Österreichisches Wörterbuch (neue Rechtschreibung). Herausgegeben im Auftrag des Bundesministeriums für Unterricht, Kunst und Kultur. Auf der Grundlage des amtlichen Regelwerks. 41. circulation, Österreichischer Bundesverlag, Wien 2009, Austrian Standard German Standard German English Brandteigkrapferl Windbeutel Cream puff Eierspeise Rühreier Scrambled eggs Erdapfel (also Bavarian and Southern German) Kartoffel Potato Faschiertes Hackfleisch Minced meat/Ground beef Fisolen Gartenbohnen or Grüne Bohnen Common beans /green beans Karfiol (also Bavarian and Southern German) Blumenkohl Cauliflower Kohlsprossen Rosenkohl Brussel sprouts Kren (also Bavarian and Southern German) Meerrettich Horseradish Kukuruz (southeastern and western Austria) Mais Maize/corn Mar" +745,"In mathematics, the axiom of choice, abbreviated AC or AoC, is an axiom of set theory equivalent to the statement that a Cartesian product of a collection of non-empty sets is non-empty. Informally put, the axiom of choice says that given any collection of sets, each containing at least one element, it is possible to construct a new set by choosing one element from each set, even if the collection is infinite. Formally, it states that for every indexed family (S_i)_{i \in I} of nonempty sets, there exists an indexed set (x_i)_{i \in I} such that x_i \in S_i for every i \in I. The axiom of choice was formulated in 1904 by Ernst Zermelo in order to formalize his proof of the well-ordering theorem.. In many cases, a set created by choosing elements can be made without invoking the axiom of choice, particularly if the number of sets from which to choose the elements is finite, or if a canonical rule on how to choose the elements is available — some distinguishing property that happens to h" +746,"∀e (e ∈ x → ∃a (a ∈ e ∧ a ∈ c ∧ ∀b ((b ∈ e ∧ b ∈ c) → a = b)))) Note that P ∨ Q ∨ R is logically equivalent to (¬P ∧ ¬Q) → R. In English, this first-order sentence reads: :Given any set X, :X contains the empty set as an element or :the elements of X are not pairwise disjoint or :there exists a set C such that its intersection with any of the elements of X contains exactly one element. This guarantees for any partition of a set X the existence of a subset C of X containing exactly one element from each part of the partition. Another equivalent axiom only considers collections X that are essentially powersets of other sets: :For any set A, the power set of A (with the empty set removed) has a choice function. Authors who use this formulation often speak of the choice function on A, but this is a slightly different notion of choice function. Its domain is the power set of A (with the empty set removed), and so makes sense for any set A, whereas with the definition used elsewhere in this" +747,"choose the least element of every set under our unusual ordering. The problem then becomes that of constructing a well-ordering, which turns out to require the axiom of choice for its existence; every set can be well-ordered if and only if the axiom of choice holds. ==Criticism and acceptance== A proof requiring the axiom of choice may establish the existence of an object without explicitly defining the object in the language of set theory. For example, while the axiom of choice implies that there is a well-ordering of the real numbers, there are models of set theory with the axiom of choice in which no individual well-ordering of the reals is definable. Similarly, although a subset of the real numbers that is not Lebesgue measurable can be proved to exist using the axiom of choice, it is consistent that no such set is definable.. The axiom of choice proves the existence of these intangibles (objects that are proved to exist, but which cannot be explicitly constructed), which may confl" +748,"much more complex model that satisfies ZF¬C (ZF with the negation of AC added as axiom) and thus showing that ZF¬C is consistent. Cohen's model is a symmetric model, which is similar to permutation models, but uses generic subsets of the natural numbers (justified by forcing) in place of urelements. Together these results establish that the axiom of choice is logically independent of ZF. The assumption that ZF is consistent is harmless because adding another axiom to an already inconsistent system cannot make the situation worse. Because of independence, the decision whether to use the axiom of choice (or its negation) in a proof cannot be made by appeal to other axioms of set theory. It must be made on other grounds. One argument in favor of using the axiom of choice is that it is convenient because it allows one to prove some simplifying propositions that otherwise could not be proved. Many theorems provable using choice are of an elegant general character: the cardinalities of any t" +749,"is projective. **Baer's criterion: Every divisible abelian group is injective. **Every set is a projective object in the category Set of sets. *Functional analysis **The closed unit ball of the dual of a normed vector space over the reals has an extreme point. *Point-set topology **The Cartesian product of any family of connected topological spaces is connected. **Tychonoff's theorem: The Cartesian product of any family of compact topological spaces is compact. **In the product topology, the closure of a product of subsets is equal to the product of the closures. *Mathematical logic **If S is a set of sentences of first-order logic and B is a consistent subset of S, then B is included in a set that is maximal among consistent subsets of S. The special case where S is the set of all first-order sentences in a given signature is weaker, equivalent to the Boolean prime ideal theorem; see the section Weaker forms below. *Graph theory **Every connected graph has a spanning tree. Equivalentl" +750,"infinite-dimensional topological vector space there is a discontinuous linear map. *General topology **A uniform space is compact if and only if it is complete and totally bounded. **Every Tychonoff space has a Stone–Čech compactification. *Mathematical logic **Gödel's completeness theorem for first-order logic: every consistent set of first-order sentences has a completion. That is, every consistent set of first-order sentences can be extended to a maximal consistent set. **The compactness theorem: If \Sigma is a set of first-order (or alternatively, zero-order) sentences such that every finite subset of \Sigma has a model, then \Sigma has a model. ===Possibly equivalent implications of AC=== There are several historically important set-theoretic statements implied by AC whose equivalence to AC is open. Zermelo cited the partition principle, which was formulated before AC itself, as a justification for believing AC. In 1906, Russell declared PP to be equivalent, but whether the partit" +751,"R between objects of type σ and objects of type τ. The axiom of choice states that if for each x of type σ there exists a y of type τ such that R(x,y), then there is a function f from objects of type σ to objects of type τ such that R(x,f(x)) holds for all x of type σ: : (\forall x^\sigma)(\exists y^\tau) R(x,y) \to (\exists f^{\sigma \to \tau})(\forall x^\sigma) R(x,f(x)). Unlike in set theory, the axiom of choice in type theory is typically stated as an axiom scheme, in which R varies over all formulas or over all formulas of a particular logical form. ==Notes== ==References== * * * * * * * * Per Martin-Löf, 100 years of Zermelo's axiom of choice: What was the problem with it? , in Logicism, Intuitionism, and Formalism: What Has Become of Them?, Sten Lindström, Erik Palmgren, Krister Segerberg, and Viggo Stoltenberg-Hansen, editors (2008). * * , available as a Dover Publications reprint, 2013, . * * *Herman Rubin, Jean E. Rubin: Equivalents of the axiom of choice. North Holland, 1963" +752,"Attila ( or ; ), frequently called Attila the Hun, was the ruler of the Huns from 434 until his death in early 453. He was also the leader of an empire consisting of Huns, Ostrogoths, Alans, and Gepids, among others, in Central and Eastern Europe. As nephews to Rugila, Attila and his elder brother Bleda succeeded him to the throne in 435, ruling jointly until the death of Bleda in 445. During his reign, Attila was one of the most feared enemies of the Western and Eastern Roman Empires. He crossed the Danube twice and plundered the Balkans but was unable to take Constantinople. In 441, he led an invasion of the Eastern Roman (Byzantine) Empire, the success of which emboldened him to invade the West. He also attempted to conquer Roman Gaul (modern France), crossing the Rhine in 451 and marching as far as Aurelianum (Orléans), before being stopped in the Battle of the Catalaunian Plains. He subsequently invaded Italy, devastating the northern provinces, but was unable to take Rome. He pla" +753,"nomads, appearing from east of the Volga, who migrated further into Western Europe c. 370 and built up an enormous empire there. Their main military techniques were mounted archery and javelin throwing. They were in the process of developing settlements before their arrival in Western Europe, yet the Huns were a society of pastoral warriors whose primary form of nourishment was meat and milk, products of their herds. The origin and language of the Huns has been the subject of debate for centuries. According to some theories, their leaders at least may have spoken a Turkic language, perhaps closest to the modern Chuvash language. According to the Encyclopedia of European Peoples, the Huns, especially those who migrated to the west, may have been a combination of central Asian Turkic, Mongolic, and Ugric stocks . Attila's father Mundzuk was the brother of kings Octar and Ruga, who reigned jointly over the Hunnic empire in the early fifth century. This form of diarchy was recurrent with t" +754,"Africa was the richest province of the Western Empire and a main source of food for Rome. The Sassanid Shah Yazdegerd II invaded Armenia in 441. The Romans stripped the Balkan area of forces, sending them to Sicily in order to mount an expedition against the Vandals in Africa. This left Attila and Bleda a clear path through Illyricum into the Balkans, which they invaded in 441. The Hunnish army sacked Margus and Viminacium, and then took Singidunum (Belgrade) and Sirmium. During 442, Theodosius recalled his troops from Sicily and ordered a large issue of new coins to finance operations against the Huns. He believed that he could defeat the Huns and refused the Hunnish kings' demands. Attila responded with a campaign in 443. For the first time (as far as the Romans knew) his forces were equipped with battering rams and rolling siege towers, with which they successfully assaulted the military centers of Ratiara and Naissus (Niš) and massacred the inhabitants. Priscus said When we arrived" +755,"point of view, the best outcome was what occurred: Theodoric died, Attila was in retreat and disarray, and the Romans had the benefit of appearing victorious. == Invasion of Italy and death == thumb|Attila is besieging Aquileia (Chronicon Pictum, 1358) Attila returned in 452 to renew his marriage claim with Honoria, invading and ravaging Italy along the way. Communities became established in what would later become Venice as a result of these attacks when the residents fled to small islands in the Venetian Lagoon. His army sacked numerous cities and razed Aquileia so completely that it was afterwards hard to recognize its original site. Aëtius lacked the strength to offer battle, but managed to harass and slow Attila's advance with only a shadow force. Attila finally halted at the River Po. By this point, disease and starvation may have taken hold in Attila's camp, thus hindering his war efforts and potentially contributing to the cessation of invasion. Emperor Valentinian III sent thr" +756,"Eastern Asia, so Attila's ancestors may have come from there. Other historians have suggested that the same features may have been typical of some Scythian people.Wolff, Larry. Inventing Eastern Europe: The Map of Civilization on the Mind of the Enlightenment. Stanford University Press; (1994). pp. 299–230. Fields, Nic. Attila the Hun (Command). Osprey Publishing; UK ed. (2015). pp. 58–60. == Later folklore and iconography == The name has many variants in several languages: Atli and Atle in Old Norse; Etzel in Middle High German (Nibelungenlied); Ætla in Old English; Attila, Atilla, and Etele in Hungarian (Attila is the most popular); Attila, Atilla, Atilay, or Atila in Turkish; and Adil and Edil in Kazakh or Adil ( same/similar ) or Edil ( to use ) in Mongolian. === Attila and Hun tradition in the medieval Hungarian Royal Court === The basic premise of the Hungarian medieval chronicle tradition that the Huns, i.e. the Hungarians coming out twice from Scythia, the guiding principle of" +757,"The Aegean Sea is an elongated embayment of the Mediterranean Sea between Europe and Asia. It is located between the Balkans and Anatolia, and covers an area of some . In the north, the Aegean is connected to the Marmara Sea, which in turn connects to the Black Sea, by the straits of the Dardanelles and the Bosphorus, respectively. The Aegean Islands are located within the sea and some bound it on its southern periphery, including Crete and Rhodes. The sea reaches a maximum depth of to the east of Crete. The Thracian Sea and the Sea of Crete are main subdivisions of the Aegean Sea. The Aegean Islands can be divided into several island groups, including the Dodecanese, the Cyclades, the Sporades, the Saronic islands and the North Aegean Islands, as well as Crete and its surrounding islands. The Dodecanese, located to the southeast, includes the islands of Rhodes, Kos, and Patmos; the islands of Delos and Naxos are within the Cyclades to the south of the sea. Lesbos is part of the North" +758,"Myrtoan Sea to the west with the Argolic Gulf, the Saronic Gulf northwestward, the Petalies Gulf which connects with the South Euboic Sea, the Pagasetic Gulf which connects with the North Euboic Sea, the Thermian Gulf northwestward, the Chalkidiki Peninsula including the Cassandra and the Singitic Gulfs, northward the Strymonian Gulf and the Gulf of Kavala and the rest are in Turkey; Saros Gulf, Edremit Gulf, Dikili Gulf, Gulf of Çandarlı, Gulf of İzmir, Gulf of Kuşadası, Gulf of Gökova, Güllük Gulf. The Aegean Sea is connected to the Sea of Marmara by the Dardanelles, also known from Classical Antiquity as the Hellespont. The Dardanelles are located to the northeast of the sea. It ultimately connects with the Black Sea through the Bosporus strait, upon which lies the city of Istanbul. The Dardanelles and the Bosporus are known as the Turkish Straits. ===Extent=== According to the International Hydrographic Organization, the limits of the Aegean Sea as follows: * On the south: A line r" +759,"Service Resmi İstatistikler: İllerimize Ait Genel İstatistik Verileri (in Turkish). Turkish State Meteorological Service. Retrieved 4 May 2019. ===Population=== Numerous Greek and Turkish settlements are located along their mainland coast, as well as on towns on the Aegean islands. The largest cities are Athens and Thessaloniki in Greece and İzmir in Turkey. The most populated of the Aegean islands is Crete, followed by Euboea and Rhodes. Most populous urban areas on the Aegean coast Athens İzmir Rank City Country Region/County Population (urban) Thessaloniki Bodrum Athens İzmir 1 Athens Greece Central Greece 3,090,508 Thessaloniki Bodrum Athens İzmir 2 İzmir Turkey İzmir Province 2,948,609 Thessaloniki Bodrum Athens İzmir 3 Thessaloniki Greece Macedonia 824,676 Thessaloniki Bodrum Athens İzmir 4 Bodrum Turkey Muğla Province 198,335 Thessaloniki Bodrum Athens İzmir 5 Çanakkale Turkey Çanakkale Province 182,389 Thessaloniki Bodrum Athens İzmir 6 Heraklion Greece Crete 173,993 Thessaloni" +760,"series of conflicts fought by the Roman Republic and its Greek allies in the eastern Mediterranean against several different major Greek kingdoms. They resulted in Roman control or influence over the eastern Mediterranean basin, including the Aegean, in addition to their hegemony in the western Mediterranean after the Punic Wars. During Roman rule, the land around the Aegean Sea fell under the provinces of Achaea, Macedonia, Thracia, Asia and Creta et Cyrenaica (island of Crete) ===Medieval period=== The Fall of the Western Roman Empire allowed its successor state, the Byzantine Empire, to continue Roman control over the Aegean Sea. However, their territory would later be threatened by the Early Muslim conquests initiated by Muhammad in the 7th century. Although the Rashidun Caliphate did not manage to obtain land along the coast of the Aegean Sea, its conquest of the Eastern Anatolian peninsula as well as Egypt, the Levant, and North Africa left the Byzantine Empire weakened. The Umay" +761,"Athens is the chief port in Greece, the largest passenger port in Europe Presentation . http://www.olp.gr. Archived from the original on 20 December 2008. Retrieved 27 December 2008. and the third largest in the world, ANEK Lines – Piraeus . http://www.anek.gr. Archived from the original on 3 December 2008. Retrieved 27 December 2008. servicing about 20 million passengers annually. With a throughput of 1.4 million TEUs, Piraeus is placed among the top ten ports in container traffic in Europe and the top container port in the Eastern Mediterranean. Container terminal . http://www.olp.gr . Archived from the original on 20 December 2008. Retrieved 27 December 2008. Piraeus is also the commercial hub of Greek shipping. Piraeus bi-annually acts as the focus for a major shipping convention, known as Posidonia, which attracts maritime industry professionals from all over the world. Piraeus is currently Greece's third-busiest port in terms of tons of goods transported, behind Agioi Theodoroi a" +762,"A Clockwork Orange is a dystopian satirical black comedy novella by English writer Anthony Burgess, published in 1962. It is set in a near-future society that has a youth subculture of extreme violence. The teenage protagonist, Alex, narrates his violent exploits and his experiences with state authorities intent on reforming him. The book is partially written in a Russian-influenced argot called Nadsat , which takes its name from the Russian suffix that is equivalent to '-teen' in English. According to Burgess, the novel was a jeu d'esprit written in just three weeks. In 2005, A Clockwork Orange was included on Time magazine's list of the 100 best English-language novels written since 1923, and it was named by Modern Library and its readers as one of the 100 best English-language novels of the 20th century. 100 Best Novels . Modern Library. Retrieved 31 October 2012 The original manuscript of the book has been kept at McMaster University's William Ready Division of Archives and Researc" +763,"to be injuries she sustained in the rape. He does not recognise Alex but gives him shelter and questions him about the conditioning he has undergone. Alexander and his colleagues, all highly critical of the government, plan to use Alex as a symbol of state brutality and thus prevent the incumbent government from being re-elected. Alex inadvertently reveals that he was the ringleader of the home invasion; he is removed from the cottage and locked in an upper-storey bedroom as a relentless barrage of classical music plays over speakers. He attempts suicide by leaping from the window. Alex wakes up in a hospital, where he is courted by government officials, anxious to counter the bad publicity created by his suicide attempt. He is informed that F. Alexander has been put away for Alex's protection and his own. Alex is offered a well-paying job if he agrees to side with the government once discharged. A round of tests reveals that his old violent impulses have returned, indicating that the" +764,"Alex choose to be the first reformed by the Ludovico technique. * The Minister of the Interior: The government high-official who determined that the Ludovico's technique will be used to cut recidivism. He is referred to as the Minister of Interior or Inferior by Alex. * Dr Branom: A scientist, co-developer of the Ludovico technique. He appears friendly and almost paternal towards Alex at first, before forcing him into the theatre and what Alex calls the chair of torture . * Dr Brodsky: Branom's colleague and co-developer of the Ludovico technique. He seems much more passive than Branom and says considerably less. * F. Alexander: An author who was in the process of typing his magnum opus A Clockwork Orange when Alex and his droogs broke into his house, beat him, tore up his work and then brutally gang-raped his wife, which caused her subsequent death. He is left deeply scarred by these events and when he encounters Alex two years later, he uses him as a guinea pig in a sadistic experime" +765,"described in the foreword in the April 1995 publication. Along with removing the 21st chapter as insisted by his publisher in the original 1962 edition, he would also change the finished product's name to its current title. ===Use of slang=== The book, narrated by Alex, contains many words in a slang argot which Burgess invented for the book, called Nadsat. It is a mix of modified Slavic words, Cockney rhyming slang and derived Russian (like baboochka). For instance, these terms have the following meanings in Nadsat: droog (друг) = friend; moloko (молоко) = milk; gulliver (голова) = head; malchick (мальчик) or malchickiwick = boy; soomka (сумка) = sack or bag; Bog (Бог) = God; horrorshow (хорошо) = good; prestoopnick (преступник) = criminal; rooker (рука) = hand; cal (кал) = crap; veck (человек) = man or guy; litso (лицо) = face; malenky (маленький) = little; and so on. Some words Burgess invented himself or just adapted from existing languages. Compare Polari. One of Alex's doctors ex" +766,"Award) A Clockwork Orange was chosen by Time magazine as one of the 100 best English-language books from 1923 to 2005. ==Adaptations== A 1965 film by Andy Warhol entitled Vinyl was an adaptation of Burgess's novel. The best known adaptation of the novella is the 1971 film A Clockwork Orange by Stanley Kubrick, with Malcolm McDowell as Alex. In 1987, Burgess published a stage play titled A Clockwork Orange: A Play with Music. The play includes songs, written by Burgess, which are inspired by Beethoven and Nadsat slang. A manga anthology by Osamu Tezuka entitled Tokeijikake no Ringo (Clockwork Apple) was released in 1983. In 1988, a German adaptation of A Clockwork Orange at the intimate theatre of Bad Godesberg featured a musical score by the German punk rock band Die Toten Hosen which, combined with orchestral clips of Beethoven's Ninth Symphony and other dirty melodies (so stated by the subtitle), was released on the album Ein kleines bisschen Horrorschau. The track Hier kommt Alex be" +767,"Amsterdam ( , , ; literally, Dam in the Amstel ) is the capital and most populated city of the Netherlands. It has a population of 921,402 within the city proper, 1,457,018 in the urban area and 2,480,394 in the metropolitan area. Located in the Dutch province of North Holland, Amsterdam is colloquially referred to as the Venice of the North , for its large number of canals, now a UNESCO World Heritage Site. Amsterdam was founded at the mouth of the Amstel River, which was dammed to control flooding. Originally a small fishing village in the 12th century, Amsterdam became a major world port during the Dutch Golden Age of the 17th century, when the Netherlands was an economic powerhouse. Amsterdam was the leading centre for finance and trade, as well as a hub of secular art production.Cambridge.org , Capitals of Capital -A History of International Financial Centres – 1780–2005, Youssef Cassis, In the 19th and 20th centuries, the city expanded and new neighborhoods and suburbs were built" +768,"point in the development of the Amstel river mouth was the All Saint's Flood of 1170. In an extremely short period of time, the shallow river IJ turned into a wide estuary, which from then on offered the Amstel an open connection to the Zuiderzee, IJssel and waterways further afield. This made the water flow of the Amstel more active, so excess water could be drained better. With drier banks, the downstream Amstel mouth became attractive for permanent habitation. Moreover, the river had grown from an insignificant peat stream into a junction of international waterways., pp. 75–77. A settlement was built here immediately after the landscape change of 1170. Right from the start of its foundation it focused on traffic, production and trade; not on farming, as opposed to how communities had lived further upstream for the past 200 years and northward for thousands of years., pp. 84–85. The construction of a dam at the mouth of the Amstel, eponymously named Dam, is historically estimated to" +769,"dependent on slave plantations. On 1 July 2021, the mayor of Amsterdam, Femke Halsema, apologised for the city's involvement in the slave trade. === Decline and modernization === Amsterdam's prosperity declined during the 18th and early 19th centuries. The wars of the Dutch Republic with England (latterly, Great Britain) and France took their toll on the city. During the French Revolutionary Wars and the Napoleonic Wars, Amsterdam's significance reached its lowest point, with Holland being absorbed into the French Empire. However, the later establishment of the United Kingdom of the Netherlands in 1815 marked a turning point. The end of the 19th century is sometimes called Amsterdam's second Golden Age. New museums, a railway station, and the were built; At the same time, the Industrial Revolution reached the city. The Amsterdam–Rhine Canal was dug to give Amsterdam a direct connection to the Rhine, and the North Sea Canal was dug to give the port a shorter connection to the North Sea." +770,"==Geography== Amsterdam is located in the Western Netherlands, in the province of North Holland, the capital of which is not Amsterdam, but rather Haarlem. The river Amstel ends in the city centre and connects to a large number of canals that eventually terminate in the IJ. Amsterdam's elevation is about below sea level. The surrounding land is flat as it is formed of large polders. An artificial forest, Amsterdamse Bos, is in the southwest. Amsterdam is connected to the North Sea through the long North Sea Canal. Amsterdam is intensely urbanised, as is the Amsterdam metropolitan area surrounding the city. Comprising of land, the city proper has 4,457 inhabitants per km2 and 2,275 houses per km2. Parks and nature reserves make up 12% of Amsterdam's land area. File:Gem-Amsterdam-OpenTopo.jpg|Topographic map of Amsterdam File:Amsterdam-centrum-OpenTopo.jpg|Large-scale map of the city centre of Amsterdam, including sightseeing markers, . ===Water=== Amsterdam has more than of canals, most" +771,"refugees asylum seekers and undocumented immigrants, came from Europe, the Americas, Asia and Africa. In the 1970s and 1980s, many 'old' Amsterdammers moved to 'new' cities like Almere and Purmerend, prompted by the third Land-use planning bill of the Dutch Government. This bill promoted suburbanization and arranged for new developments in so-called groeikernen , literally cores of growth. Young professionals and artists moved into neighborhoods De Pijp and the Jordaan abandoned by these Amsterdammers. The non-Western immigrants settled mostly in the social housing projects in Amsterdam-West and the Bijlmer. Today, people of non-Western origin make up approximately one-fifth of the population of Amsterdam, and more than 30% of the city's children. A slight majority of the residents of Amsterdam have at least one parent who was born outside the country. However, a much larger majority has at least one parent who was born inside the country (intercultural marriages are common in the city" +772,"fans out south from the Amsterdam Centraal station and Damrak, the main street off the station. The oldest area of the town is known as De Wallen (English: The Quays ). It lies to the east of Damrak and contains the city's famous red-light district. To the south of De Wallen is the old Jewish quarter of Waterlooplein. The medieval and colonial age canals of Amsterdam, known as grachten, embraces the heart of the city where homes have interesting gables. Beyond the Grachtengordel are the former working-class areas of Jordaan and de Pijp. The Museumplein with the city's major museums, the Vondelpark, a 19th-century park named after the Dutch writer Joost van den Vondel, as well as the Plantage neighbourhood, with the zoo, are also located outside the Grachtengordel. Several parts of the city and the surrounding urban area are polders. This can be recognised by the suffix -meer which means lake, as in Aalsmeer, Bijlmermeer, Haarlemmermeer and Watergraafsmeer. ===Canals=== The Amsterdam ca" +773,"that period. Around 1815, architects broke with the baroque style and started building in different neo-styles. Most Gothic style buildings date from that era and are therefore said to be built in a neo-gothic style. At the end of the 19th century, the Jugendstil or Art Nouveau style became popular and many new buildings were constructed in this architectural style. Since Amsterdam expanded rapidly during this period, new buildings adjacent to the city centre were also built in this style. The houses in the vicinity of the Museum Square in Amsterdam Oud-Zuid are an example of Jugendstil. The last style that was popular in Amsterdam before the modern era was Art Deco. Amsterdam had its own version of the style, which was called the Amsterdamse School. Whole districts were built in this style, such as the Rivierenbuurt. A notable feature of the façades of buildings designed in Amsterdamse School is that they are highly decorated and ornate, with oddly shaped windows and doors. The old ci" +774,"five stars contribute 42% of the total beds available and 41% of the overnight stays in Amsterdam. The room occupation rate was 85% in 2017, up from 78% in 2006. The majority of tourists (74%) originate from Europe. The largest group of non-European visitors come from the United States, accounting for 14% of the total. Certain years have a theme in Amsterdam to attract extra tourists. For example, the year 2006 was designated Rembrandt 400 , to celebrate the 400th birthday of Rembrandt van Rijn. Some hotels offer special arrangements or activities during these years. The average number of guests per year staying at the four campsites around the city range from 12,000 to 65,000. In 2023, the city began running a campaign to dissuade British men between the ages of 18 and 35 from coming to the city as tourists. The ad shows young men being handcuffed by police and is part of a new campaign to clean up the city's reputation. On 25 May 2023, in a bid to crackdown on wild tourist behaviour," +775,"Plans were made in 2008 to remodel the square again because many inhabitants of Amsterdam are not happy with its current appearance. thumb|left|upright=0.8|Rembrandt monument on Rembrandtplein The possesses the largest and most important collection of classical Dutch art. It opened in 1885. Its collection consists of nearly one million objects. The artist most associated with Amsterdam is Rembrandt, whose work, and the work of his pupils, is displayed in the Rijksmuseum. Rembrandt's masterpiece The Night Watch is one of the top pieces of art of the museum. It also houses paintings from artists like Bartholomeus van der Helst, Johannes Vermeer, Frans Hals, Ferdinand Bol, Albert Cuyp, Jacob van Ruisdael and Paulus Potter. Aside from paintings, the collection consists of a large variety of decorative art. This ranges from Delftware to giant doll-houses from the 17th century. The architect of the gothic revival building was P.J.H. Cuypers. The museum underwent a 10-year, 375 million euro r" +776,"aan 't IJ is a concert hall, which is located in the IJ near the central station. Its concerts perform mostly modern classical music. Located adjacent to it, is the Bimhuis, a concert hall for improvised and Jazz music. ===Performing arts=== Amsterdam has three main theatre buildings. The Stadsschouwburg at the Leidseplein is the home base of Toneelgroep Amsterdam. The current building dates from 1894. Most plays are performed in the Grote Zaal (Great Hall). The normal program of events encompasses all sorts of theatrical forms. In 2009, the new hall of the Stadsschouwburg Amsterdam, Toneelgroep Amsterdam and Melkweg opened, and the renovation of the front end of the theatre was ready. The Dutch National Opera and Ballet (formerly known as Het Muziektheater), dating from 1986, is the principal opera house and home to Dutch National Opera and Dutch National Ballet. Royal Theatre Carré was built as a permanent circus theatre in 1887 and is currently mainly used for musicals, cabaret perf" +777,"and the Amsterdam Panthers. The Amsterdam Pirates baseball team competes in the Dutch Major League. There are three field hockey teams: Amsterdam, Pinoké and Hurley, who play their matches around the Wagener Stadium in the nearby city of Amstelveen. The basketball team MyGuide Amsterdam competes in the Dutch premier division and play their games in the Sporthallen Zuid. There is one rugby club in Amsterdam, which also hosts sports training classes such as RTC (Rugby Talenten Centrum or Rugby Talent Centre) and the National Rugby stadium. Since 1999, the city of Amsterdam honours the best sportsmen and women at the Amsterdam Sports Awards. Boxer Raymond Joval and field hockey midfielder Carole Thate were the first to receive the awards, in 1999. Amsterdam hosted the World Gymnaestrada in 1991 and will do so again in 2023. ==Politics== The city of Amsterdam is a municipality under the Dutch Municipalities Act. It is governed by a directly elected municipal council, a municipal executive" +778,"oath and his coronation take place in the capital Amsterdam ( de hoofdstad Amsterdam ). Previous versions of the constitution only mentioned the city of Amsterdam ( de stad Amsterdam ). For a royal investiture, therefore, the States General of the Netherlands (the Dutch Parliament) meets for a ceremonial joint session in Amsterdam. The ceremony traditionally takes place at the Nieuwe Kerk on Dam Square, immediately after the former monarch has signed the act of abdication at the nearby Royal Palace of Amsterdam. Normally, however, the Parliament sits in The Hague, the city which has historically been the seat of the Dutch government, the Dutch monarchy, and the Dutch supreme court. Foreign embassies are also located in The Hague. ===Symbols=== The coat of arms of Amsterdam is composed of several historical elements. First and centre are three St Andrew's crosses, aligned in a vertical band on the city's shield (although Amsterdam's patron saint was Saint Nicholas). These St Andrew's cr" +779,"442,693 households (850,000 residents) in Amsterdam together owned 847,000 bicycles – 1.91 bicycles per household. Theft is widespreadin 2011, about 83,000 bicycles were stolen in Amsterdam. Bicycles are used by all socio-economic groups because of their convenience, Amsterdam's small size, the of bike paths, the flat terrain, and the inconvenience of driving an automobile. ==Education== Amsterdam has two universities: the University of Amsterdam (Universiteit van Amsterdam, UvA), and the Vrije Universiteit Amsterdam (VU). Other institutions for higher education include an art school – Gerrit Rietveld Academie, a university of applied sciences – the Hogeschool van Amsterdam, and the Amsterdamse Hogeschool voor de Kunsten. Amsterdam's International Institute of Social History is one of the world's largest documentary and research institutions concerning social history, and especially the history of the labour movement. Amsterdam's Hortus Botanicus, founded in the early 17th century, is" +780,"The Museum of Work (Arbetets museum) is a museum located in Norrköping, Sweden. The museum is located in the Strykjärn (Clothes iron), a former weaving mill in the old industrial area on the Motala ström river in the city centre of Norrköping. The former textile factory Holmens Bruk (sv) operated in the building from 1917 to 1962. The museum documents work and everyday life by collecting personal stories about people's professional lives from both the past and the present. The museum's archive contain material from memory collections and documentation projects. Since 2009, the museum also houses the EWK – Center for Political Illustration Art, which is based on work of the satirist Ewert Karlsson (1918–2004). For decades he was frequently published in the Swedish tabloid, Aftonbladet. == Overview == The museum is a national central museum with the task of preserving and telling about work and everyday life. It has, among other things, exhibitions on the terms and conditions of the work" +781,"Audi AG () is a German automotive manufacturer of luxury vehicles headquartered in Ingolstadt, Bavaria, Germany. A subsidiary of the Volkswagen Group, Audi produces vehicles in nine production facilities worldwide. The origins of the company are complex, dating back to the early 20th century and the initial enterprises (Horch and the Audiwerke) founded by engineer August Horch. Two other manufacturers (DKW and Wanderer) also contributed to the foundation of Auto Union in 1932. The modern Audi era began in the 1960s, when Auto Union was acquired by Volkswagen from Daimler-Benz. After relaunching the Audi brand with the 1965 introduction of the Audi F103 series, Volkswagen merged Auto Union with NSU Motorenwerke in 1969, thus creating the present-day form of the company. The company name is based on the Latin translation of the surname of the founder, August Horch. , meaning 'listen', becomes in Latin. The four rings of the Audi logo each represent one of four car companies that banded t" +782,"car company, after Volkswagen, BMW and Daimler, to commission a study of their wartime activities.Audi's Nazi past, May 26, 2014, DW, https://www.dw.com/en/audi-comes-clean-about-its-nazi-past/a-17664050 The investigation found that the company worked with the SS to build seven labor camps where more than 3,700 prisoners were put to work for Auto Union.May 26, 2014, Audi Used Forced Labour under Nazis in Germany, The Local.de, https://www.thelocal.de/20140526/audi-used-forced-labour-under-nazis-in-germany In addition, 16,500 more people were forced to work at the company's factories in Zwickau and Chemnitz, and another 18,000 at an underground plant in Bavaria where 4,500 people died. Some of the company's factories were bombed by the Allies. ===Post-World War II=== With no prospect of continuing production in Soviet-controlled East Germany, Auto Union executives began the process of relocating what was left of the company to West Germany. A site was chosen in Ingolstadt, Bavaria, to s" +783,"produce small cars like the NSU Prinz, the TT and TTS versions of which are still popular as vintage race cars. NSU then focused on new rotary engines based on the ideas of Felix Wankel. In 1967, the new NSU Ro 80 was a car well ahead of its time in technical details such as aerodynamics, light weight, and safety. However, teething problems with the rotary engines put an end to the independence of NSU. The Neckarsulm plant is now used to produce the larger Audi models A6 and A8. The Neckarsulm factory is also home of the quattro GmbH (from November 2016 Audi Sport GmbH ), a subsidiary responsible for development and production of Audi high-performance models: the R8 and the RS model range. ===Modern era=== The new merged company was incorporated on 1 January 1969 and was known as Audi NSU Auto Union AG, with its headquarters at NSU's Neckarsulm plant, and saw the emergence of Audi as a separate brand for the first time since the pre-war era. Volkswagen introduced the Audi brand to the" +784,"of power. A 60 Minutes report aired 23 November 1986, featuring interviews with six people who had sued Audi after reporting unintended acceleration, showing an Audi 5000 ostensibly suffering a problem when the brake pedal was pushed. Subsequent investigation revealed that 60 Minutes had engineered the failure—fitting a canister of compressed air on the passenger-side floor, linked via a hose to a hole drilled into the transmission. Audi contended, prior to findings by outside investigators, that the problems were caused by driver error, specifically pedal misapplication. Subsequently, the National Highway Traffic Safety Administration (NHTSA) concluded that the majority of unintended acceleration cases, including all the ones that prompted the 60 Minutes report, were caused by driver error such as confusion of pedals. CBS did not acknowledge the test results of involved government agencies, but did acknowledge the similar results of another study. In a review study published in 2012," +785,"with other VW Group marques although many sub-assemblies such as engines and transmissions are manufactured within other Volkswagen Group plants. Audi's two principal assembly plants in Germany are: * Ingolstadt, the former Auto Union site originally opened in 1945 and substantially rebuilt by Daimler-Benz in 1962, and acquired by Volkswagen in 1964 (Q2, A3, A4, A5) * Neckarsulm, the former NSU plant, acquired by Volkswagen in 1969: (A4, A5 (cabrio), A6, A7, A8, R8, and all RS variants) - a satellite plant at nearby Böllinger Höfe produces the e-tron GT Outside of Germany, Audi produces vehicles at: * Aurangabad, India, since 2006 * Bratislava, Slovakia, shared with Volkswagen, SEAT, Škoda and Porsche (Q7 and Q8) * Brussels, Belgium, acquired from Volkswagen in 2007 (e-tron) * Changchun, China, since 1995 * Győr, Hungary (TT, A3, Q3) * Jakarta, Indonesia, since 2011 * Martorell, Spain, shared with SEAT and Volkswagen (A1, Q3) * San José Chiapa, Mexico (2nd gen Q5) In September 2012, Au" +786,"also having best-in-class fuel economy. The Audi A2, Audi TT and Audi R8 also use Audi Space Frame designs. ===Drivetrains=== ====Layout==== For most of its lineup (excluding the A3, A1, and TT models), Audi has not adopted the transverse engine layout which is typically found in economy cars (such as Peugeot and Citroën), since that would limit the type and power of engines that can be installed. To be able to mount powerful engines (such as a V8 engine in the Audi S4 and Audi RS4, as well as the W12 engine in the Audi A8L W12), Audi has usually engineered its more expensive cars with a longitudinally front-mounted engine, in an overhung position, over the front wheels in front of the axle line—this layout dates back to the DKW and Auto Union saloons from the 1950s. But while this allows for the easy adoption of all-wheel drive, it goes against the ideal 50:50 weight distribution. In all its post Volkswagen era models, Audi has firmly refused to adopt the traditional rear-wheel drive" +787,"functions. The colour screen is mounted on the upright dashboard, and on the A4 (new), A5, A6, A8, and Q7, the controls are mounted horizontally. === Synthetic fuels === Audi has assisted with technology to produce synthetic diesel from water and carbon dioxide. Audi calls the synthetic diesel E-diesel. It is also working on synthetic gasoline (which it calls E-gasoline). === Logistics === Audi uses scanning gloves for parts registration during assembly, and automatic robots to transfer cars from factory to rail cars. ==Models== ===Current model range=== The following tables list Audi production vehicles that are sold as of 2024: Audi cars A1 * Sportback (5-door hatchback) A3 * Saloon (sedan) * Sportback (5-door hatchback) A5 * Saloon (5-door hatchback) * Avant (estate/wagon) A6 * Saloon (sedan) * Avant (estate/wagon) * Allroad (crossover estate/wagon) A6 e-tron * Saloon (5-door hatchback) * Avant (estate/wagon) A7 * Sportback (5-door hatchback) A8 * Saloon (sedan) e-tron GT * 5-door f" +788,"car to drive to the summit of the Pikes Peak mountain in Colorado, and in 1985, Michèle Mouton set a new record of 11:25.39, and being the first woman to set a Pikes Peak record. In 1986, Audi formally left international rallying following an accident in Portugal involving driver Joaquim Santos in his Ford RS200. Santos swerved to avoid hitting spectators in the road, and left the track into the crowd of spectators on the side, killing three and injuring 30. Bobby Unser used an Audi in that same year to claim a new record for the Pikes Peak Hill Climb at 11:09.22. In 1987, Walter Röhrl claimed the title for Audi setting a new Pikes Peak International Hill Climb record of 10:47.85 in his Audi S1, which he had retired from the WRC two years earlier. The Audi S1 employed Audi's time-tested inline-five-cylinder turbocharged engine, with the final version generating . The engine was mated to a six-speed gearbox and ran on Audi's famous four-wheel drive system. All of Audi's top drivers drov" +789,"car at 12 Hours of Sebring (the car was developed with a Diesel engine due to ACO regulations that favor diesel engines). As well as winning the 24 Hours of Le Mans in 2006, the R10 TDI beat the Peugeot 908 HDi FAP in , and in , (however Peugeot won the 24h in 2009) with a podium clean-sweep (all four 908 entries retired) while breaking a distance record (set by the Porsche 917K of Martini Racing in ), in with the R15 TDI Plus. Audi's sports car racing success would continue with the Audi R18's victory at the 2011 24 Hours of Le Mans. Audi Sport Team Joest's Benoît Tréluyer earned Audi their first pole position in five years while the team's sister car locked out the front row. Early accidents eliminated two of Audi's three entries, but the sole remaining Audi R18 TDI of Tréluyer, Marcel Fässler, and André Lotterer held off the trio of Peugeot 908s to claim victory by a margin of 13.8 seconds. ====Results==== Car Year 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 201" +790,"Ret 2010 Audi R15 Audi Sport Team Joest 5 3 2010 Audi R15 Audi Sport Team Joest 12 ===World Endurance Championship=== ====2012==== In 2012, the FIA sanctioned a World Endurance Championship which would be organised by the ACO as a continuation of the ILMC. Audi competed won the first WEC race at Sebring and followed this up with a further three successive wins, including the 2012 24 Hours of Le Mans. Audi scored a final 5th victory in the 2012 WEC in Bahrain and were able to win the inaugural WEC Manufacturers' Championship. ====2013==== As defending champions, Audi once again entered the Audi R18 e-tron quattro chassis into the 2013 WEC and the team won the first five consecutive races, including the 2013 24 Hours of Le Mans. The victory at Round 5, Circuit of the Americas, was of particular significance as it marked the 100th win for Audi in Le Mans prototypes. Audi secured their second consecutive WEC Manufacturers' Championship at Round 6 after taking second place and half points i" +791,"2 3 4 5 1 7 248 2nd 2016–17 Spark SRT01-e ABT Schaeffler FE02 66 Daniel Abt Ret 6 7 7 7 13 6 4 14 Ret 4 6 248 2nd Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler Audi Sport Abt Schaeffler 2017–18 Spark SRT01-e Audi e-tron FE04 HKG HKG MRK SCL MEX PDE RME PAR BER ZUR NYC NYC 264 1st 2017–18 Spark SRT01-e Audi e-tron FE04 1 Lucas di Grassi 17 14 Ret Ret 9 2 2 2 2 1 1 2 264 1st 2017–18 Spark SRT01-e Audi e-tron FE04 66 Daniel Abt 5 DSQ 10 Ret 1 14 4 7 1 13 2 3 264 1st 2018–1" +792,"is used), and in other markets, such as Latin America, Oceania, Africa and parts of Asia including Japan. Originally, the American tagline was Innovation through technology, but in Canada Vorsprung durch Technik was used. Since 2007, Audi has used the slogan Truth in Engineering in the U.S.Lavrinc, Damon. Audi planning TT and R8 lightweight Sport models. Autoblog. Retrieved 24 April 2010. However, since the Audi emissions testing scandal came to light in September 2015, this slogan was lambasted for being discordant with reality. In fact, just hours after disgraced Volkswagen CEO Martin Winterkorn admitted to cheating on emissions data, an advertisement during the 2015 Primetime Emmy Awards promoted Audi's latest advances in low emissions technology with Kermit the Frog stating, It's not that easy being green. Vorsprung durch Technik was first used in English-language advertising after Sir John Hegarty of the Bartle Bogle Hegarty advertising agency visited the Audi factory in 1982. In" +793,"Stark (Iron Man). === Lawsuit on the use of the letter Q === In early 2005, Nissan North America Inc. filed a lawsuit against Audi over the use of the letter Q as a model name. Audi is using the Q for the designation of their quattro four-wheel drive system, used in production cars for over twenty-five years (Audi's Quattro trademark is actually an umbrella term for several types of four-wheel-drive systems developed by Torsen, Haldex Traction AB, and Borg-Warner, the latter being used in the Q7). Nissan's Infiniti marque first used Q for their 1989 Infiniti Q45 flagship, but later expanded to its entire lineup, with Q for passenger cars (the Q30, Q40, Q50, and Q60) and QX for SUVs (the QX30, QX50, QX60, and QX70). A settlement between Audi AG and Nissan was reached in late 2006. The agreement stipulates that Audi will only use the Q-prefix for three models, the Q3, Q5 and the Q7. Audi has since released other Q series cars as well, such as the Q2, Q6, and Q8. ===In video games=== Audi" +794,"An aircraft (: aircraft) is a vehicle that is able to fly by gaining support from the air. It counters the force of gravity by using either static lift or the dynamic lift of an airfoil, or, in a few cases, direct downward thrust from its engines. Common examples of aircraft include airplanes, helicopters, airships (including blimps), gliders, paramotors, and hot air balloons. The human activity that surrounds aircraft is called aviation. The science of aviation, including designing and building aircraft, is called aeronautics. Crewed aircraft are flown by an onboard pilot, whereas unmanned aerial vehicles may be remotely controlled or self-controlled by onboard computers. Aircraft may be classified by different criteria, such as lift type, aircraft propulsion (if any), usage and others. == History == Flying model craft and stories of manned flight go back many centuries; however, the first manned ascent — and safe descent — in modern times took place by larger hot-air balloons develop" +795,"on the air for its lift (and can even fly into space); however, many aerodynamic lift vehicles have been powered or assisted by rocket motors. Rocket-powered missiles that obtain aerodynamic lift at very high speed due to airflow over their bodies are a marginal case. ==== Fixed-wing ==== The forerunner of the fixed-wing aircraft is the kite. Whereas a fixed-wing aircraft relies on its forward speed to create airflow over the wings, a kite is tethered to the ground and relies on the wind blowing over its wings to provide lift. Kites were the first kind of aircraft to fly and were invented in China around 500 BC. Much aerodynamic research was done with kites before test aircraft, wind tunnels, and computer modelling programs became available. The first heavier-than-air craft capable of controlled free-flight were gliders. A glider designed by George Cayley carried out the first true manned, controlled flight in 1853. The first powered and controllable fixed-wing aircraft (the airplane o" +796,"weight and largest regular fixed-wing aircraft ever built, , was the Antonov An-225 Mriya. That Soviet-built (Ukrainian SSR) six-engine transport of the 1980s was long, with an wingspan. It holds the world payload record, after transporting of goods, and has flown loads commercially. With a maximum loaded weight of , it was also the heaviest aircraft built to date. It could cruise at . Watch the world's biggest plane land in Australia, 16 May 2016, Fox News. Retrieved 22 November 2016.Lewis, Danny, The World's Largest Aircraft Might Lose its Title to a Blimp, , 18 September 2015, Smart News, Smithsonian.com, Smithsonian Institution, Washington, D.C.. Retrieved 22 November 2016. Ask Us – Largest Plane in the World, Aerospaceweb.org. Retrieved 22 November 2016. The aircraft was destroyed during the Russo-Ukrainian War. The largest military airplanes are the Ukrainian Antonov An-124 Ruslan (world's second-largest airplane, also used as a civilian transport), World's Second Largest Aircraf" +797,"Pratt & Whitney J58. Compared to engines using propellers, jet engines can provide much higher thrust, higher speeds and, above about , greater efficiency. They are also much more fuel-efficient than rockets. As a consequence nearly all large, high-speed or high-altitude aircraft use jet engines. ==== Rotorcraft ==== Some rotorcraft, such as helicopters, have a powered rotary wing or rotor, where the rotor disc can be angled slightly forward so that a proportion of its lift is directed forwards. The rotor may, like a propeller, be powered by a variety of methods such as a piston engine or turbine. Experiments have also used jet nozzles at the rotor blade tips. ==== Other types of powered aircraft ==== * Rocket-powered aircraft have occasionally been experimented with, and the Messerschmitt Me 163 Komet fighter even saw action in the Second World War. Since then, they have been restricted to research aircraft, such as the North American X-15, which traveled up into space where air-breat" +798,"non-recoverable spin (possible reasons for the boundary). === Range === The range is the distance an aircraft can fly between takeoff and landing, as limited by the time it can remain airborne. For a powered aircraft the time limit is determined by the fuel load and rate of consumption. For an unpowered aircraft, the maximum flight time is limited by factors such as weather conditions and pilot endurance. Many aircraft types are restricted to daylight hours, while balloons are limited by their supply of lifting gas. The range can be seen as the average ground speed multiplied by the maximum time in the air. The Airbus A350-900ULR is among the longest range airliners. === Flight dynamics === Flight dynamics is the science of air vehicle orientation and control in three dimensions. The three critical flight dynamics parameters are the angles of rotation around three axes which pass through the vehicle's center of gravity, known as pitch, roll, and yaw. * Roll is a rotation about the long" +799,"private (where the pilot is not paid for time or expenses) and commercial use, and involving a wide range of aircraft types such as business jets (bizjets), trainers, homebuilt, gliders, warbirds and hot air balloons to name a few. The vast majority of aircraft today are general aviation types. === Experimental === An experimental aircraft is one that has not been fully proven in flight, or that carries a Special Airworthiness Certificate, called an Experimental Certificate in United States parlance. This often implies that the aircraft is testing new aerospace technologies, though the term also refers to amateur-built and kit-built aircraft, many of which are based on proven designs. === Model === A model aircraft is a small unmanned type made to fly for fun, for static display, for aerodynamic research or for other purposes. A scale model is a replica of some larger design. == See also == === Lists === * Early flying machines * Flight altitude record * List of aircraft * List of civi" +800,"Alfred Bernhard Nobel ( , ; 21 October 1833 – 10 December 1896) was a Swedish chemist, inventor, engineer and businessman. He is known for inventing dynamite as well as having bequeathed his fortune to establish the Nobel Prizes. He also made several important contributions to science, holding 355 patents in his lifetime. A member of the prominent Nobel family, Nobel displayed an early aptitude for science and learning, particularly in chemistry and languages; he became fluent in six languages and filed his first patent at the age of 24. He embarked on many business ventures with his family, most notably owning the company Bofors, which was an iron and steel producer that he had developed into a major manufacturer of cannons and other armaments. Nobel's most famous invention, dynamite, was an explosive using nitroglycerin that was patented in 1867. He further invented gelignite in 1875 and ballistite in 1887. Upon his death, Nobel donated his fortune to a foundation to fund Nobel Prize" +801,"factories, despite his apparently pacifist character. ===Inventions=== Nobel found that when nitroglycerin was incorporated in an absorbent inert substance like kieselguhr (diatomaceous earth) it became safer and more convenient to handle, and this mixture he patented in 1867 as dynamite . Nobel demonstrated his explosive for the first time that year, at a quarry in Redhill, Surrey, England. In order to help reestablish his name and improve the image of his business from the earlier controversies associated with dangerous explosives, Nobel had also considered naming the highly powerful substance Nobel's Safety Powder , which is the text used in his patent, but settled with Dynamite instead, referring to the Greek word for power (). Nobel later combined nitroglycerin with various nitrocellulose compounds, similar to collodion, but settled on a more efficient recipe combining another nitrate explosive, and obtained a transparent, jelly-like substance, which was a more powerful explosive" +802,"was a contributing factor to his death at a premature age. ==Personal life== === Religion === Nobel was Lutheran and, during his years living in Paris, he regularly attended the Church of Sweden Abroad led by pastor Nathan Söderblom who received the Nobel Peace Prize in 1930. He was an agnostic in youth and became an atheist later in life, though he still donated generously to the Church. ===Relationships and personality=== Nobel remained a solitary character, given to periods of depression. He never married, although his biographers note that he had at least three loves. His first love was in Russia with a girl named Alexandra who rejected his marriage proposal. In 1876, Austro-Bohemian Countess Bertha von Suttner became his secretary, but she left him after a brief stay to marry her previous lover Baron Arthur Gundaccar von Suttner. Her contact with Nobel was brief, yet she corresponded with him until his death in 1896, and probably influenced his decision to include the Nobel Peace" +803,"Alexander Graham Bell (, born Alexander Bell; March 3, 1847 – August 2, 1922) was a Scottish-born Canadian-American inventor, scientist, and engineer who is credited with patenting the first practical telephone. He also co-founded the American Telephone and Telegraph Company (AT&T) in 1885. Bell's father, grandfather, and brother had all been associated with work on elocution and speech, and both his mother and wife were deaf, profoundly influencing Bell's life's work. His research on hearing and speech further led him to experiment with hearing devices, which eventually culminated in his being awarded the first U.S. patent for the telephone, on March 7, 1876. Bell considered his invention an intrusion on his real work as a scientist and refused to have a telephone in his study. Many other inventions marked Bell's later life, including groundbreaking work in optical telecommunications, hydrofoils, and aeronautics. Bell also had a strong influence on the National Geographic Society and" +804,"the earlier work of Baron Wolfgang von Kempelen. The rudimentary mechanical man simulated a human voice. Bell was fascinated by the machine, and after he obtained a copy of von Kempelen's book, published in German, and had laboriously translated it, he and Melville built their own automaton head. Their father, highly interested in their project, offered to pay for any supplies and spurred the boys on with the enticement of a big prize if they were successful. While his brother constructed the throat and larynx, Bell tackled the more difficult task of recreating a realistic skull. His efforts resulted in a remarkably lifelike head that could speak , albeit only a few words. The boys would carefully adjust the lips and when a bellows forced air through the windpipe, a very recognizable Mama ensued, to the delight of neighbors who came to see the invention. Intrigued by the results of the automaton, Bell continued to experiment with a live subject, the family's Skye Terrier, Trouve. After" +805,"distance. Once the family was settled, Bell and his father made plans to establish a teaching practice and in 1871, he accompanied his father to Montreal, where Melville was offered a position to teach his System of Visible Speech. ==Work with deaf people== Bell's father was invited by Sarah Fuller, principal of the Boston School for Deaf Mutes (later to become the public Horace Mann School for the Deaf) to introduce the Visible Speech System by providing training for Fuller's instructors, but he declined the post in favor of his son. Traveling to Boston in April 1871, Bell proved successful in training the school's instructors. He was asked to repeat the program at the American Asylum for Deaf-mutes in Hartford, Connecticut, and the Clarke School for the Deaf in Northampton, Massachusetts. Returning home to Brantford after six months abroad, Bell continued his experiments with his harmonic telegraph . The basic concept behind his device was that messages could be sent through a single" +806,"system of commerce . Orton had contracted with inventors Thomas Edison and Elisha Gray to find a way to send multiple telegraph messages on each telegraph line to avoid the great cost of constructing new lines. When Bell mentioned to Gardiner Hubbard and Thomas Sanders that he was working on a method of sending multiple tones on a telegraph wire using a multi-reed device, the two began to financially support Bell's experiments. Patent matters were handled by Hubbard's patent attorney, Anthony Pollok. In March 1875, Bell and Pollok visited the scientist Joseph Henry, then the director of the Smithsonian Institution, to ask his advice on the electrical multi-reed apparatus that Bell hoped would transmit the human voice by telegraph. Henry said Bell had the germ of a great invention . When Bell said that he lacked the necessary knowledge, Henry replied, Get it! That declaration greatly encouraged Bell to keep trying, even though he had neither the equipment needed to continue his experime" +807,"an improvised wire strung up along telegraph lines and fences, and laid through a tunnel. This time, guests at the household distinctly heard people in Brantford reading and singing. The third test, on August 10, 1876, was made via the telegraph line between Brantford and Paris, Ontario, away. This test is said by many sources to be the world's first long-distance call . It proved that the telephone could work over long distances, at least as a one-way call. The first two-way (reciprocal) conversation over a line occurred between Cambridge and Boston (roughly 2.5 miles) on October 9, 1876. During that conversation, Bell was on Kilby Street in Boston and Watson was at the offices of the Walworth Manufacturing Company. Bell and his partners, Hubbard and Sanders, offered to sell the patent outright to Western Union for $100,000, equal to $ today, but it did not work (according to an apocryphal story, the president of Western Union balked, countering that the telephone was nothing but a to" +808,"that Bell's work on the telephone was influenced by Meucci's inventions. The value of Bell's patent was acknowledged throughout the world, and patent applications were made in most major countries. When Bell delayed the German patent application, the electrical firm Siemens & Halske set up a rival manufacturer of Bell telephones under its own patent. Siemens produced near-identical copies of the Bell telephone without having to pay royalties. The establishment of the International Bell Telephone Company in Brussels, Belgium, in 1880, as well as a series of agreements in other countries eventually consolidated a global telephone operation. The strain put on Bell by his constant appearances in court, necessitated by the legal battles, eventually resulted in his resignation from the company. ==Family life== On July 11, 1877, a few days after the Bell Telephone Company was established, Bell married Mabel Hubbard (1857–1923) at the Hubbard estate in Cambridge, Massachusetts. His wedding pre" +809,"produced from the waste of farms and factories. At his Canadian estate in Nova Scotia, he experimented with composting toilets and devices to capture water from the atmosphere. In a magazine article published in 1917, he reflected on the possibility of using solar energy to heat houses. ===Photophone=== Bell and his assistant Charles Sumner Tainter jointly invented a wireless telephone, named a photophone, which allowed for the transmission of both sounds and normal human conversations on a beam of light. Both men later became full associates in the Volta Laboratory Association. On June 21, 1880, Bell's assistant transmitted a wireless voice telephone message a considerable distance, from the roof of the Franklin School in Washington, D.C., to Bell at the window of his laboratory, some away, 19 years before the first voice radio transmissions. Bell believed the photophone's principles were his life's greatest achievement , telling a reporter shortly before his death that the photophone" +810,"headed by Bell and the founding members were four young men: American Glenn H. Curtiss, a motorcycle manufacturer at the time and who held the title world's fastest man , having ridden his self-constructed motor bicycle around in the shortest time, and who was later awarded the Scientific American Trophy for the first official one-kilometre flight in the Western hemisphere, and who later became a world-renowned airplane manufacturer; Lieutenant Thomas Selfridge, an official observer from the U.S. Federal government and one of the few people in the army who believed that aviation was the future; Frederick W. Baldwin, the first Canadian and first British subject to pilot a public flight in Hammondsport, New York; and J. A. D. McCurdy–Baldwin and McCurdy being new engineering graduates from the University of Toronto. The AEA's work progressed to heavier-than-air machines, applying their knowledge of kites to gliders. Moving to Hammondsport, the group then designed and built the Red Wing," +811,"the Museum of Natural History and chaired by Davenport. Although Bell did not present any research or speak as part of the proceedings, he was named as honorary president as a means to attract other scientists to attend the event. A summary of the event notes that Bell was a pioneering investigator in the field of human heredity . ==Death== Bell died of complications arising from diabetes on August 2, 1922, at his private estate in Cape Breton, Nova Scotia, at age 75. Bell had also been affected by pernicious anemia. His last view of the land he had inhabited was by moonlight on his mountain estate at 2:00 a.m. While tending to him after his long illness, Mabel, his wife, whispered, Don't leave me. By way of reply, Bell signed no... , lost consciousness, and died shortly after. On learning of Bell's death, the Canadian Prime Minister, Mackenzie King, cabled Mrs. Bell, saying: Bell's coffin was constructed of Beinn Bhreagh pine by his laboratory staff, lined with the same red silk fabri" +812,"in London awarded him the Albert Medal in 1902; the University of Würzburg, Bavaria, granted him a PhD, and he was awarded the Franklin Institute's Elliott Cresson Medal in 1912. He was one of the founders of the American Institute of Electrical Engineers in 1884 and served as its president from 1891 to 1892. Bell was later awarded the AIEE's Edison Medal in 1914 For meritorious achievement in the invention of the telephone . The bel (B) and the smaller decibel (dB) are units of measurement of sound pressure level (SPL) invented by Bell Labs and named after him. Decibel. sfu.ca. Retrieved: July 28, 2010. Since 1976, the IEEE's Alexander Graham Bell Medal has been awarded to honor outstanding contributions in the field of telecommunications. In 1936, the US Patent Office declared Bell first on its list of the country's greatest inventors, leading to the US Post Office issuing a commemorative stamp honoring Bell in 1940 as part of its 'Famous Americans Series'. The First Day of Issue cer" +813,"photophone : les contributions indirectes de Graham Bell à l'idée de la vision à distance par l'électricité at the Histoire de la télévision * * Alexander Graham Bell and the Aerial Experiment Association Photograph Collection at The Museum of Flight (Seattle, Washington). ===Multimedia=== * Alexander Graham Bell at The Biography Channel * * * * * Shaping The Future, from the Heritage Minutes and Radio Minutes collection at HistoricaCanada.ca (1:31 audio drama, Adobe Flash required) Category:1847 births Category:1922 deaths Category:19th-century Scottish inventors Category:19th-century Canadian inventors Category:19th-century Canadian scientists Category:19th-century Scottish businesspeople Category:19th-century Scottish scientists Category:20th-century American inventors Category:20th-century American scientists Category:20th-century Canadian scientists Category:Alumni of the University of Edinburgh Category:Alumni of University College London Category:American agnostics Category:Amer" +814,"Anatolia (), also known as Asia Minor, is a peninsula of Turkey situated in Western Asia. It is the westernmost protrusion of the Asian continent, and constitutes the majority of contemporary territory of Turkey. Geographically, Anatolia is bounded by the Mediterranean Sea to the south, the Aegean Sea to the west, the Turkish Straits to the northwest, and the Black Sea to the north. The eastern and southeastern limits has either been expanded to the entirety of Asiatic Turkey, or to an imprecise line from the Black Sea to Gulf of Iskenderun. Topographically, the Sea of Marmara connects the Black Sea with the Aegean Sea through the Bosporus strait and the Dardanelles strait, and separates Anatolia from Thrace in the Balkan peninsula of Southeastern Europe. Anatolia was an early centre for the development of farming during the Neolithic period following its origination in the adjacent Fertile Crescent. Beginning around 9,000 years ago there was major migration of Anatolian Neolithic Farm" +815,"is called Anatolikón or Theme of the Anatolics, not because it is above and in the direction of the east where the sun rises, but because it lies to the East of Byzantium and Europe. Constantine VII Porphyrogenitus, De Thematibus, ed. A. Pertusi. Vatican: Vatican Library, 1952, pp. 59 ff.John Haldon, Byzantium, a History, 2002, p. 32. The Latinized form , with its -ia ending, is probably a Medieval Latin innovation. The modern Turkish form derives directly from the Greek name (Anatolḗ). The Russian male name Anatoly, the French Anatole and plain Anatol, all stemming from saints Anatolius of Laodicea (d. 283) and Anatolius of Constantinople (d. 458; the first Patriarch of Constantinople), share the same linguistic origin. == Names == The oldest known name for any region within Anatolia is related to its central area, known as the Land of Hatti – a designation that was initially used for the land of ancient Hattians, but later became the most common name for the entire territory under th" +816,"17th century BCE. They were speakers of an Indo-European language, the Hittite language, or nesili (the language of Nesa) in Hittite. The Hittites originated from local ancient cultures that grew in Anatolia, in addition to the arrival of Indo-European languages. Attested for the first time in the Assyrian tablets of Nesa around 2000 BCE, they conquered Hattusa in the 18th century BCE, imposing themselves over Hattian- and Hurrian-speaking populations. According to the widely accepted Kurgan theory on the Proto-Indo-European homeland, however, the Hittites (along with the other Indo-European ancient Anatolians) were themselves relatively recent immigrants to Anatolia from the north. However, they did not necessarily displace the population genetically; they assimilated into the former peoples' culture, preserving the Hittite language. The Hittites adopted the Mesopotamian cuneiform script. In the Late Bronze Age, Hittite New Kingdom () was founded, becoming an empire in the 14th centur" +817,"the temporary possession of a monk from Galatia by Satan and rendered speechless, but when he recovered he spoke only in his native Galatian when questioned: 'If he were pressed, he spoke only in Galatian'.180 After this, the rest is silence, and further archaeological or literary discoveries are awaited to see if Galatian survived any later. In this regard, the example of Crimean Gothic is instructive. It was presumed to have died out in the fifth century CE, but the discovery of a small corpus of the language dating from the sixteenth century altered this perception. Cappadocian in the homonymous region,J. Eric Cooper, Michael J. Decker, Life and Society in Byzantine Cappadocia , p. 14 Armenian in the east, and Kartvelian languages in the northeast. Anatolia is known as the birthplace of minted coinage (as opposed to unminted coinage, which first appears in Mesopotamia at a much earlier date) as a medium of exchange, some time in the 7th century BCE in Lydia. The use of minted coins" +818,"p. 122 The Beyliks did not mint coins in the names of their own leaders while they remained under the suzerainty of the Mongol Ilkhanids.Osman's Dream: The History of the Ottoman Empire, p. 13 The Osmanli ruler Osman I was the first Turkish ruler who minted coins in his own name in 1320s; they bear the legend Minted by Osman son of Ertugrul .Artuk – Osmanli Beyliginin Kurucusu, 27f Since the minting of coins was a prerogative accorded in Islamic practice only to a sovereign, it can be considered that the Osmanli, or Ottoman Turks, had become formally independent from the Mongol Khans.Pamuk – A Monetary History, pp. 30–31 ===Ottoman Empire=== Among the Turkish leaders, the Ottomans emerged as great power under Osman I and his son Orhan. The Anatolian beyliks were successively absorbed into the rising Ottoman Empire during the 15th century. It is not well understood how the Osmanlı, or Ottoman Turks, came to dominate their neighbours, as the history of medieval Anatolia is still little k" +819,"Olive (Olea europaea), Strawberry Tree (Arbutus unedo), Arbutus andrachne, Kermes Oak (Quercus coccifera), and Bay Laurel (Laurus nobilis). * Southern Anatolian montane conifer and deciduous forests: These mountain forests occupy the Mediterranean-climate Taurus Mountains of southern Anatolia. Conifer forests are predominant, chiefly Anatolian black pine (Pinus nigra), Cedar of Lebanon (Cedrus libani), Taurus fir (Abies cilicica), and juniper (Juniperus foetidissima and J. excelsa). Broadleaf trees include oaks, hornbeam, and maples. * Eastern Mediterranean conifer-sclerophyllous-broadleaf forests: This ecoregion occupies the coastal strip of southern Anatolia between the Taurus Mountains and the Mediterranean Sea. Plant communities include broadleaf sclerophyllous maquis shrublands, forests of Aleppo Pine (Pinus halepensis) and Turkish Pine (Pinus brutia), and dry oak (Quercus spp.) woodlands and steppes. ==Demographics== The largest cities in Anatolia (aside from the Asian side of Is" +820,"Apple Inc. is an American multinational corporation and technology company headquartered and incorporated in Cupertino, California, in Silicon Valley. It is best known for its consumer electronics, software, and services. Founded in 1976 as Apple Computer Company by Steve Jobs, Steve Wozniak and Ronald Wayne, the company was incorporated by Jobs and Wozniak as Apple Computer, Inc. the following year. It was renamed Apple Inc. in 2007 as the company had expanded its focus from computers to consumer electronics. Apple is the largest technology company by revenue, with billion in 2023. The company was founded to produce and market Wozniak's Apple I personal computer. Its second computer, the Apple II, became a best seller as one of the first mass-produced microcomputers. Apple introduced the Lisa in 1983 and the Macintosh in 1984, as some of the first computers to use a graphical user interface and a mouse. By 1985, internal company problems led to Jobs leaving to form NeXT, Inc., and Woz" +821,"preferential rate. This visit influenced Jobs to implement a GUI in Apple's products, starting with the Apple Lisa. Despite being pioneering as a mass-marketed GUI computer, the Lisa suffered from high costs and limited software options, leading to commercial failure. Jobs, angered by being pushed off the Lisa team, took over the company's Macintosh division. Wozniak and Raskin had envisioned the Macintosh as a low-cost computer with a text-based interface like the Apple II, but a plane crash in 1981 forced Wozniak to step back from the project. Jobs quickly redefined the Macintosh as a graphical system that would be cheaper than the Lisa, undercutting his former division. Jobs was also hostile to the Apple II division, which at the time, generated most of the company's revenue. In 1984, Apple launched the Macintosh, the first personal computer without a bundled programming language. Its debut was signified by 1984 , a million television advertisement directed by Ridley Scott that aire" +822,"developers to create applications for Macintosh rather than Apple II, and authorized salespersons to redirect consumers from Apple II and toward Macintosh. The Apple IIe was discontinued in 1993. Apple experimented with several other unsuccessful consumer targeted products during the 1990s, including QuickTake digital cameras, PowerCD portable CD audio players, speakers, the Pippin video game console, the eWorld online service, and Apple Interactive Television Box. Enormous resources were invested in the problematic Newton tablet division, based on John Sculley's unrealistic market forecasts. Throughout this period, Microsoft continued to gain market share with Windows by focusing on delivering software to inexpensive personal computers, while Apple was delivering a richly engineered but expensive experience. Apple relied on high profit margins and never developed a clear response; it sued Microsoft for making a GUI similar to the Lisa in Apple Computer, Inc. v. Microsoft Corp.Hormby," +823,"in April 2000, which had developed the DVD authoring software DVDirector, which Apple repackaged as the professional-oriented DVD Studio Pro, and reused its technology to create iDVD for the consumer market. In 2000, Apple purchased the SoundJam MP audio player software from Casady & Greene. Apple renamed the program iTunes, and simplified the user interface and added CD burning. In 2001, Apple changed course with three announcements. First, on March 24, 2001, Apple announced the release of a new modern operating system, Mac OS X. This was after numerous failed attempts in the early 1990s, and several years of development. Mac OS X is based on NeXTSTEP, OpenStep, and BSD Unix, to combine the stability, reliability, and security of Unix with the ease of use of an overhauled user interface. Second, in May 2001, the first two Apple Store retail locations opened in Virginia and California, offering an improved presentation of the company's products. At the time, many speculated that the st" +824,"as the iPhone, and all iPhone apps were compatible with the iPad. This gave the iPad a large app catalog on launch, though having very little development time before the release. Later that year on April 3, 2010, the iPad was launched in the U.S. It sold more than 300,000 units on its first day, and 500,000 by the end of the first week. In May 2010, Apple's market cap exceeded that of competitor Microsoft for the first time since 1989. In June 2010, Apple released the iPhone 4,; which introduced video calling using FaceTime, multitasking, and a new design with an exposed stainless steel frame as the phone's antenna system. Later that year, Apple again refreshed the iPod line by introducing a multi-touch iPod Nano, an iPod Touch with FaceTime, and an iPod Shuffle that brought back the clickwheel buttons of earlier generations.; ; It also introduced the smaller, cheaper second-generation Apple TV which allowed the rental of movies and shows. On January 17, 2011, Jobs announced in an inte" +825,"and Oprah Winfrey to a multi-year content partnership.; Additional partnerships for original series include Sesame Workshop and DHX Media and its subsidiary Peanuts Worldwide, and a partnership with A24 to create original films.; ; During the Apple Special Event in September 2017, the AirPower wireless charger was announced alongside the iPhone X, iPhone 8, and Watch Series 3. The AirPower was intended to wirelessly charge multiple devices, simultaneously. Though initially set to release in early 2018, the AirPower would be canceled in March 2019, marking the first cancellation of a device under Cook's leadership.; ; On August 19, 2020, Apple's share price briefly topped $467.77, making it the first US company with a market capitalization of trillion. During its annual WWDC keynote speech on June 22, 2020, Apple announced it would move away from Intel processors, and the Mac would transition to processors developed in-house. The announcement was expected by industry analysts, and it ha" +826,"major smartphone platforms in the world, alongside Android. The iPhone has generated large profits for the company, and is credited with helping to make Apple one of the world's most valuable publicly traded companies. , the iPhone accounts for more than half of the company's revenue. === iPad === The iPad is Apple's line of tablets which run iPadOS. The first-generation iPad was announced on January 27, 2010. The iPad is mainly marketed for consuming multimedia, creating art, working on documents, videoconferencing, and playing games. The iPad lineup consists of several base iPad models, and the smaller iPad Mini, upgraded iPad Air, and high-end iPad Pro. Apple has consistently improved the iPad's performance, with the iPad Pro adopting the same M1 and M2 chips as the Mac; but the iPad still receives criticism for its limited OS. Apple has sold more than 500 million iPads, though sales peaked in 2013.; ; The iPad still remains the most popular tablet computer by sales , and accounted" +827,"product sales have placed Apple among the top-tier retail stores, with sales over $16 billion globally in 2011. Apple Stores underwent a period of significant redesign, beginning in May 2016. This redesign included physical changes to the Apple Stores, such as open spaces and re-branded rooms, and changes in function to facilitate interaction between consumers and professionals.; Many Apple Stores are located inside shopping malls, but Apple has built several stand-alone flagship stores in high-profile locations. It has been granted design patents and received architectural awards for its stores' designs and construction, specifically for its use of glass staircases and cubes. The success of Apple Stores have had significant influence over other consumer electronics retailers, who have lost traffic, control and profits due to a perceived higher quality of service and products at Apple Stores.; Due to the popularity of the brand, Apple receives a large number of job applications, many o" +828,"on older models and when asking Apple for comment, he was told by Apple that It is expected behavior that the Location Services icon appears in the status bar when Location Services is enabled. The icon appears for system services that do not have a switch in Settings. Apple later further clarified that this behavior was to ensure compliance with ultra-wideband regulations in specific countries, a technology Apple started implementing in iPhones starting with iPhone 11 Pro, and emphasized that the management of ultra wideband compliance and its use of location data is done entirely on the device and Apple is not collecting user location data. Will Strafach, an executive at security firm Guardian Firewall, confirmed the lack of evidence that location data was sent off to a remote server. Apple promised to add a new toggle for this feature and in later iOS revisions Apple provided users with the option to tap on the location services indicator in Control Center to see which specific serv" +829,"2011—was responsible for site selection, in-store service, and store layout, yet had no control of the inventory in his stores. This was done by Tim Cook, who had a background in supply-chain management. Apple is known for strictly enforcing accountability. Each project has a directly responsible individual or DRI in Apple jargon. Unlike other major U.S. companies, Apple provides a relatively simple compensation policy for executives that does not include perks enjoyed by other CEOs like country club fees or private use of company aircraft. The company typically grants stock options to executives every other year. In 2015, Apple had 110,000 full-time employees. This increased to 116,000 full-time employees the next year, a notable hiring decrease, largely due to its first revenue decline. Apple does not specify how many of its employees work in retail, though its 2014 SEC filing put the number at approximately half of its employee base. In September 2017, Apple announced that it had ov" +830,"Canaccord Genuity. In the first quarter of 2015, the company garnered 92% of all earnings. On April 30, 2017, The Wall Street Journal reported that Apple had cash reserves of $250 billion, officially confirmed by Apple as specifically $256.8 billion a few days later. , Apple was the largest publicly traded corporation in the world by market capitalization. On August 2, 2018, Apple became the first publicly traded U.S. company to reach a $1 trillion market value, and , is valued at just over $3.2 trillion. Apple was ranked No. 4 on the 2018 Fortune 500 rankings of the largest United States corporations by revenue. In July 2022, Apple reported an 11% decline in Q3 profits compared to 2021. Its revenue in the same period rose 2% year-on-year to $83 billion, though this figure was also lower than in 2021, where the increase was at 36%. The general downturn is reportedly caused by the slowing global economy and supply chain disruptions in China. That year, Apple was one of the largest corpo" +831,"In the arrangement, Apple has committed that through April 24, WWF will receive 100% of the proceeds from the applications participating in the App Store via both the purchases of any paid apps and the In-App Purchases. Apple and WWF's Apps for Earth campaign raised more than $8 million in total proceeds to support WWF's conservation work. WWF announced the results at WWDC 2016 in San Francisco.; ; During the COVID-19 pandemic, Apple's CEO Cook announced that the company will be donating millions of masks to health workers in the United States and Europe. On January 13, 2021, Apple announced a $100 million Racial Equity and Justice Initiative to help combat institutional racism worldwide after the 2020 murder of George Floyd.; In June 2023, Apple announced doubling this and then distributed more than $200 million to support organizations focused on education, economic growth, and criminal justice. Half is philanthropic grants and half is centered on equity. == Environment == === Apple" +832,"replacing the cold cathode fluorescent lamp (CCFL) backlit LCD displays in its computers with mercury-free LED-backlit LCD displays and arsenic-free glass, starting with the upgraded MacBook Pro.; Apple offers comprehensive and transparent information about the CO2e, emissions, materials, and electrical usage concerning every product they currently produce or have sold in the past (and which they have enough data needed to produce the report), in their portfolio on their homepage. Allowing consumers to make informed purchasing decisions on the products they offer for sale. In June 2009, Apple's iPhone 3GS was free of PVC, arsenic, and BFRs.; Since 2009, all Apple products have mercury-free LED-backlit LCD displays, arsenic-free glass, and non-PVC cables. All Apple products have EPEAT Gold status and beat the latest Energy Star guidelines in each product's respective regulatory category. In November 2011, Apple was featured in Greenpeace's Guide to Greener Electronics, which ranks elect" +833,"factories primarily located inside China, and, to a lesser extent, Foxconn plants in Brazil, and India. Apple workers around the globe have been involved in organizing since the 1990s. Apple unions are made up of retail, corporate, and outsourced workers. Apple employees have joined trade unions or formed works councils in Australia, France, Germany, Italy, Japan, the United Kingdom and the United States. In 2021, Apple Together, a solidarity union, sought to bring together the company's global worker organizations. The majority of industrial labor disputes (including union recognition) involving Apple occur indirectly through its suppliers and contractors, notably Foxconn plants in China and, to a lesser extent, in Brazil and India. == See also == * List of Apple Inc. media events * Outline of Apple Inc. == Notes == == References == === Bibliography === * * == Further reading == * * * * * * * * * * * * * * == External links == * * Category:1976 establishments in California Category:19" +834,"Aberdeenshire (; ) is one of the 32 council areas of Scotland. It takes its name from the historic county of Aberdeenshire, which had substantially different boundaries. The Aberdeenshire Council area includes all of the areas of the historic counties of Aberdeenshire and Kincardineshire except the area making up Aberdeen City Council area, as well as part of Banffshire. The historic county boundaries are still officially used for a few purposes, namely land registration and lieutenancy.Land Register Counties & Operational Dates Aberdeenshire Council is headquartered at Woodhill House in Aberdeen, making it the only Scottish council whose headquarters are located outside its jurisdiction. Aberdeen itself forms a different council area (Aberdeen City). Aberdeenshire borders onto Angus and Perth and Kinross to the south, Highland and Moray to the west and Aberdeen City to the east. Traditionally, it has depended economically on the primary sector (agriculture, fishing, and forestry) and" +835,"religion' > 'Ethnic Group' Ethnic Group Number % Number % Number % White: Total 225,243 99.28% 249,112 98.47% 256,017 97.08% White: Scottish 194,169 85.59% 207,936 82.20% 210,537 79.83% White: Other British 26,945 11.88% 31,158 12.32% 32,508 12.33% White: Irish 976 0.43% 1,162 0.46% 1,493 0.57% White: Gypsy/Traveller – – 175 0.07% 169 0.06% White: Polish – – 3,020 1.19% 3,998 1.52% White: Other 3,153 1.39% 5,661 2.24% 7,312 2.77% Asian, Asian Scottish or Asian British: Total 645 0.28% 2,037 0.81% 2,939 1.11% Asian, Asian Scottish or Asian British: Indian 166 0.07% 473 0.19% 783 0.30% Asian, Asian Scottish or Asian British: Pakistani 69 – 253 0.10% 343 0.13% Asian, Asian Scottish or Asian British: Bangladeshi 29 – 138 0.05% 106 – Asian, Asian Scottish or Asian British: Chinese 277 0.12% 424 0.17% 575 0.22% Asian, Asian Scottish or Asian British: Asian Other 104 0.05% 749 0.30% 1,135 0.43% Black, Black Scottish or Black British 14 – – – – – African: Total 85 – 490 0.19% 1,313 0.50% Afric" +836,"Grampians, therefore it has a generally dry climate for a maritime region, with portions of the coast receiving of moisture annually. Summers are mild, and winters are typically cold in Aberdeenshire; Coastal temperatures are moderated by the North Sea such that coastal areas are typically cooler in the summer and warmer in the winter than inland locations. Coastal areas are also subject to haar, or coastal fog. ==Notable residents== *John Skinner, (1721–1807) author, poet and ecclesiastic. *Hugh Mercer, (1726–1777), born in the manse of Pitsligo Kirk, near Rosehearty, brigadier general of the Continental Army. *Alexander Garden, (1730–1791), born in Birse, was a naturalist and physician who moved to North America in 1754. The gardenia flower is named in his honour. *John Kemp, (1763–1812), born in Auchlossan, was a noted educator at Columbia University who is said to have influenced DeWitt Clinton's opinions and policies. *George MacDonald (1824–1905), author, poet, and theologian bor" +837,"Aztlan Underground is a band from Los Angeles, California that combines Hip-Hop, Punk Rock, Jazz, and electronic music with Chicano and Native American themes, and indigenous instrumentation. They are often cited as progenitors of Chicano rap. == Background == The band traces its roots to the late-1980s hardcore scene in the Eastside of Los Angeles. They have played rapcore, with elements of punk, hip hop, rock, funk, jazz, indigenous music, and spoken word. Indigenous drums, flutes, and rattles are also commonly used in their music. Their lyrics often address the family and economic issues faced by the Chicano community, and they have been noted as activists for that community. As an example of the politically active and culturally important artists in Los Angeles in the 1990s, Aztlan Underground appeared on Culture Clash on Fox in 1993; and was part of Breaking Out, a concert on pay per view in 1998, The band was featured in the independent films Algun Dia and Frontierland in the 199" +838,"The American Civil War (April 12, 1861 – May 26, 1865; also known by other names) was a civil war in the United States between the Union ( the North ) and the Confederacy ( the South ), which was formed in 1861 by states that had seceded from the Union. The central conflict leading to war was a dispute over whether slavery should be permitted to expand into the western territories, leading to more slave states, or be prohibited from doing so, which many believed would place slavery on a course of ultimate extinction. Decades of controversy over slavery were brought to a head when Abraham Lincoln, who opposed slavery's expansion, won the 1860 presidential election. Seven Southern slave states responded to Lincoln's victory by seceding from the United States and forming the Confederacy. The Confederacy seized U.S. forts and other federal assets within their borders. The war began on April 12, 1861, when the Confederacy bombarded Fort Sumter in South Carolina. A wave of enthusiasm for war" +839,"of Lincoln, wanted slaves to be free was not simply that southerners wanted the opposite, but that they themselves cherished a conflicting value: they wanted the Constitution, which protected slavery, to be honored, and the Union, which was a fellowship with slaveholders, to be preserved. Thus they were committed to values that could not logically be reconciled. ===Lincoln's election=== Lincoln's election in November 1860 was the final trigger for secession. Southern leaders feared Lincoln would stop slavery's expansion and put it on a course toward extinction. However, Lincoln would not be inaugurated until March 4, 1861, which gave the South time to secede and prepare for war during the winter of 1860–61. According to Lincoln, the American people had shown they had been successful in establishing and administering a republic, but a third challenge faced the nation: maintaining a republic based on the people's vote, in the face of an attempt to destroy it. ==Outbreak of the war== ===S" +840,"forts in the Confederacy: Fort Monroe in Virginia, Fort Pickens, Fort Jefferson, and Fort Taylor in Florida, and Fort Sumter in South Carolina. ===Battle of Fort Sumter=== The American Civil War began on April 12, 1861, when Confederate forces opened fire on the Union-held Fort Sumter. Fort Sumter is located in the harbor of Charleston, South Carolina. Its status had been contentious for months. Outgoing President Buchanan had dithered in reinforcing its garrison, commanded by Major Robert Anderson. Anderson took matters into his own hands and on December 26, 1860, under the cover of darkness, sailed the garrison from the poorly placed Fort Moultrie to the stalwart island Fort Sumter. Anderson's actions catapulted him to hero status in the North. An attempt to resupply the fort on January 9, 1861, failed and nearly started the war then, but an informal truce held. On March 5, Lincoln was informed the fort was low on supplies. Fort Sumter proved a key challenge to Lincoln's administrati" +841,"=== Mobilization === As the Confederate states organized, the U.S. Army numbered 16,000, while Northern governors began mobilizing their militias. The Confederate Congress authorized up to 100,000 troops in February. By May, Jefferson Davis was pushing for another 100,000 soldiers for one year or the duration, and the U.S. Congress responded in kind. In the first year of the war, both sides had more volunteers than they could effectively train and equip. After the initial enthusiasm faded, relying on young men who came of age each year was not enough. Both sides enacted draft laws (conscription) to encourage or force volunteering, though relatively few were drafted. The Confederacy passed a draft law in April 1862 for men aged 18–35, with exemptions for overseers, government officials, and clergymen. The U.S. Congress followed in July, authorizing a militia draft within states that could not meet their quota with volunteers. European immigrants joined the Union Army in large numbers, i" +842,"nation at war with a stronger enemy and feared souring relations with the U.S. === Union blockade === By early 1861, General Winfield Scott had devised the Anaconda Plan to win the war with minimal bloodshed, calling for a blockade of the Confederacy to suffocate the South into surrender. Lincoln adopted parts of the plan but opted for a more active war strategy. In April 1861, Lincoln announced a blockade of all Southern ports; commercial ships could not get insurance, ending regular traffic. The South blundered by embargoing cotton exports before the blockade was fully effective; by the time they reversed this decision, it was too late. King Cotton was dead, as the South could export less than 10% of its cotton. The blockade shut down the ten Confederate seaports with railheads that moved almost all the cotton. By June 1861, warships were stationed off the principal Southern ports, and a year later nearly 300 ships were in service. ==== Blockade runners ==== The Confederates began th" +843,"to serious postwar disputes. However, public opinion against slavery in Britain created a political liability for politicians, where the anti-slavery movement was powerful.Richard Huzzeym, Freedom Burning: Anti-Slavery and Empire in Victorian Britain (2013). War loomed in late 1861 between the U.S. and Britain over the Trent affair, which began when U.S. Navy personnel boarded the British ship and seized two Confederate diplomats. However, London and Washington smoothed this over after Lincoln released the two men. Prince Albert left his deathbed to issue diplomatic instructions to Lord Lyons during the Trent affair. His request was honored, and, as a result, the British response to the U.S. was toned down, helping avert war. In 1862, the British government considered mediating between the Union and Confederacy, though such an offer would have risked war with the U.S. British Prime Minister Lord Palmerston reportedly read Uncle Tom's Cabin three times when deciding what his decision wo" +844,"as they successfully engaged three Union armies (52,000 men), including those of Nathaniel P. Banks and John C. Fremont, preventing them from reinforcing the Union offensive against Richmond. The swiftness of Jackson's men earned them the nickname of foot cavalry . Johnston halted McClellan's advance at the Battle of Seven Pines, but he was wounded in the battle, and Robert E. Lee assumed his position of command. Lee and top subordinates James Longstreet and Stonewall Jackson defeated McClellan in the Seven Days Battles and forced his retreat. The Northern Virginia Campaign, which included the Second Battle of Bull Run, ended in yet another victory for the South. McClellan resisted General-in-Chief Halleck's orders to send reinforcements to John Pope's Union Army of Virginia, which made it easier for Lee's Confederates to defeat twice the number of combined enemy troops. Emboldened by Second Bull Run, the Confederacy made its first invasion of the North with the Maryland Campaign. Lee" +845,"Missouri, and then Memphis, Tennessee. In April 1862, the Union Navy captured New Orleans. The key to the river was New Orleans, the South's largest port [and] greatest industrial center. Kennedy, p. 58. U.S. Naval forces under Farragut ran past Confederate defenses south of New Orleans. Confederate forces abandoned the city, giving the Union a critical anchor in the deep South, which allowed Union forces to move up the Mississippi. Memphis fell to Union forces on June 6, 1862, and became a key base for further advances south along the Mississippi. Only the fortress city of Vicksburg, Mississippi, prevented Union control of the entire river. Bragg's second invasion of Kentucky in the Confederate Heartland Offensive included initial successes such as Kirby Smith's triumph at the Battle of Richmond and the capture of the Kentucky capital of Frankfort on September 3, 1862. However, the campaign ended with a meaningless victory over Maj. Gen. Don Carlos Buell at the Battle of Perryville. B" +846,"Philip, which guarded the river approach to New Orleans from the south. While part of the fleet bombarded the forts, other vessels forced a break in the obstructions in the river and enabled the rest of the fleet to steam upriver to the city. A Union army force commanded by Major General Benjamin Butler landed near the forts and forced their surrender. Butler's controversial command of New Orleans earned him the nickname Beast . The following year, the Union Army of the Gulf commanded by Major General Nathaniel P. Banks laid siege to Port Hudson for nearly eight weeks, the longest siege in US military history. The Confederates attempted to defend with the Bayou Teche Campaign but surrendered after Vicksburg. These surrenders gave the Union control over the Mississippi. Several small skirmishes but no major battles were fought in Florida. The biggest was the Battle of Olustee in early 1864. ==Pacific Coast theater== The Pacific Coast theater refers to military operations on the Pacific" +847,"Grant chased Lee and got in front of him, so that when Lee's army reached the village of Appomattox Court House, they were surrounded. After an initial battle, Lee decided the fight was hopeless, and surrendered his Army of Northern Virginia to Grant on April 9, 1865, during a conference at the McLean House.William Marvel (2002) Lee's Last Retreat: The Flight to Appomattox, pp. 158–181. In an untraditional gesture and as a sign of Grant's respect and anticipation of peacefully restoring Confederate states to the Union, Lee was permitted to keep his sword and horse, Traveller. His men were paroled, and a chain of Confederate surrenders began. On April 14, 1865, Lincoln was shot by John Wilkes Booth, a Confederate sympathizer. Lincoln died early the next morning. Lincoln's vice president, Andrew Johnson, was unharmed, because his would-be assassin, George Atzerodt, lost his nerve, so Johnson was immediately sworn in as president. Meanwhile, Confederate forces across the South surrendered" +848,"Copperheads, who had wanted a negotiated peace with the Confederacy. Comparison of Union and Confederacy, 1860–1864 Year Union Confederacy Population 1860 22,100,000 (71%) 9,100,000 (29%) Population 1864 28,800,000 (90%) 3,000,000 (10%) At the beginning of 1865, the Confederacy controlled one third of its congressional districts, which were apportioned by population. The major slave populations found in Louisiana, Mississippi, Tennessee, and Alabama were effectively under Union control by the end of 1864. Free 1860 21,700,000 (98%) 5,600,000 (62%) Slave 1860 490,000 (2%) 3,550,000 (38%) Slave 1864 negligible 1,900,000 Soldiers 1860–64 2,100,000 (67%) 1,064,000 (33%) Railroad miles 1860 21,800 (71%) 8,800 (29%) Railroad miles 1864 29,100 (98%) negligible Manufactures 1860 90% 10% Manufactures 1864 98% 2% Arms production 1860 97% 3% Arms production 1864 98% 2% Cotton bales 1860 negligible 4,500,000 Cotton bales 1864 300,000 negligible Exports 1860 30% 70% Exports 1864 98% 2% Some scholar" +849,"figures of 360,000 army deaths for the Union and 260,000 for the Confederacy remained commonly cited, they are incomplete. In addition to many Confederate records being missing, partly as a result of Confederate widows not reporting deaths due to being ineligible for benefits, both armies only counted troops who died during their service and not the tens of thousands who died of wounds or diseases after being discharged. This often happened only days or weeks later. Francis Amasa Walker, superintendent of the 1870 census, used census and surgeon general data to estimate a minimum of 500,000 Union military deaths and 350,000 Confederate military deaths, a total of 850,000 soldiers. While Walker's estimates were originally dismissed because of the 1870 census's undercounting, it was later found that the census was only off by 6.5 percent and that the data Walker used would be roughly accurate. Losses were far higher than during the war with Mexico, which saw roughly 13,000 American death" +850,"told his cabinet about his proposed emancipation proclamation, which would apply to the states still in rebellion on January 1, 1863, Seward advised Lincoln to wait for a Union military victory before issuing it, as to do otherwise would seem like our last shriek on the retreat .Oates, Stephen B., Abraham Lincoln: The Man Behind the Myths, p. 106. Walter Stahr, however, writes, There are contemporary sources, however, that suggest others were involved in the decision to delay , and Stahr quotes them.Stahr, Walter, Stanton: Lincoln's War Secretary, New York: Simon & Schuster, 2017, p. 226. Lincoln laid the groundwork for public support in an open letter published in response to Horace Greeley's The Prayer of Twenty Millions ; the letter stated that Lincoln's goal was to save the Union, and that, if he freed the slaves, it would be as a means to that end.Lincoln's letter was published first in the Washington National Intelligencer on August 23, 1862. Holzer, Harold, Lincoln and the Power" +851,"the war had finally ended.C. Vann Woodward, Reunion and Reaction: The Compromise of 1877 and the End of Reconstruction (2nd ed. 1991). With the withdrawal of federal troops, however, whites retook control of every Southern legislature, and the Jim Crow era of disenfranchisement and legal segregation was ushered in. The war had a demonstrable impact on American politics. Many veterans on both sides were elected to political office, including five U.S. Presidents: Ulysses Grant, Rutherford B. Hayes, James A. Garfield, Benjamin Harrison, and William McKinley. ==Memory and historiography== The war is a central event in American collective memory. There are innumerable statues, commemorations, books, and archival collections. The memory includes the home front, military affairs, the treatment of soldiers, both living and dead, in the war's aftermath, depictions of the war in literature and art, evaluations of heroes and villains, and considerations of the moral and political lessons of the" +852,"the first action involving steam-powered ironclad warships in naval warfare history.Sondhaus, Naval Warfare 1815–1914 p. 77. Repeating firearms such as the Henry rifle, Spencer rifle, Colt revolving rifle, Triplett & Scott carbine and others, first appeared during the Civil War; they were a revolutionary invention that would soon replace muzzle-loading and single-shot firearms. The war saw the first appearances of rapid-firing weapons and machine guns such as the Agar gun and Gatling gun. ==In works of culture and art== The Civil War is one of the most studied events in American history, and the collection of cultural works around it is enormous. This section gives an abbreviated overview of the most notable works. ===Literature=== * When Lilacs Last in the Dooryard Bloom'd and O Captain! My Captain! (1865) by Walt Whitman, famous eulogies to Lincoln * Battle-Pieces and Aspects of the War (1866) poetry by Herman Melville * The Rise and Fall of the Confederate Government (1881) by Jeffe" +853,"Books, 2013) online * Wiley, Bell I. The Life of Johnny Reb and The Life of Billy Yank (1943 and 1951; reprint 1994), two standard scholarly histories combined; 960pp. online ==External links== * West Point Atlas of Civil War Battles * Civil War photos at the National Archives * View images from the Civil War Photographs Collection at the Library of Congress * * American Civil World maps at the Persuasive Cartography, The PJ Mode Collection, Cornell University Library * Statements of each state as to why they were seceding, battlefields.org * National Park Service Civil War Places * Civil War Battlefield Places from the National Park Service * American Battlefield Trust – A non-profit land preservation and educational organization with two divisions, the Civil War Trust and the Revolutionary War Trust, dedicated to preserving America's battlefields through land acquisitions. * Civil War Era Digital Collection at Gettysburg College – This collection contains digital images of political" +854,"Andy Warhol (;Random House Webster's Unabridged Dictionary: Warhol born Andrew Warhola Jr.; August 6, 1928 – February 22, 1987) was an American visual artist, film director and producer. A leading figure in the pop art movement, Warhol is considered one of the most important American artists of the second half of the 20th century. His works explore the relationship between artistic expression, advertising, and celebrity culture that flourished by the 1960s, and span a variety of media, including painting, silkscreening, photography, film, and sculpture. Some of his best-known works include the silkscreen paintings Campbell's Soup Cans (1962) and Marilyn Diptych (1962), the experimental films Empire (1964) and Chelsea Girls (1966), and the multimedia events known as the Exploding Plastic Inevitable (1966–67). Born and raised in Pittsburgh, Warhol initially pursued a successful career as a commercial illustrator in the 1950s. After exhibiting his work in art galleries, he began to receiv" +855,"he organized first solo show at the Hugo Gallery in New York. Although that show was not well received, by 1956 Warhol was included in his first group exhibition at the Museum of Modern Art, New York. In 1956, Warhol traveled around the world with his friend, production designer Charles Lisanby, studying art and culture in several countries. Warhol's whimsical ink drawings of shoe advertisements figured in some of his earliest showings at the Bodley Gallery in New York in 1957.Bodley Gallery Warhol exhibition announcement . Retrieved March 24, 2011. Warhol habitually used the expedient of tracing photographs projected with an epidiascope. Using prints by Edward Wallowitch, his first boyfriend , the photographs would undergo a subtle transformation during Warhol's often cursory tracing of contours and hatching of shadows. Warhol used Wallowitch's photograph Young Man Smoking a Cigarette () for a 1958 design for a book cover he submitted to Simon and Schuster for the Walter Ross pulp nov" +856,"writer John Giorno and filmmaker Jack Smith, also appear in Warhol films (many premiering at the New Andy Warhol Garrick Theatre and 55th Street Playhouse) of the 1960s, revealing Warhol's connections to a diverse range of artistic scenes during this time. Less well known was his support and collaboration with several teenagers during this era, who would achieve prominence later in life, including writer David Dalton, photographer Stephen Shore and artist Bibbe Hansen (mother of pop musician Beck). In November 1964, Warhol's first Flowers series exhibited at the Leo Castelli Gallery in New York. In May 1965, his second Flowers series, which had more sizes and color variation that the previous, was shown at Galerie Ileana Sonnabend in Paris. During this trip Warhol announced that he was retiring from art to focus on film. In 1967, Warhol intended to present the film Chelsea Girls (1966) at the Cannes Film Festival, but it wasn't shown because the festival authorities explained that the" +857,"Leo Castelli Gallery, as well as art collector Peter Brant. In February 1974, some of the Mao portraits were installed at the Musée Galliera in Paris. Warhol and his longtime partner Jed Johnson got a dachshund, Archie (Bunker) Warhol, for Christmas in 1972. Warhol doted on Archie and took him everywhere: to the studio, parties, restaurants, and on trips to Europe. He created portraits of Archie, Johnson and Amos, a second dachshund they got a few years later. Warhol was visiting Europe more often in the early 1970s. He had a fondness for Paris, and by 1973 he had purchased an apartment on rue du Cherche-Midi on Paris' Left Bank. In 1974, Warhol and Johnson moved from his home on Lexington Avenue to a townhouse at 57 East 66th Street in Manhattan's Lenox Hill neighborhood. In May 1975, Warhol attended President Gerald Ford's state dinner in honor of the Shah of Iran, Mohammad Reza Pahlavi, at the White House. In 1975, Warhol published The Philosophy of Andy Warhol (From A to B & Back A" +858,"Referencing the many celebrity portraits produced by Warhol across his career, Orange Prince (1984) was created using a similar composition to the Marilyn Flavors series from 1962, among some of Warhol's first celebrity portraits. Prince is depicted in a pop color palette commonly used by Warhol, in bright orange with highlights of bright green and blue. The facial features and hair are screen-printed in black over the orange background. In September 1985, Warhol's joint exhibition with Basquiat, Paintings, opened to negative reviews at the Tony Shafrazi Gallery. That month, despite apprehension from Warhol, his silkscreen series Reigning Queens was shown at the Leo Castelli Gallery. In the Andy Warhol Diaries, Warhol noted: They were supposed to be only for Europe—nobody here cares about royalty and it'll be another bad review. In January 1987, Warhol traveled to Milan for the opening of his last exhibition, Last Supper, at the Palazzo delle Stelline. The next month, Warhol modeled wi" +859,"series. The Flavor Marilyns were selected from a group of fourteen canvases in the sub-series, each measuring 20 x 16 . Some of the canvases were named after various candy Life Savers flavors, including Cherry Marilyn, Lemon Marilyn and Licorice Marilyn. The others are identified by their background colors. Warhol produced both comic and serious works; his subject could be a soup can or an electric chair. Warhol used the same techniques—silkscreens, reproduced serially, and often painted with bright colors—whether he painted celebrities, everyday objects, or images of suicide, car crashes and disasters, as in the 1962–63 Death and Disaster series. In the 1970s, Warhol evolved into a commercial artist, painting mostly commissioned portraits of celebrities. In 1979, Warhol was commissioned to paint a BMW M1 Group 4 racing version for the fourth installment of the BMW Art Car project. He was initially asked to paint a BMW 320i in 1978, but the car model was changed and it didn't qualify f" +860,"under production as of 2013. Art galleries and dealers also market Sunday B Morning reprint versions of several other screenprint works including Flowers, Campbell's Soup I, Campbell's Soup Cans II,Gold Marilyn Monroe Mao and Dollare bill prints. Although the original Sunday B Morning versions had black stamps on the back, by the 1980s, they switched to blue. In 1970, Warhol's painting Campbell's Soup Can With Peeling Label (1962) sold for $60,000 at an auction by Parke-Bernet Galleries. At the time it was the high price ever paid at a public auction for a work by a living American artist. In the 1970s, the price of a commissioned portrait by Warhol was $25,000, two for $40,000. The value of Andy Warhol's work has been on an endless upward trajectory since his death in 1987. In 2014, his works accumulated $569 million at auction, which accounted for more than a sixth of the global art market. However, there have been some dips. According to art dealer Dominique Lévy: The Warhol trade m" +861,"the most brilliant mirror of our times , contending that Warhol had captured something irresistible about the zeitgeist of American culture in the 1970s. In addition to his paintings and drawings, Warhol directed and produced films, managed the Velvet Underground, and authored numerous books, as well as producing works in such diverse media as audio, photography, sculpture, theater, fashion and performance art. His ability to blur the lines between art, commerce, and everyday life was central to his creative philosophy. ===Filmography=== Warhol attended the 1962 premiere of the static composition by La Monte Young called Trio for Strings and subsequently created his famous series of static films. Filmmaker Jonas Mekas, who accompanied Warhol to the Trio premiere, claims Warhol's static films were directly inspired by the performance. Between 1963 and 1968, he made more than 60 films, plus some 500 short black-and-white screen test portraits of Factory visitors.Schaffner (1999), p. 73." +862,"1950s, Warhol produced several unbound portfolios of his work. The first of several bound self-published books by Warhol was 25 Cats Name Sam and One Blue Pussy, printed in 1954 by Seymour Berlin on Arches brand watermarked paper using his blotted line technique for the lithographs. The original edition was limited to 190 numbered, hand-colored copies, using Dr. Martin's ink washes. Most of these were given by Warhol as gifts to clients and friends. Copy No. 4, inscribed Jerry on the front cover and given to Geraldine Stutz, was used for a facsimile printing in 1987, and the original was auctioned in May 2006 for US$35,000 by Doyle New York.May 3, 2006, auction at Doyle New York . Retrieved August 14, 2006. Other self-published books by Warhol include: * A Gold Book * Wild Raspberries * Holy Cats Warhol's book A La Recherche du Shoe Perdu (1955) marked his transition from commercial to gallery artist .Smith, John W., Pamela Allara, and Andy Warhol. Possession Obsession: Andy Warhol and" +863,"more on presence than on words .George Walden, Who's a Dandy?—Dandyism and Beau Brummell, London: Gibson Square, 2002. . Reviewed by Frances Wilson in Uncommon People , The Guardian, October 12, 2006. * Performance Art: Warhol and his friends staged theatrical multimedia happenings at parties and public venues, combining music, film, slide projections and even Gerard Malanga in an S&M outfit cracking a whip. The Exploding Plastic Inevitable in 1966 was the culmination of this area of his work. * Theater: Warhol's play Andy Warhol's Pork opened on May 5, 1971, at LaMama theater in New York for a two-week run and was brought to the Roundhouse in London for a longer run in August 1971. Pork was based on tape-recorded conversations between Brigid Berlin and Andy during which Brigid would play for Andy tapes she had made of phone conversations between herself and her mother, socialite Honey Berlin. The play featured Jayne County as Vulva and Cherry Vanilla as Amanda Pork . In 1974, Andy War" +864,"inner circles of the New York art world. ===Religion=== Warhol was a practicing Ruthenian Catholic. He regularly volunteered at homeless shelters in New York City, particularly during the busier times of the year, and described himself as a religious person. Many of Warhol's later works depicted religious subjects, including two series, Details of Renaissance Paintings (1984) and The Last Supper (1986). In addition, a body of religious-themed works was found posthumously in his estate. Warhol regularly attended Mass, and the priest at Warhol's church, Saint Vincent Ferrer, said that the artist went there almost daily, although he was not observed taking Communion or going to Confession and sat or knelt in the pews at the back. The priest thought he was afraid of being recognized; Warhol said he was self-conscious about being seen in a Latin Catholic church crossing himself in the Orthodox way (right to left instead of the reverse). Warhol's art is noticeably influenced by the Eastern C" +865,"V were released in 2019. The subsequent volumes are still in the process of being compiled. The Foundation remains one of the largest grant-giving organizations for the visual arts in the US. Many of Warhol's works and possessions are on display at the Andy Warhol Museum in Pittsburgh. The foundation donated more than 3,000 works of art to the museum. ===Revelation in Brooklyn=== From November 19, 2021 – June 19, 2022, the Brooklyn Museum displayed the Andy Warhol: Revelation exhibition. Revelation examines themes such as life and death, power and desire, the role and representation of women, Renaissance imagery, family and immigrant traditions and rituals, depictions and duplications of Christ and the Catholic body and queer desire. Among the more than one hundred objects on view were rare source materials and newly discovered items that provide a fresh and intimate look at Warhol's creative process, as well as major paintings from his epic Last Supper series (1986), the experimental" +866,"Marina del Rey. In 1986, Warhol appeared in an ad for the Drexel Burnham Lambert investment group. Warhol appeared as a recurring character in TV series Vinyl, played by John Cameron Mitchell. Warhol was portrayed by Evan Peters in the American Horror Story: Cult episode Valerie Solanas Died for Your Sins: Scumbag . The episode depicts the attempted assassination of Warhol by Valerie Solanas (Lena Dunham). === Music === Warhol strongly influenced the new wave/punk rock band Devo, as well as David Bowie. Bowie recorded a song called Andy Warhol for his 1971 album Hunky Dory. Lou Reed wrote the song Andy's Chest , about Valerie Solanas, the woman who shot Warhol, in 1968. He recorded it with the Velvet Underground, and this version was released on the VU album in 1985. The band Triumph also wrote a song about Andy Warhol, Stranger In A Strange Land off their 1984 album Thunder Seven. ===Books=== Many books have been written about Warhol. In 1989, the biography The Life and Death of Andy" +867,"Alp Arslan born Muhammad Alp Arslan bin Dawud Chaghri, was the second sultan of the Seljuk Empire and great-grandson of Seljuk, the eponymous founder of the dynasty. He greatly expanded the Seljuk territory and consolidated his power, defeating rivals to the south, east and northwest, and his victory over the Byzantines at the Battle of Manzikert, in 1071, ushered in the Turkmen settlement of Anatolia. But the Battle of Manzikert opened Asia Minor to Turkmen conquest ==Early life== Historical sources differ about his actual birth date. His birth year, which some early sources of medieval period mentioned 1032 and 1033 in khorasan -iran 1 while later sources gave 1030. However, the most authentic considered as TDV Encyclopedia of Islam mentions, is that recorded by Ibn al-Athir, a medieval historian, as 1 Muharram 420 AH equivalent to 20 January 1029 CE. He was the son of Chaghri and nephew of Tughril, the founding sultans of the Seljuk Empire. His grandfather was Mikail, who in turn wa" +868,"and presented to Alp Arslan. It is reported that upon seeing the Roman emperor, the sultan leaped from his throne, commanded Romanos to kiss the ground, and stepped on his neck. He repeatedly berated the emperor, including for spurning his emissaries and offers of peace. Romanos remained unrepentant, asserting that he had merely done what was possible for a man, and which kings are bound to do, and I have fallen short in nothing. But God has fulfilled his will. And now, do what you wish and abandon recriminations. Carole Hillenbrand. (2007).Turkish Myth and Muslim Symbol: The Battle of Manzikert Oxford University Press Purportedly declaring Romanos too trivial... to kill , Arslan then led him about the camp to sell the prisoner to one of his men. The Seljuk soldiers initially refused to spend any money on buying the emperor, until one man traded a dog for him. Next, wishing to test Romanos, Alp Arslan asked Romanos what he would do if their situation were reversed and Arslan was impris" +869,"Another of his wives was Shah Khatun. She was the daughter of Qadir Khan Yusuf, and had been formerly married to Ghaznavid Mas'ud I. Another wife was Ummu Hifchaq also known as Ummu Qipchaq. Another of his wives was the daughter of King of Tashir Kiurike I, who was married to the sister of the Georgian king Bagrat IV. Alp Arslan divorced her, and married her to Nizam al-Mulk. His sons were Malik-Shah I, Tutush I, Arslan Shah, Tekish, Toghan-Shah, Ayaz and Buibars. One of his daughters married the son of Kurd Surkhab, son of Bard in 1068. Another daughter, Zulaikha Khatun, was married to a Muslim, son of Quraish in 1086–87. Another daughter, Aisha Khatun, married Shams al-Mulk Nasr, son of Ibrahim Khan Tamghach. Another daughter was married to Mas'ud III of Ghazni and was his first wife. Another daughter was Sara Khatun. == Legacy == Alp Arslan's conquest of Anatolia from the Byzantines is also seen as one of the pivotal precursors to the launch of the Crusades. From 2002 to July 2008 u" +870,"The American Film Institute (AFI) is an American nonprofit film organization that educates filmmakers and honors the heritage of the motion picture arts in the United States. AFI is supported by private funding and public membership fees. ==Leadership== The institute is composed of leaders from the film, entertainment, business, and academic communities. The board of trustees is chaired by Kathleen Kennedy and the board of directors chaired by Robert A. Daly guide the organization, which is led by President and CEO, film historian Bob Gazzale. Prior leaders were founding director George Stevens Jr. (from the organization's inception in 1967 until 1980) and Jean Picker Firstenberg (from 1980 to 2007). ==History== The American Film Institute was founded by a 1965 presidential mandate announced in the Rose Garden of the White House by Lyndon B. Johnson—to establish a national arts organization to preserve the legacy of American film heritage, educate the next generation of filmmakers, and" +871,"anticipated films at the festival, presenting selected feature-length movies from world-class filmmakers and artisans. Although it is a very restrictive selection, usually presenting between three and seven movies at most, many films selected by AFI for this section eventually also earn an Academy Award Best Picture nomination. Examples include Bradley Cooper's Maestro (2023), Steven Spielberg's The Fabelmans (2022), Will Smith's King Richard (2021), Jane Campion's The Power of the Dog (2021), Anthony Hopkins's The Father (2020), Noah Baumbach's Marriage Story (2019), Peter Farrelly's Green Book (2018), Luca Guadagnino's Call Me by Your Name (2017), Damien Chazelle's La La Land (2016), and Adam McKay's The Big Short (2015). ===AFI Docs=== Held annually in June, AFI Docs (formerly Silverdocs) is a documentary festival in Washington, D.C. The festival attracts over 27,000 documentary enthusiasts. ==AFI programs== ===AFI Catalog of Feature Films=== The AFI Catalog, started in 1968, is a w" +872,"was a Japanese filmmaker who directed 30 films in a career spanning over five decades. He is widely regarded as one of the greatest and most influential filmmakers in the history of cinema. Kurosawa displayed a bold, dynamic style, strongly influenced by Western cinema yet distinct from it; he was involved with all aspects of film production. Kurosawa entered the Japanese film industry in 1936, following a brief stint as a painter. After years of working on numerous films as an assistant director and scriptwriter, he made his debut as a director during World War II with the popular action film Sanshiro Sugata (1943). After the war, the critically acclaimed Drunken Angel (1948), in which Kurosawa cast the then little-known actor Toshiro Mifune in a starring role, cemented the director's reputation as one of the most important young filmmakers in Japan. The two men would go on to collaborate on another fifteen films. Rashomon (1950), which premiered in Tokyo, became the surprise winner o" +873,"Kurosawa worked under numerous directors, but by far the most important figure in his development was Yamamoto. Of his 24 films as A.D., he worked on 17 under Yamamoto, many of them comedies featuring the popular actor Ken'ichi Enomoto, known as Enoken . Yamamoto nurtured Kurosawa's talent, promoting him directly from third assistant director to chief assistant director after a year. Kurosawa's responsibilities increased, and he worked at tasks ranging from stage construction and film development to location scouting, script polishing, rehearsals, lighting, dubbing, editing, and second-unit directing. In the last of Kurosawa's films as an assistant director for Yamamoto, Horse (1941), Kurosawa took over most of the production, as his mentor was occupied with the shooting of another film. Yamamoto advised Kurosawa that a good director needed to master screenwriting. Kurosawa soon realized that the potential earnings from his scripts were much higher than what he was paid as an assistant" +874,"was the action-adventure thriller, Snow Trail, directed by Senkichi Taniguchi from Kurosawa's screenplay. It marked the debut of the intense young actor Toshiro Mifune. It was Kurosawa who, with his mentor Yamamoto, had intervened to persuade Toho to sign Mifune, during an audition in which the young man greatly impressed Kurosawa, but managed to alienate most of the other judges. thumb|left|upright|Takashi Shimura played a dedicated doctor helping an ailing gangster in Drunken Angel. Shimura performed in over 20 of Kurosawa's films.|alt=Publicity still of Shimura cleanly shaven and wearing glasses. Drunken Angel is often considered the director's first major work. Although the script, like all of Kurosawa's occupation-era works, had to go through rewrites due to American censorship, Kurosawa felt that this was the first film in which he was able to express himself freely. A gritty story of a doctor who tries to save a gangster (yakuza) with tuberculosis, it was also the first time tha" +875,"166 minutes, making the resulting narrative exceedingly difficult to follow. The severely edited film version is widely considered to be one of the director's least successful works and the original full-length version no longer exists. Contemporary reviews of the much shortened edited version were very negative, but the film was a moderate success at the box office, largely because of the popularity of one of its stars, Setsuko Hara. Meanwhile, unbeknownst to Kurosawa, Rashomon had been entered in the Venice Film Festival, due to the efforts of Giuliana Stramigioli, a Japan-based representative of an Italian film company, who had seen and admired the movie and convinced Daiei to submit it. On September 10, 1951, Rashomon was awarded the festival's highest prize, the Golden Lion, shocking not only Daiei but the international film world, which at the time was largely unaware of Japan's decades-old cinematic tradition. After Daiei briefly exhibited a subtitled print of the film in Los An" +876,"and close friend Fumio Hayasaka died (of tuberculosis) at the age of 41. The film's score was finished by Hayasaka's student, Masaru Sato, who would go on to score all of Kurosawa's next eight films. I Live in Fear opened in November 1955 to mixed reviews and muted audience reaction, becoming the first Kurosawa film to lose money during its original theatrical run. Today, it is considered by many to be among the finest films dealing with the psychological effects of the global nuclear stalemate. Kurosawa's next project, Throne of Blood, an adaptation of William Shakespeare's Macbeth—set, like Seven Samurai, in the Sengoku Era—represented an ambitious transposition of the English work into a Japanese context. Kurosawa instructed his leading actress, Isuzu Yamada, to regard the work as if it were a cinematic version of a Japanese rather than a European literary classic. Given Kurosawa's appreciation of traditional Japanese stage acting, the acting of the players, particularly Yamada, dra" +877,"films to be adapted from the work of the writer Shūgorō Yamamoto (the others would be Red Beard and Dodeskaden). It is lighter in tone and closer to a conventional period film than Yojimbo, though its story of a power struggle within a samurai clan is portrayed with strongly comic undertones. The film opened on January 1, 1962, quickly surpassing Yojimbos box office success and garnering positive reviews. Kurosawa had meanwhile instructed Toho to purchase the film rights to King's Ransom, a novel about a kidnapping written by American author and screenwriter Evan Hunter, under his pseudonym of Ed McBain, as one of his 87th Precinct series of crime books. The director intended to create a work condemning kidnapping, which he considered one of the very worst crimes. The suspense film, titled High and Low, was shot during the latter half of 1962 and released in March 1963. It broke Kurosawa's box office record (the third film in a row to do so), became the highest grossing Japanese film o" +878,"Zanuck, a more or less finalized cut screenplay was agreed upon in May 1968. Shooting began in early December, but Kurosawa would last only a little over three weeks as director. He struggled to work with an unfamiliar crew and the requirements of a Hollywood production, while his working methods puzzled his American producers, who ultimately concluded that the director must be mentally ill. Kurosawa was examined at Kyoto University Hospital by a neuropsychologist, Dr. Murakami, whose diagnosis was forwarded to Darryl Zanuck and Richard Zanuck at Fox studios indicating a diagnosis of neurasthenia stating that, He is suffering from disturbance of sleep, agitated with feelings of anxiety and in manic excitement caused by the above mentioned illness. It is necessary for him to have rest and medical treatment for more than two months. Hiroshi Tasogawa. All the Emperor's Men, Hardcover: 337 pp Publisher: Applause; 1 edition (2012). . p. 255. On Christmas Eve 1968, the Americans announced th" +879,"ads for Suntory whiskey, which aired in 1976. While fearing that he might never be able to make another film, the director nevertheless continued working on various projects, writing scripts and creating detailed illustrations, intending to leave behind a visual record of his plans in case he would never be able to film his stories. ==== Two epics (1978–1986) ==== In 1977, George Lucas released Star Wars, a wildly successful science fiction film influenced by Kurosawa's The Hidden Fortress. Lucas, like many other New Hollywood directors, revered Kurosawa and considered him a role model, and was shocked to discover that the Japanese film-maker was unable to secure financing for any new work. The two met in San Francisco in July 1978 to discuss the project Kurosawa considered most financially viable: , the epic story of a thief hired as the double of a medieval Japanese lord of a great clan. Lucas, enthralled by the screenplay and Kurosawa's illustrations, leveraged his influence over 20" +880,"as the greatest living filmmaker. Kurosawa now turned to a more conventional story with Rhapsody in August—the director's first film fully produced in Japan since Dodeskaden over twenty years before—which explored the scars of the nuclear bombing which destroyed Nagasaki at the very end of World War II. It was adapted from a Kiyoko Murata novel, but the film's references to the Nagasaki bombing came from the director rather than from the book. This was his only movie to include a role for an American movie star: Richard Gere, who plays a small role as the nephew of the elderly heroine. Shooting took place in early 1991, with the film opening on May 25 that year to a largely negative critical reaction, especially in the United States, where the director was accused of promulgating naïvely anti-American sentiments, though Kurosawa rejected these accusations. Kurosawa wasted no time moving onto his next project: Madadayo, or Not Yet. Based on autobiographical essays by Hyakken Uchida, the" +881,"in 2008. The AK100 Project aims to expose young people who are the representatives of the next generation, and all people everywhere, to the light and spirit of Akira Kurosawa and the wonderful world he created . === Reputation among filmmakers === Many filmmakers have been influenced by Kurosawa's work. Ingmar Bergman called his own film The Virgin Spring a touristic... lousy imitation of Kurosawa , and added, At that time my admiration for the Japanese cinema was at its height. I was almost a samurai myself! Federico Fellini considered Kurosawa to be the greatest living example of all that an author of the cinema should be . Steven Spielberg cited Kurosawa's cinematic vision as shaping his own. Satyajit Ray, who was posthumously awarded the Akira Kurosawa Award for Lifetime Achievement in Directing at the San Francisco International Film Festival in 1992, had said earlier of Rashomon: Roman Polanski considered Kurosawa to be among the three film-makers he favored most, along with Fel" +882,"Akira Kurosawa 100 Project before being reassigned in 2011 to the L.A. based company Splendent. Kurosawa Production works closely with the Akira Kurosawa Foundation, established in December 2003 and also run by Hisao Kurosawa. The foundation organizes an annual short film competition and spearheads Kurosawa-related projects, including a recently shelved one to build a memorial museum for the director. === Film studios === In 1981, the Kurosawa Film Studio was opened in Yokohama; two additional locations have since been launched in Japan. A large collection of archive material, including scanned screenplays, photos and news articles, has been made available through the Akira Kurosawa Digital Archive, a Japanese proprietary website maintained by Ryukoku University Digital Archives Research Center in collaboration with Kurosawa Production. ==== Anaheim University Akira Kurosawa School of Film ==== Anaheim University in collaboration with Kurosawa Production and the Kurosawa family establi" +883,"Ancient Egypt was a civilization of ancient Northeast Africa. It was concentrated along the lower reaches of the Nile River, situated within the contemporary territory of modern-day Egypt. Ancient Egyptian civilization followed prehistoric Egypt and coalesced around 3100BC (according to conventional Egyptian chronology) with the political unification of Upper and Lower Egypt under Menes (often identified with Narmer). The history of ancient Egypt unfolded as a series of stable kingdoms interspersed by periods of relative instability known as Intermediate Periods . The various kingdoms fall into one of three categories: the Old Kingdom of the Early Bronze Age, the Middle Kingdom of the Middle Bronze Age, or the New Kingdom of the Late Bronze Age. Ancient Egypt reached the pinnacle of its power during the New Kingdom, ruling much of Nubia and a sizable portion of the Levant. After this period, it entered an era of slow decline. During the course of its history, ancient Egypt was invaded" +884,"the early Sumerian-Akkadian civilization of Mesopotamia and of ancient Elam. The third-centuryBC Egyptian priest Manetho grouped the long line of kings from Menes to his own time into 30 dynasties, a system still used today. He began his official history with the king named Meni (or Menes in Greek), who was believed to have united the two kingdoms of Upper and Lower Egypt. The transition to a unified state happened more gradually than ancient Egyptian writers represented, and there is no contemporary record of Menes. Some scholars now believe, however, that the mythical Menes may have been the king Narmer, who is depicted wearing royal regalia on the ceremonial Narmer Palette, in a symbolic act of unification. In the Early Dynastic Period, which began about 3000BC, the first of the Dynastic kings solidified control over lower Egypt by establishing a capital at Memphis, from which he could control the labor force and agriculture of the fertile delta region, as well as the lucrative and" +885,"kings weakened, a Western Asian people called the Hyksos, who had already settled in the Delta, seized control of Egypt and established their capital at Avaris, forcing the former central government to retreat to Thebes. The king was treated as a vassal and expected to pay tribute. The Hyksos ('foreign rulers') retained Egyptian models of government and identified as kings, thereby integrating Egyptian elements into their culture. They and other invaders introduced new tools of warfare into Egypt, most notably the composite bow and the horse-drawn chariot. After retreating south, the native Theban kings found themselves trapped between the Canaanite Hyksos ruling the north and the Hyksos' Nubian allies, the Kushites, to the south. After years of vassalage, Thebes gathered enough strength to challenge the Hyksos in a conflict that lasted more than 30 years, until 1555BC. The kings Seqenenre Tao II and Kamose were ultimately able to defeat the Nubians to the south of Egypt, but failed to" +886,"Psamtik I was able to oust the Assyrians with the help of Greek mercenaries, who were recruited to form Egypt's first navy. Greek influence expanded greatly as the city-state of Naucratis became the home of Greeks in the Nile Delta. The Saite kings based in the new capital of Sais witnessed a brief but spirited resurgence in the economy and culture, but in 525BC, the powerful Persians, led by Cambyses II, began their conquest of Egypt, eventually capturing the pharaoh Psamtik III at the Battle of Pelusium. Cambyses II then assumed the formal title of pharaoh, but ruled Egypt from Iran, leaving Egypt under the control of a satrap. A few successful revolts against the Persians marked the 5th centuryBC, but Egypt was never able to permanently overthrow the Persians. Following its annexation by Persia, Egypt was joined with Cyprus and Phoenicia in the sixth satrapy of the Achaemenid Persian Empire. This first period of Persian rule over Egypt, also known as the Twenty-Seventh Dynasty, ende" +887,"for his jurisdiction. The temples formed the backbone of the economy. Not only were they places of worship, but were also responsible for collecting and storing the kingdom's wealth in a system of granaries and treasuries administered by overseers, who redistributed grain and goods. Much of the economy was centrally organized and strictly controlled. Although the ancient Egyptians did not use coinage until the Late period, they did use a type of money-barter system, with standard sacks of grain and the deben, a weight of roughly of copper or silver, forming a common denominator. Workers were paid in grain; a simple laborer might earn sacks (200 kg or 400 lb) of grain per month, while a foreman might earn sacks (250 kg or 550 lb). Prices were fixed across the country and recorded in lists to facilitate trading; for example a shirt cost five copper deben, while a cow cost 140deben. Grain could be traded for other goods, according to the fixed price list. During the fifth centuryBC coined" +888,"(flooding), Peret (planting), and Shemu (harvesting). The flooding season lasted from June to September, depositing on the river's banks a layer of mineral-rich silt ideal for growing crops. After the floodwaters had receded, the growing season lasted from October to February. Farmers plowed and planted seeds in the fields, which were irrigated with ditches and canals. Egypt received little rainfall, so farmers relied on the Nile to water their crops. From March to May, farmers used sickles to harvest their crops, which were then threshed with a flail to separate the straw from the grain. Winnowing removed the chaff from the grain, and the grain was then ground into flour, brewed to make beer, or stored for later use. The ancient Egyptians cultivated emmer and barley, and several other cereal grains, all of which were used to make the two main food staples of bread and beer. Flax plants, uprooted before they started flowering, were grown for the fibers of their stems. These fibers were" +889,"a critical source of quality timber not found in Egypt. By the Fifth Dynasty, trade with Punt provided gold, aromatic resins, ebony, ivory, and wild animals such as monkeys and baboons. Egypt relied on trade with Anatolia for essential quantities of tin as well as supplementary supplies of copper, both metals being necessary for the manufacture of bronze. The ancient Egyptians prized the blue stone lapis lazuli, which had to be imported from far-away Afghanistan. Egypt's Mediterranean trade partners also included Greece and Crete, which provided, among other goods, supplies of olive oil. ==Language== ===Historical development=== The Egyptian language is a northern Afro-Asiatic language closely related to the Berber and Semitic languages. It has the longest known history of any language having been written from BC to the Middle Ages and remaining as a spoken language for longer. The phases of ancient Egyptian are Old Egyptian, Middle Egyptian (Classical Egyptian), Late Egyptian, Demotic" +890,"in previous historical eras, when Egypt was an independent nation ruled by great pharaohs such as Ramesses II. ==Culture== ===Daily life=== Most ancient Egyptians were farmers tied to the land. Their dwellings were restricted to immediate family members, and were constructed of mudbrick designed to remain cool in the heat of the day. Each home had a kitchen with an open roof, which contained a grindstone for milling grain and a small oven for baking the bread. Ceramics served as household wares for the storage, preparation, transport, and consumption of food, drink, and raw materials. Walls were painted white and could be covered with dyed linen wall hangings. Floors were covered with reed mats, while wooden stools, beds raised from the floor and individual tables comprised the furniture. The ancient Egyptians placed a great value on hygiene and appearance. Most bathed in the Nile and used a pasty soap made from animal fat and chalk. Men shaved their entire bodies for cleanliness; perf" +891,"of figures with no indication of spatial depth—created a sense of order and balance within a composition. Images and text were intimately interwoven on tomb and temple walls, coffins, stelae, and even statues. The Narmer Palette, for example, displays figures that can also be read as hieroglyphs. Because of the rigid rules that governed its highly stylized and symbolic appearance, ancient Egyptian art served its political and religious purposes with precision and clarity. Ancient Egyptian artisans used stone as a medium for carving statues and fine reliefs, but used wood as a cheap and easily carved substitute. Paints were obtained from minerals such as iron ores (red and yellow ochres), copper ores (blue and green), soot or charcoal (black), and limestone (white). Paints could be mixed with gum arabic as a binder and pressed into cakes, which could be moistened with water when needed. Pharaohs used reliefs to record victories in battle, royal decrees, and religious scenes. Common citi" +892,"mummy cases. Actual preservation practices declined during the Ptolemaic and Roman eras, while greater emphasis was placed on the outer appearance of the mummy, which was decorated. Wealthy Egyptians were buried with larger quantities of luxury items, but all burials, regardless of social status, included goods for the deceased. Funerary texts were often included in the grave, and, beginning in the New Kingdom, so were shabti statues that were believed to perform manual labor for them in the afterlife. Rituals in which the deceased was magically re-animated accompanied burials. After burial, living relatives were expected to occasionally bring food to the tomb and recite prayers on behalf of the deceased. ==Military== The ancient Egyptian military was responsible for defending Egypt against foreign invasion, and for maintaining Egypt's domination in the ancient Near East. The military protected mining expeditions to the Sinai during the Old Kingdom and fought civil wars during the Firs" +893,"asthma symptoms. Ancient Egyptian surgeons stitched wounds, set broken bones, and amputated diseased limbs, but they recognized that some injuries were so serious that they could only make the patient comfortable until death occurred. ===Maritime technology=== Early Egyptians knew how to assemble planks of wood into a ship hull and had mastered advanced forms of shipbuilding as early as 3000BC. The Archaeological Institute of America reports that the oldest planked ships known are the Abydos boats. A group of 14 discovered ships in Abydos were constructed of wooden planks sewn together. Discovered by Egyptologist David O'Connor of New York University, woven straps were found to have been used to lash the planks together, and reeds or grass stuffed between the planks helped to seal the seams. Because the ships are all buried together and near a mortuary belonging to Pharaoh Khasekhemwy, originally they were all thought to have belonged to him, but one of the 14 ships dates to 3000BC, an" +894,"(New Kingdom to Late Period), and one individual to the Ptolemaic Period, spanning around 1300 years of Egyptian history. These results point to a genetic continuity of Ancient Egyptians with modern Egyptians. The results further point to a close genetic affinity between ancient Egyptians and Middle Eastern populations, especially ancient groups from the Levant. thumb|The preserved Temple of Horus at Edfu is a model of Egyptian architecture. Ancient Egyptians also displayed affinities to Nubians to the south of Egypt, in modern-day Sudan. Archaeological and historical evidence support interactions between Egyptian and Nubian populations more than 5000 years ago, with socio-political dynamics between Egyptians and Nubians ranging from peaceful coexistence to variably successful attempts of conquest. A study on sixty-six ancient Nubian individuals revealed significant contact with ancient Egyptians, characterized by the presence of % Neolithic/Bronze Age Levantine ancestry in these indiv" +895,"Analog Brothers were an experimental hip hop band featuring Tracy Ice-T Marrow (Ice Oscillator) on keyboards, drums and vocals, Keith Kool Keith Thornton (Keith Korg) on bass, strings and vocals, Marc Live (Marc Moog) on drums, violins and vocals, Christopher Black Silver Rodgers (Silver Synth) on synthesizer, lazar bell and vocals, and Rex Colonel Pimpin' Rex Doby Jr. (Rex Roland JX3P) on keyboards, vocals and production. ==Music== The group's only studio album Pimp to Eat featured guest appearances by various members of Rhyme Syndicate, Odd Oberheim, Jacky Jasper (who appears as Jacky Jasper on the song We Sleep Days and H-Bomb on War ), D.J. Cisco from S.M., Synth-A-Size Sisters and Teflon. ==Legacy== While the group only recorded one album together as the Analog Brothers, a few bootlegs of its live concert performances, including freestyles with original lyrics, have occasionally surfaced online. After Pimp to Eat, the Analog Brothers continued performing together in various line u" +896,"Motor neuron diseases or motor neurone diseases (MNDs) are a group of rare neurodegenerative disorders that selectively affect motor neurons, the cells which control voluntary muscles of the body. They include amyotrophic lateral sclerosis (ALS), progressive bulbar palsy (PBP), pseudobulbar palsy, progressive muscular atrophy (PMA), primary lateral sclerosis (PLS), spinal muscular atrophy (SMA) and monomelic amyotrophy (MMA), as well as some rarer variants resembling ALS. Motor neuron diseases affect both children and adults. While each motor neuron disease affects patients differently, they all cause movement-related symptoms, mainly muscle weakness. Most of these diseases seem to occur randomly without known causes, but some forms are inherited. Studies into these inherited forms have led to discoveries of various genes (e.g. SOD1) that are thought to be important in understanding how the disease occurs. Symptoms of motor neuron diseases can be first seen at birth or can come on slow" +897,"of a muscle, but damage to an UMN can be distinguished from damage to a LMN by physical exam. Type UMN degeneration LMN degeneration Sporadic MNDs Sporadic MNDs Sporadic MNDs Sporadic amyotrophic lateral sclerosis (ALS)* Yes Yes Primary lateral sclerosis (PLS)* Yes No Progressive muscular atrophy (PMA)* No Yes Progressive bulbar palsy (PBP)* Yes Yes, bulbar region Pseudobulbar palsy Yes, bulbar region No Monomelic amyotrophy (MMA) No Yes Inherited MNDs Inherited MNDs Inherited MNDs Familial amyotrophic lateral sclerosis (ALS)* Yes Yes === Tests === * Cerebrospinal fluid (CSF) tests: Analysis of the fluid from around the brain and spinal cord could reveal signs of an infection or inflammation. * Magnetic resonance imaging (MRI): An MRI of the brain and spinal cord is recommended in patients with UMN signs and symptoms to explore other causes, such as a tumor, inflammation, or lack of blood supply (stroke). * Electromyogram (EMG) & nerve conduction study (NCS): The EMG, which evaluates m" +898,"An abjad (, ), also abgad, is a writing system in which only consonants are represented, leaving the vowel sounds to be inferred by the reader. This contrasts with alphabets, which provide graphemes for both consonants and vowels. The term was introduced in 1990 by Peter T. Daniels.Daniels, P. (1990). Fundamentals of Grammatology. Journal of the American Oriental Society, 110(4), 727-731. doi:10.2307/602899: We must recognize that the West Semitic scripts constitute a third fundamental type of script, the kind that denotes individual consonants only. It cannot be subsumed under either of the other terms. A suitable name for this type would be alephbeth, in honor of its Levantine origin, but this term seems too similar to alphabet to be practical; so I propose to call this type an abjad, [Footnote: I.e., the alif-ba-jim order familiar from earlier Semitic alphabets, from which the modern order alif-ba-ta-tha is derived by placing together the letters with similar shapes and differing nu" +899,"vowel letters are also used to write certain consonants, particularly approximants that sound similar to long vowels. A pure abjad is exemplified (perhaps) by very early forms of ancient Phoenician, though at some point (at least by the 9th century BC) it and most of the contemporary Semitic abjads had begun to overload a few of the consonant symbols with a secondary function as vowel markers, called matres lectionis. This practice was at first rare and limited in scope but became increasingly common and more developed in later times. ===Addition of vowels=== In the 9th century BC the Greeks adapted the Phoenician script for use in their own language. The phonetic structure of the Greek language created too many ambiguities when vowels went unrepresented, so the script was modified. They did not need letters for the guttural sounds represented by aleph, he, heth or ayin, so these symbols were assigned vocalic values. The letters waw and yod were also adapted into vowel signs; along wit" +900,"An abugida (; from Ge'ez: , )sometimes also called alphasyllabary, neosyllabary, or pseudo-alphabetis a segmental writing system in which consonant–vowel sequences are written as units; each unit is based on a consonant letter, and vowel notation is secondary, similar to a diacritical mark. This contrasts with a full alphabet, in which vowels have status equal to consonants, and with an abjad, in which vowel marking is absent, partial, or optional – in less formal contexts, all three types of the script may be termed alphabets . The terms also contrast them with a syllabary, in which a single symbol denotes the combination of one consonant and one vowel. Related concepts were introduced independently in 1948 by James Germain Février (using the term ) and David Diringer (using the term semisyllabary), then in 1959 by Fred Householder (introducing the term pseudo-alphabet).Householder, F. (1959). Review of The Decipherment of Linear B by John Chadwick, The Classical Journal, 54(8), 379–8" +901,"abjad is derived from the Arabic a b j d, and alphabet is derived from the names of the two first letters in the Greek alphabet, alpha and beta. Abugida as a term in linguistics was proposed by Peter T. Daniels in his 1990 typology of writing systems. ==Terminology== As Daniels used the word, an abugida is in contrast with a syllabary, where letters with shared consonant or vowel sounds show no particular resemblance to one another. Furthermore, an abugida is also in contrast with an alphabet proper, where independent letters are used to denote both consonants and vowels. The term alphasyllabary was suggested for the Indic scripts in 1997 by William Bright, following South Asian linguistic usage, to convey the idea that, they share features of both alphabet and syllabary. He describes this term as formal, i.e., more concerned with the graphic arrangement of symbols, whereas abugida was functional, putting the focus on sound–symbol correspondence. However, this is not a distinction made" +902,"as in Khmer and Tai Tham. *by a special dependent consonant sign, which may be a smaller or differently placed version of the full consonant letter, or may be a distinct sign altogether. *not at all. For example, repeated consonants need not be represented, homorganic nasals may be ignored, and in Baybayin and Makasar script, the syllable-final consonant was traditionally never represented. More complicated unit structures (e.g. CC or CCVC) are handled by combining the various techniques above. Examples using the Devanagari script * K = /ka/ = * Ki = /ki/ = * K* = /k/ = (with a Halant under the character) * K*M = /kma/ = * İK = /ika/ = * İK* = /ik/ = * İKi = /iki/ = * etc. ==Family-specific features== There are three principal families of abugidas, depending on whether vowels are indicated by modifying consonants by diacritics, distortion, or orientation.John D. Berry (2002:19) Language Culture Type *The oldest and largest is the Brahmic family of India and Southeast Asia, in which vow" +903,"by फ, rather than forming a conjunct. This expedient is used by ISCII and South Asian scripts of Unicode.) Thus a closed syllable such as phaṣ requires two aksharas to write: फष् phaṣ. The Róng script used for the Lepcha language goes further than other Indic abugidas, in that a single akshara can represent a closed syllable: Not only the vowel, but any final consonant is indicated by a diacritic. For example, the syllable [sok] would be written as something like s̥̽, here with an underring representing and an overcross representing the diacritic for final . Most other Indic abugidas can only indicate a very limited set of final consonants with diacritics, such as or , if they can indicate any at all. ===Ethiopic=== In Ethiopic or Ge'ez script, fidels (individual letters of the script) have diacritics that are fused with the consonants to the point that they must be considered modifications of the form of the letters. Children learn each modification separately, as in a syllabary; none" +904,"each syllable or consonant-vowel combination, and where these have no systematic similarity to each other, and typically develop directly from logographic scripts. Compare the examples above to sets of syllables in the Japanese hiragana syllabary: か ka, き ki, く ku, け ke, こ ko have nothing in common to indicate k; while ら ra, り ri, る ru, れ re, ろ ro have neither anything in common for r, nor anything to indicate that they have the same vowels as the k set. Most Indian and Indochinese abugidas appear to have first been developed from abjads with the Kharoṣṭhī and Brāhmī scripts; the abjad in question is usually considered to be the Aramaic one, but while the link between Aramaic and Kharosthi is more or less undisputed, this is not the case with Brahmi. The Kharosthi family does not survive today, but Brahmi's descendants include most of the modern scripts of South and Southeast Asia. Ge'ez derived from a different abjad, the Sabean script of Yemen; the advent of vowels coincided with the" +905,"ABBA ( , ) were a Swedish pop group formed in Stockholm in 1972 by Agnetha Fältskog, Björn Ulvaeus, Benny Andersson, and Anni-Frid Lyngstad. They are one of the most popular and successful musical groups of all time, and are one of the best-selling music acts in the history of popular music. In , ABBA were 's first winner of the Eurovision Song Contest with the song Waterloo , which in 2005 was chosen as the best song in the competition's history as part of the 50th anniversary celebration of the contest. During the band's main active years, it consisted of two married couples: Fältskog and Ulvaeus, and Lyngstad and Andersson. With the increase of their popularity, their personal lives suffered, which eventually resulted in the collapse of both marriages. The relationship changes were reflected in the group's music, with later songs featuring darker and more introspective lyrics. After ABBA disbanded in December 1982, Andersson and Ulvaeus continued their success writing music for mult" +906,"Virginmedia.com. Retrieved 19 April 2014. They also set up Hep House, their equivalent of Apple Corps. Andersson played the keyboard and eventually started writing original songs for his band, many of which became major hits, including No Response , which hit number three in 1965, and Sunny Girl , Wedding , and Consolation , all of which hit number one in 1966. Andersson also had a fruitful songwriting collaboration with Lasse Berghagen, with whom he wrote his first Svensktoppen entry, Sagan om lilla Sofie ( The tale of Little Sophie ) in 1968. Andersson wrote and submitted the song Hej, Clown for Melodifestivalen 1969, the national festival to select the Swedish entry to the Eurovision Song Contest. The song tied for first place, but re-voting relegated Andersson's song to second place. On that occasion Andersson briefly met his future spouse, singer Anni-Frid Lyngstad, who also participated in the contest. A month later, the two had become a couple. As their respective bands began to" +907,"Young ), with more prominent vocals by Fältskog and Lyngstad–and moderate chart success. Fältskog and Ulvaeus, now married, started performing together with Andersson on a regular basis at the Swedish folkparks in the middle of 1971. Stig Anderson, founder and owner of Polar Music, was determined to break into the mainstream international market with music by Andersson and Ulvaeus. One day the pair of you will write a song that becomes a worldwide hit, he predicted. Stig Anderson encouraged Ulvaeus and Andersson to write a song for Melodifestivalen, and after two rejected entries in 1971, Andersson and Ulvaeus submitted their new song Säg det med en sång ( Say It with a Song ) for the 1972 contest, choosing newcomer Lena Anderson to perform. The song came in third place, encouraging Stig Anderson, and became a hit in Sweden. The first signs of foreign success came as a surprise, as the Andersson and Ulvaeus single She's My Kind of Girl was released through Epic Records in Japan in Marc" +908,"returned to Stockholm. Since then, the original logo has been reinstated on all official products.ABBA Logo 25th Anniversary Retrieved from Internet Archive 10 January 2014. === 1973–1976: breakthrough === ==== Eurovision Song Contest 1974 ==== ABBA entered the Melodifestivalen with Ring Ring but did not qualify as the 1973 Swedish entry. Stig Anderson started planning for the 1974 contest. Ulvaeus, Andersson and Stig Anderson saw possibilities in using the Eurovision Song Contest to make the music business aware of them as songwriters, as well as to publicise the band. In late 1973 they were invited by Swedish television to contribute a song for the Melodifestivalen 1974, and the upbeat song Waterloo was chosen. The group were now inspired by the growing glam rock scene in England. With this third attempt, ABBA were more experienced and better prepared for the Eurovision Song Contest, and they won the nation's hearts on Swedish television on 9 February 1974. Winning the 1974 Eurovisio" +909,"voiced, by Creem in particular, that in the US ABBA had endured a very sloppy promotional campaign . Nevertheless, the group enjoyed warm reviews from the American press. Cashbox went as far as saying that there is a recurrent thread of taste and artistry inherent in Abba's marketing, creativity and presentation that makes it almost embarrassing to critique their efforts , while Creem wrote: SOS is surrounded on this LP by so many good tunes that the mind boggles. In Australia, the airing of the music videos for I Do, I Do, I Do, I Do, I Do and Mamma Mia on the nationally broadcast TV pop show Countdown (which premiered in November 1974) saw the band rapidly gain enormous popularity, and Countdown become a key promoter of the group via their distinctive music videos. This started an immense interest for ABBA in Australia, resulting in I Do, I Do, I Do, I Do, I Do staying at number one for three weeks, then SOS spending a week there, followed by Mamma Mia staying there for ten weeks, an" +910,"available only by mail application and it was later revealed that the box-office received 3.5 million requests for tickets, enough to fill the venue 580 times. Along with praise ( ABBA turn out to be amazingly successful at reproducing their records , wrote Creem), there were complaints that ABBA performed slickly...but with a zero personality coming across from a total of 16 people on stage (Melody Maker). One of the Royal Albert Hall concerts was filmed as a reference for the filming of the Australian tour for what became ABBA: The Movie, though it is not exactly known how much of the concert was filmed. After the European leg of the tour, in March 1977, ABBA played 11 dates in Australia before a total of 160,000 people. The opening concert in Sydney at the Sydney Showground on 3 March to an audience of 20,000 was marred by torrential rain with Lyngstad slipping on the wet stage during the concert. However, all four members would later recall this concert as the most memorable of the" +911,"January 1979, the group performed Chiquitita at the Music for UNICEF Concert held at the United Nations General Assembly to celebrate UNICEF's Year of the Child. ABBA donated the copyright of this worldwide hit to the UNICEF; see Music for UNICEF Concert. Bee Gees, Olivia were a hit with the VIPs (11 January 1979). Courier Mail (Brisbane); p. 30. The single was released the following week, and reached number-one in ten countries. ==== North American and European tours ==== In mid-January 1979, Ulvaeus and Fältskog announced they were getting divorced. The news caused interest from the media and led to speculation about the band's future. ABBA assured the press and their fan base they were continuing their work as a group and that the divorce would not affect them. ABBA divorce – Agnetha moves out (17 January 1979). The Sun (Sydney); p. 1. Nonetheless, the media continued to confront them with this in interviews. To escape the media swirl and concentrate on their writing, Andersson and" +912,"but has stated they were not about his own divorce; Fältskog has repeatedly stated she was not the loser in their divorce. In the United States, the single peaked at number-eight on the Billboard Hot 100 chart and became ABBA's second Billboard Adult Contemporary number-one. It was also re-recorded by Andersson and Ulvaeus with a slightly different backing track, by French chanteuse Mireille Mathieu at the end of 1980 – as Bravo tu as gagné , with French lyrics by Alain Boublil. In November 1980, ABBA's seventh album Super Trouper was released, which reflected a certain change in ABBA's style with more prominent use of synthesizers and increasingly personal lyrics. It set a record for the most pre-orders ever received for a UK album after one million copies were ordered before release. The second single from the album, Super Trouper , also hit number-one in the UK, becoming the group's ninth and final UK chart-topper. Another track from the album, Lay All Your Love on Me , released in" +913,"Just Like That were left unreleased on The Singles: The First Ten Years for possible inclusion on the next projected studio album, though this never came to fruition. I Am the City was eventually released on the compilation album More ABBA Gold in 1993, while Just Like That has been recycled in new songs with other artists produced by Andersson and Ulvaeus. A reworked version of the verses ended up in the musical Chess. The chorus section of Just Like That was eventually released on a retrospective box set in 1994, as well as in the ABBA Undeleted medley featured on disc 9 of The Complete Studio Recordings. Despite a number of requests from fans, Ulvaeus and Andersson are still refusing to release ABBA's version of Just Like That in its entirety, even though the complete version has surfaced on bootlegs. The group travelled to London to promote The Singles: The First Ten Years in the first week of November 1982, appearing on Saturday Superstore and The Late, Late Breakfast Show, and al" +914,"and Lyngstad and Fältskog kept claiming in interviews that ABBA would come together for a new album repeatedly during 1983 and 1984. Internal strife between the group and their manager escalated and the band members sold their shares in Polar Music during 1983. Except for a TV appearance in 1986, the foursome did not come together publicly again until they were reunited at the Swedish premiere of the Mamma Mia! movie on 4 July 2008. The individual members' endeavours shortly before and after their final public performance coupled with the collapse of both marriages and the lack of significant activity in the following few years after that widely suggested that the group had broken up. In an interview with the Sunday Telegraph following the premiere, Ulvaeus and Andersson said that there was nothing that could entice them back on stage again. Ulvaeus said: We will never appear on stage again. [...] There is simply no motivation to re-group. Money is not a factor and we would like people" +915,"singers who had worked with the songwriters through the years) showcasing not only their ABBA years, but hits both before and after ABBA. The concert was a success and was ultimately released on CD. It later toured Scandinavia and even went to Beijing in the People's Republic of China for two concerts. In 2000 ABBA were reported to have turned down an offer of approximately one billion US dollars to do a reunion tour consisting of 100 concerts. For the semi-final of the Eurovision Song Contest 2004, staged in Istanbul 30 years after ABBA had won the contest in Brighton, all four members made cameo appearances in a special comedy video made for the interval act, titled Our Last Video Ever. Other well-known stars such as Rik Mayall, Cher and Iron Maiden's Eddie also made appearances in the video. It was not included in the official DVD release of the 2004 Eurovision contest, but was issued as a separate DVD release, retitled The Last Video at the request of the former ABBA members. The v" +916,"they had recorded two new songs, titled I Still Have Faith in You and Don't Shut Me Down , to feature in a TV special set to air later that year. In September 2018, Ulvaeus stated that the two new songs, as well as the TV special, now called ABBA: Thank You for the Music, An All-Star Tribute, would not be released until 2019. The TV special was later revealed to be scrapped by 2018, as Andersson and Ulvaeus rejected Fuller's project, and instead partnered with visual effects company Industrial Light and Magic to prepare the ABBAtars for a music video and a concert. In January 2019, it was revealed that neither song would be released before the summer. Andersson hinted at the possibility of a third song. In June 2019, Ulvaeus announced that the first new song and video containing the ABBAtars would be released in November 2019. In September, he stated in an interview that there were now five new ABBA songs to be released in 2020. In early 2020, Andersson confirmed that he was aiming for" +917,"and bass player, and overlaid other arrangements and instruments. Vocals were then added, and orchestra overdubs were usually left until last. Fältskog and Lyngstad contributed ideas at the studio stage. Andersson and Ulvaeus played them the backing tracks and they made comments and suggestions. According to Fältskog, she and Lyngstad had the final say in how the lyrics were shaped. After vocals and overdubs were done, the band took up to five days to mix a song. === Fashion, style, videos, advertising campaigns === ABBA was widely noted for the colourful and trend-setting costumes its members wore. The reason for the wild costumes was Swedish tax law: the cost of the clothes was deductible only if they could not be worn other than for performances. In their early years, group member Anni-Frid Lyngstad designed and even hand sewed the outfits. Later, as their success grew, they used professional theatrical clothes designer Owe Sandström together with tailor Lars Wigenius with Lyngstad" +918,"Don't Shut Me Down , also from Voyage, was nominated for Best Pop Duo/Group Performance. == Members == * Agnetha Fältskog – lead and backing vocals * Anni-Frid Frida Lyngstad – lead and backing vocals * Björn Ulvaeus – guitars, backing and lead vocals * Benny Andersson – keyboards, synthesizers, piano, accordion, backing and lead vocals The members of ABBA were married as follows: Agnetha Fältskog and Björn Ulvaeus from 1971 to 1979; Benny Andersson and Anni-Frid Lyngstad from 1978 to 1981. For their subsequent marriages, see their articles. In addition to the four members of ABBA, other musicians regularly played on their studio recordings, live appearances and concert performances. These include: * Rutger Gunnarsson – bass guitar, string arrangements (1972–1982; died 2015) * Ola Brunkert – drums (1972–1981; died 2008) * – bass guitar (1972–1980) * Janne Schaffer – electric lead guitar (1972–1982) * – drums (1972–1979; died 2024) * Malando Gassama – percussion (1973–1979; died 1999) *" +919,"An allegiance is a duty of fidelity said to be owed, or freely committed, by the people, subjects or citizens to their state or sovereign. ==Etymology== The word allegiance comes from Middle English (see Medieval Latin , a liegance ). The al- prefix was probably added through confusion with another legal term, allegiance, an allegation (the French comes from the English). Allegiance is formed from liege, from Old French , liege, free , of Germanic origin. The connection with Latin , to bind, is erroneous. ==Usage== Traditionally, English legal commentators used the term allegiance in two ways. In one sense, it referred to the deference which anyone, even a foreigner, was expected to pay to the institutions of the country where one lived. In the other sense, it meant national character and the subjection due to that character. ==Types== * Local allegiance * Natural allegiance ==United Kingdom== The English doctrine, which was at one time adopted in the United States, asserted that alleg" +920,"they owe by birthright allegiance and obedience to the Sovereign (Ex p. Anderson (1861) 3 E & E 487). A natural-born subject owes allegiance wherever they may be, so that where territory is occupied in the course of hostilities by an enemy's force, even if the annexation of the occupied country is proclaimed by the enemy, there can be no change of allegiance during the progress of hostilities on the part of a citizen of the occupied country (R v Vermaak (1900) 21 NLR 204 (South Africa)). Acquired allegiance was acquired by naturalisation or denization. Denization, or ligeantia acquisita, appears to be threefold (Thomas v Sorrel (1673) 3 Keb 143); * (a) absolute, as the common denization, without any limitation or restraint; * (b) limited, as when the sovereign grants letters of denization to an alien, and the alien's male heirs, or to an alien for the term of their life; * (c) It may be granted upon condition, cujus est dare, ejus est disponere, and this denization of an alien may come" +921,"to a monarch, the pledge is made to the flag, the republic, and to the core values of the country, specifically liberty and justice. The reciting of the pledge in the United States is voluntary because of the rights guaranteed to the people under the First Amendment to the United States Constitution - specifically, the guarantee of freedom of speech, which inherently includes the freedom not to speak. ==In Islam== The word used in the Arabic language for allegiance is bay'at (Arabic: بيعة), which means taking hand . The practice is sanctioned in the Quran by Surah 48:10: Verily, those who give thee their allegiance, they give it but to Allah Himself . The word is used for the oath of allegiance to an emir. It is also used for the initiation ceremony specific to many Sufi orders. ==See also== * Impressment * Legitimacy (political) * Mandate of Heaven * Renunciation of citizenship * Treason * Usurpation * War of 1812 * Winfield Scott ==References== ==Further reading== * Salmond on Citize" +922,"Altenberg (German for old mountain or mountain of the old ) may refer to: ==Places== ===Austria=== * Altenberg, a town in Sankt Andrä-Wördern, Tulln District * Altenberg bei Linz, in Upper Austria * Altenberg an der Rax, in Styria ===Germany=== * Altenberg (Bergisches Land), an area in Odenthal, North Rhine-Westphalia ** Altenberg Abbey, Cistercian monastery in Altenberg (Bergisches Land) *** Altenberger Dom, sometimes called Altenberg Cathedral, the former church of this Cistercian monastery * Altenberg (Hohenahr), hill in Hesse * Altenberg (Limpurg Hills), highest summit in the Limpurg Hills * Altenberg, Saxony, a town * Altenberga, a municipality in the Saale-Holzfeld district, Thuringia * Altenberg Abbey, Solms, a former Premonstratensian nunnery near Wetzlar in Hesse * Zinkfabrik Altenberg, a former zinc factory, now a branch of the LVR Industrial Museum, Oberhausen, North Rhine-Westphalia * Grube Altenberg, a show mine near Kreuztal, North Rhine-Westphalia ===Other places=== * Al" +923,"The MessagePad is a series of personal digital assistant devices developed by Apple Computer for the Newton platform, first released in 1993. Some electronic engineering and the manufacture of Apple's MessagePad devices was undertaken in Japan by Sharp. The devices are based on the ARM 610 RISC processor, run Newton OS, and all feature handwriting recognition software. Alongside the MessagePad series, Apple also developed and released the eMate 300 Newton device. ==History== The development of the Newton MessagePad first began with Apple's former senior vice president of research and development, Jean-Louis Gassée; his team included Steve Capps, co-writer of Mac OS Finder, and an employed engineer named Steve Sakoman. The development of the Newton MessagePad operated in secret until it was eventually revealed to the Apple Board of Directors in late 1990. When Gassée resigned from his position due to a significant disagreement with the board, seeing how his employer was treated, Sakoman" +924,"letter shapes - this would be useful in note-taking scenarios if there was insufficient time to make corrections immediately. To conserve memory and storage space, alternative recognition hypotheses would not be saved indefinitely. If the user returned to a note a week later, for example, they would only see the best match. Error correction in many current handwriting systems provides such functionality but adds more steps to the process, greatly increasing the interruption to a user's workflow that a given correction requires. ====User interface==== Text could also be entered by tapping with the stylus on a small on-screen pop-up QWERTY virtual keyboard, although more layouts were developed by users. Newton devices could also accept free-hand Sketches , Shapes , and Ink Text , much like a desktop computer graphics tablet. With Shapes , Newton could recognize that the user was attempting to draw a circle, a line, a polygon, etc., and it would clean them up into perfect vector represent" +925,"for two-way communications, the Mini 2000 which would have been very similar to a Palm Pilot, and the NewtonPhone developed by Siemens, which incorporated a handset and a keyboard. ==Market reception== Fourteen months after Sculley demoed it at the May 1992, Chicago CES, the MessagePad was first offered for sale on August 2, 1993, at the Boston Macworld Expo. The hottest item at the show, it cost $900. 50,000 MessagePads were sold in the device's first three months on the market. The original Apple MessagePad and MessagePad 100 were limited by the very short lifetime of their inadequate AAA batteries. Later versions of Newton OS offered improved handwriting recognition, quite possibly a leading reason for the continued popularity of the devices among Newton users. Even given the age of the hardware and software, Newtons still demand a sale price on the used market far greater than that of comparatively aged PDAs produced by other companies. In 2006, CNET compared an Apple MessagePad 20" +926,"Connectivity RS-422, LocalTalk & SHARP ASK Infrared RS-422, LocalTalk & SHARP ASK Infrared RS-422, LocalTalk & SHARP ASK Infrared Modem and Telephone dock Attachment RS-422, LocalTalk & SHARP ASK Infrared RS-422, LocalTalk & SHARP ASK Infrared RS-422, LocalTalk & SHARP ASK Infrared RS-422, LocalTalk & SHARP ASK Infrared RS-422, LocalTalk & SHARP ASK Infrared RS-422, LocalTalk, Infrared, ARDIS Network RS-232, LocalTalk WLAN, V.22bis modem, Analog/Digital Cellular, CDPD, RAM, ARDIS, Trunk Radio RS-232, LocalTalk, CDPD, WLAN, Optional dGPS, GSM, or IR via modular attachments RS-422, LocalTalk & SHARP ASK Infrared IrDA, headphone port, Interconnect port, LocalTalk, Audio I/O, Autodock Dual-mode IR;IrDA & SHARP ASK, LocalTalk, Audio I/O, Autodock, Phone I/O Dual-mode IR; IrDA & SHARP ASK, LocalTalk, Audio I/O, Autodock Dual-mode IR;IrDA & SHARP ASK, LocalTalk, Audio I/O, Autodock, Phone I/O Dual-mode IR;IrDA & SHARP ASK, LocalTalk, Audio I/O, Autodock, Phone I/O Dual-mode IR;IrDA & SHARP AS" +927,"with the Newton at the center. After Apple officially cancelled the Newton in 1998 they had to acquire as many Newtons as possible for this project. It was estimated initially 1000 Newtons, but later readjusted the figure to possibly 750 Newtons. They placed an “Internet Call” for Newtons. They purchased them in large and small quantities. The Newton was also used in healthcare applications, for example in collecting data directly from patients. Newtons were used as electronic diaries, with patients entering their symptoms and other information concerning their health status on a daily basis. The compact size of the device and its ease of use made it possible for the electronic diaries to be carried around and used in the patients' everyday life setting. This was an early example of electronic patient-reported outcomes (ePRO). ==See also== * Newton (platform) * Newton OS * eMate 300 * NewtonScript * Orphaned technology * Pen computing == References == ==Bibliography== * Apple's press r" +928,"Alfred Elton van Vogt ( ; April 26, 1912 – January 26, 2000) was a Canadian-born American science fiction writer. His fragmented, bizarre narrative style influenced later science fiction writers, notably Philip K. Dick. He was one of the most popular and influential practitioners of science fiction in the mid-twentieth century, the genre's so-called Golden Age, and one of the most complex. Although [van Vogt] catered for the pulps, he intensified the emotional impact and complexity of the stories they would bear . The Science Fiction Writers of America named him their 14th Grand Master in 1995 (presented 1996). ==Early life== Alfred Vogt (both Elton and van were added much later) was born on April 26, 1912, on his grandparents' farm in Edenburg, Manitoba, a tiny (and now defunct) Russian Mennonite community east of Gretna, Manitoba, Canada, in the Mennonite West Reserve. He was the third of six children born to Heinrich Henry Vogt and Aganetha Agnes Vogt (née Buhr), both of whom were b" +929,"Slan (Arkham House, 1946), which Campbell serialized in Astounding (September to December 1940). Using what became one of van Vogt's recurring themes, it told the story of a nine-year-old superman living in a world in which his kind are slain by Homo sapiens. Others saw van Vogt's talent from his first story, and in May 1941 van Vogt decided to become a full-time writer, quitting his job at the Canadian Department of National Defence. Freed from the necessity of living in Ottawa, he and his wife lived for a time in the Gatineau region of Quebec before moving to Toronto in the fall of 1941. Prolific throughout this period, van Vogt wrote many of his more famous short stories and novels in the years from 1941 through 1944. The novels The Book of Ptath and The Weapon Makers both appeared in magazines in serial form during this period; they were later published in book form after World War II. As well, several (though not all) of the stories that were compiled to make up the novels The Wea" +930,"Empire of the Atom was lifted almost bodily from that of Robert Graves' I, Claudius. (Also, one non-fiction work, The Hypnotism Handbook, appeared in 1956, though it had apparently been written much earlier.) After more than a decade of running their Dianetics center, Hull and van Vogt closed it in 1961. Nevertheless, van Vogt maintained his association with the organization and was still president of the Californian Association of Dianetic Auditors into the 1980s. ==Return to writing and later career (1962–1986)== Though the constant re-packaging of his older work meant that he had never really been away from the book publishing world, van Vogt had not published any wholly new fiction for almost 12 years when he decided to return to writing in 1962. He did not return immediately to science fiction, but instead wrote the only mainstream, non-sf novel of his career. Van Vogt was profoundly affected by revelations of totalitarian police states that emerged after World War II. Accordingly" +931,"Edna Mayne Hull, were Guests of Honor at the fourth World Science Fiction Convention. In 1980, van Vogt received a Casper Award (precursor to the Canadian Prix Aurora Awards) for Lifetime Achievement. The Science Fiction Writers of America (SFWA) named him its 14th Grand Master in 1995 (presented 1996). Great controversy within SFWA accompanied its long wait in bestowing its highest honor (limited to living writers, no more than one annually). Writing an obituary of van Vogt, Robert J. Sawyer, a fellow Canadian writer of science fiction, remarked: It is generally held that a key factor in the delay was damnable SFWA politics reflecting the concerns of Damon Knight, the founder of the SFWA, who abhorred van Vogt's style and politics and thoroughly demolished his literary reputation in the 1950s.Hartwell, David: The Way We Were: A. E. van Vogt, 1912-2000 , The New York Review of Science Fiction, March 2000, Number 139, Vol. 12, No. 7, page 24. Harlan Ellison was more explicit in 1999 int" +932,"Renaissance 1979 Cosmic Encounter 1983 Computerworld Computer Eye (1985) 1984 Null-A Three Null-A 1985 To Conquer Kiber Unpublished in English. It was published in French as A la conquête de Kiber and in Romanian as Cucerirea Kiberului ====Special works published as books==== * Planets for Sale by E. Mayne Hull (1954). A fix-up of five stories by Hull, originally published 1942 to 1946. Certain later editions (from 1965) credit both authors. * The Enchanted Village (1979). A 25-page chapbook of a short story originally published in 1950. * Slan Hunter by Kevin J. Anderson (2007). A sequel to Slan, based an unfinished draft by van Vogt. * Null-A Continuum by John C. Wright (2008). An authorized continuation of the Null-A series which ignored the events of Null-A Three. ===Collections=== * Out of the Unknown (1948), with Edna Mayne Hull * Masters of Time (1950) (a.k.a. Recruiting Station) [also includes The Changeling, both works were later published separately] * Triad (1951) omnibus of" +933,"Anna Sergeyevna Kournikova (; born 7 June 1981) is a Russian model and television personality, and former professional tennis player. Her appearance and celebrity status made her one of the best known tennis stars worldwide. At the peak of her fame, fans looking for images of Kournikova made her name one of the most common search strings on Google Search. Despite never winning a singles title, she reached No. 8 in the world in 2000. She achieved greater success playing doubles, where she was at times the world No. 1 player. With Martina Hingis as her partner, she won Grand Slam titles in Australia in 1999 and 2002, and the WTA Championships in 1999 and 2000. They referred to themselves as the Spice Girls of Tennis . Kournikova retired from professional tennis in 2003 due to serious back and spinal problems, including a herniated disk. She lives in Miami Beach, Florida, and played in occasional exhibitions and in doubles for the St. Louis Aces of World Team Tennis before the team folded" +934,"to Lindsay Davenport and Jana Novotná partnering with Likhovtseva. She broke into the top 50 on 19 May, and was ranked No. 32 in singles and No. 41 in doubles at the end of the season. Original redirects to Facebook. === 1998–2000: success and stardom === In 1998, Kournikova broke into the WTA's top 20 rankings for the first time, when she was ranked No. 16. At the Australian Open, Kournikova lost in the third round to world No. 1 player, Martina Hingis. She also partnered with Larisa Savchenko-Neiland in women's doubles, and they lost to eventual champions Hingis and Mirjana Lučić in the second round. Although she lost in the second round of the Paris Open to Anke Huber in singles, Kournikova reached her second doubles WTA Tour final, partnering with Larisa Savchenko-Neiland. They lost to Sabine Appelmans and Miriam Oremans. Kournikova and Savchenko-Neiland reached their second consecutive final at the Linz Open, losing to Alexandra Fusai and Nathalie Tauziat. At the Miami Open, Kourn" +935,"Australian Open. They also lost in the quarterfinals of the US Open. With Chanda Rubin, Kournikova played the semi-finals of Wimbledon, but they lost to Serena and Venus Williams. Partnering with Janet Lee, she won the Shanghai title. At the end of 2002 season, she was ranked No. 35 in singles and No. 11 in doubles. In 2003, Anna Kournikova achieved her first Grand Slam match victory in two years at the Australian Open. She defeated Henrieta Nagyová in the first round, and then lost to Justine Henin-Hardenne in the 2nd round. She withdrew from Tokyo due to a sprained back suffered at the Australian Open and did not return to Tour until Miami. On 9 April, in what would be the final WTA match of her career, Kournikova dropped out in the first round of the Family Circle Cup in Charleston, due to a left adductor strain. Her singles world ranking was 67. She reached the semi-finals at the ITF tournament in Sea Island, before withdrawing from a match versus Maria Sharapova due to the adducto" +936,"not know when they got married, he knew Fedorov was married . Kournikova started dating singer Enrique Iglesias in late 2001 after she had appeared in his music video for Escape . The couple have three children together, fraternal twins, a son and daughter, born on 16 December 2017, and another daughter born on 30 January 2020. It was reported in 2010 that Kournikova had become an American citizen. == Media publicity == In 2000, Kournikova became the new face for Berlei's shock absorber sports bras, and appeared in the only the ball should bounce billboard campaign. Following that, she was cast by the Farrelly brothers for a minor role in the 2000 film Me, Myself & Irene starring Jim Carrey and Renée Zellweger. Photographs of her have appeared on covers of various publications, including men's magazines, such as one in the much-publicized 2004 Sports Illustrated Swimsuit Issue, where she posed in bikinis and swimsuits, as well as in FHM and Maxim. Kournikova was named one of Peoples 50" +937,"Alfons Maria Jakob (2 July 1884 – 17 October 1931) was a German neurologist who worked in the field of neuropathology. He was born in Aschaffenburg, Bavaria and educated in medicine at the universities of Munich, Berlin, and Strasbourg, where he received his doctorate in 1908. During the following year, he began clinical work under the psychiatrist Emil Kraepelin and did laboratory work with Franz Nissl and Alois Alzheimer in Munich.Alfons Maria Jakob at Who Named It In 1911, by way of an invitation from Wilhelm Weygandt,Neurological Eponyms edited by Peter J. Koehler, George W. Bruyn, John M. S. Pearce he relocated to Hamburg, where he worked with Theodor Kaes and eventually became head of the laboratory of anatomical pathology at the psychiatric State Hospital Hamburg-Friedrichsberg. Following the death of Kaes in 1913, Jakob succeeded him as prosector. During World War I he served as an army physician in Belgium, and afterwards returned to Hamburg. In 1919, he obtained his habilitat" +938,"Agnosticism is the view or belief that the existence of God, the divine, or the supernatural is either unknowable in principle or unknown in fact. (page 56 in 1967 edition) When used in this epistemological sense, the term “agnosticism” can very naturally be extended beyond the issue of what is or can be known to cover a large family of positions, depending on what sort of “positive epistemic status” is at issue. For example, it might be identified with any of the following positions: that neither theistic belief nor atheistic belief is justified, that neither theistic belief nor atheistic belief is rationally required, that neither belief is rationally permissible, that neither has warrant, that neither is reasonable, or that neither is probable. It can also mean an apathy towards such religious belief and refer to personal limitations rather than a worldview. Another definition is the view that human reason is incapable of providing sufficient rational grounds to justify either the b" +939,"deity exists or not, and I don't care if any deity exists or not. Rauch, Jonathan, Let It Be: Three Cheers for Apatheism, The Atlantic Monthly, May 2003 ==History== ===Hindu philosophy=== Throughout the history of Hinduism there has been a strong tradition of philosophic speculation and skepticism. The Rig Veda takes an agnostic view on the fundamental question of how the universe and the gods were created. Nasadiya Sukta (Creation Hymn) in the tenth chapter of the Rig Veda says: ===Hume, Kant, and Kierkegaard=== Aristotle, Anselm, Aquinas, Descartes, and Gödel presented arguments attempting to rationally prove the existence of God. The skeptical empiricism of David Hume, the antinomies of Immanuel Kant, and the existential philosophy of Søren Kierkegaard convinced many later philosophers to abandon these attempts, regarding it impossible to construct any unassailable proof for the existence or non-existence of God. In his 1844 book Philosophical Fragments, Kierkegaard writes:Kierkegaa" +940,"Aristotle invented and Thomas Aquinas revived among the scientific community. Secondly, the divorce of pure science from human experience, as manifested in American Industrialization, had completely altered the environment, often disfiguring it, so as to suggest its insufficiency to human needs. Thirdly, because scientists were constantly producing more data—to the point where no single human could grasp it all at once—it followed that human intelligence was incapable of attaining a complete understanding of universe; therefore, to admit the mysteries of the unobserved universe was to be actually scientific. Bell believed that there were two other ways that humans could perceive and interact with the world. Artistic experience was how one expressed meaning through speaking, writing, painting, gesturing—any sort of communication which shared insight into a human's inner reality. Mystical experience was how one could read people and harmonize with them, being what we commonly call love." +941,"claim that agnosticism is impossible in practice, since a person can live only either as if God did not exist (etsi deus non-daretur), or as if God did exist (etsi deus daretur). ====Christian==== According to Pope Benedict XVI, strong agnosticism in particular contradicts itself in affirming the power of reason to know scientific truth. He blames the exclusion of reasoning from religion and ethics for dangerous pathologies such as crimes against humanity and ecological disasters. Agnosticism , said Benedict, is always the fruit of a refusal of that knowledge which is in fact offered to man ... The knowledge of God has always existed . He asserted that agnosticism is a choice of comfort, pride, dominion, and utility over truth, and is opposed by the following attitudes: the keenest self-criticism, humble listening to the whole of existence, the persistent patience and self-correction of the scientific method, a readiness to be purified by the truth. The Catholic Church sees merit in ex" +942,"Argon is a chemical element; it has symbol Ar and atomic number 18. It is in group 18 of the periodic table and is a noble gas.In older versions of the periodic table, the noble gases were identified as Group VIIIA or as Group 0. See Group (periodic table). Argon is the third most abundant gas in Earth's atmosphere, at 0.934% (9340 ppmv). It is more than twice as abundant as water vapor (which averages about 4000 ppmv, but varies greatly), 23 times as abundant as carbon dioxide (400 ppmv), and more than 500 times as abundant as neon (18 ppmv). Argon is the most abundant noble gas in Earth's crust, comprising 0.00015% of the crust. Nearly all argon in Earth's atmosphere is radiogenic argon-40, derived from the decay of potassium-40 in Earth's crust. In the universe, argon-36 is by far the most common argon isotope, as it is the most easily produced by stellar nucleosynthesis in supernovas. The name argon is derived from the Greek word , neuter singular form of meaning 'lazy' or 'inactiv" +943,"will be the dominant isotope, as it is on Earth. Argon produced directly by stellar nucleosynthesis is dominated by the alpha-process nuclide . Correspondingly, solar argon contains 84.6% (according to solar wind measurements), and the ratio of the three isotopes 36Ar : 38Ar : 40Ar in the atmospheres of the outer planets is 8400 : 1600 : 1. This contrasts with the low abundance of primordial in Earth's atmosphere, which is only 31.5 ppmv (= 9340 ppmv × 0.337%), comparable with that of neon (18.18 ppmv) on Earth and with interplanetary gasses, measured by probes. The atmospheres of Mars, Mercury and Titan (the largest moon of Saturn) contain argon, predominantly as . The predominance of radiogenic is the reason the standard atomic weight of terrestrial argon is greater than that of the next element, potassium, a fact that was puzzling when argon was discovered. Mendeleev positioned the elements on his periodic table in order of atomic weight, but the inertness of argon suggested a place" +944,"chamber for fine grained three-dimensional imaging of neutrino interactions. At Linköping University, Sweden, the inert gas is being utilized in a vacuum chamber in which plasma is introduced to ionize metallic films. This process results in a film usable for manufacturing computer processors. The new process would eliminate the need for chemical baths and use of expensive, dangerous and rare materials. ===Preservative=== thumb|A sample of caesium is packed under argon to avoid reactions with air Argon is used to displace oxygen- and moisture-containing air in packaging material to extend the shelf-lives of the contents (argon has the European food additive code E938). Aerial oxidation, hydrolysis, and other chemical reactions that degrade the products are retarded or prevented entirely. High-purity chemicals and pharmaceuticals are sometimes packed and sealed in argon. In winemaking, argon is used in a variety of activities to provide a barrier against oxygen at the liquid surface, wh" +945,"Arsenic is a chemical element with the symbol As and the atomic number 33. It is a metalloid and one of the pnictogens, and therefore shares many properties with its group 15 neighbors phosphorus and antimony. Arsenic is a notoriously toxic heavy metal. It occurs naturally in many minerals, usually in combination with sulfur and metals, but also as a pure elemental crystal. It has various allotropes, but only the grey form, which has a metallic appearance, is important to industry. The primary use of arsenic is in alloys of lead (for example, in car batteries and ammunition). Arsenic is a common n-type dopant in semiconductor electronic devices. It is also a component of the III–V compound semiconductor gallium arsenide. Arsenic and its compounds, especially the trioxide, are used in the production of pesticides, treated wood products, herbicides, and insecticides. These applications are declining with the increasing recognition of the toxicity of arsenic and its compounds. Arsenic has" +946,"the +3 oxidation state, arsenic is typically pyramidal owing to the influence of the lone pair of electrons. === Inorganic compounds === One of the simplest arsenic compounds is the trihydride, the highly toxic, flammable, pyrophoric arsine (AsH3). This compound is generally regarded as stable, since at room temperature it decomposes only slowly. At temperatures of 250–300 °C decomposition to arsenic and hydrogen is rapid.Greenwood and Earnshaw, pp. 557–558 Several factors, such as humidity, presence of light and certain catalysts (namely aluminium) facilitate the rate of decomposition. It oxidises readily in air to form arsenic trioxide and water, and analogous reactions take place with sulfur and selenium instead of oxygen. Arsenic forms colorless, odorless, crystalline oxides As2O3 ( white arsenic ) and As2O5 which are hygroscopic and readily soluble in water to form acidic solutions. Arsenic(V) acid is a weak acid and its salts, known as arsenates, are a major source of arsenic con" +947,"arsenical bronze. Jabir ibn Hayyan described the isolation of arsenic before 815 AD.George Sarton, Introduction to the History of Science. We find in his writings [...] preparation of various substances (e.g., basic lead carbonatic, arsenic and antimony from their sulphides). Albertus Magnus (Albert the Great, 1193–1280) later isolated the element from a compound in 1250, by heating soap together with arsenic trisulfide. In 1649, Johann Schröder published two ways of preparing arsenic. Crystals of elemental (native) arsenic are found in nature, although rarely. Cadet's fuming liquid (impure cacodyl), often claimed as the first synthetic organometallic compound, was synthesized in 1760 by Louis Claude Cadet de Gassicourt through the reaction of potassium acetate with arsenic trioxide. In the Victorian era, women would eat arsenic ( white arsenic or arsenic trioxide) mixed with vinegar and chalk to improve the complexion of their faces, making their skin paler (to show they did not work" +948,"content of 0.3% has an increased corrosion stability in certain environments. Gallium arsenide is an important semiconductor material, used in integrated circuits. Circuits made from GaAs are much faster (but also much more expensive) than those made from silicon. Unlike silicon, GaAs has a direct bandgap, and can be used in laser diodes and LEDs to convert electrical energy directly into light. === Military === After World War I, the United States built a stockpile of 20,000 tons of weaponized lewisite (ClCH=CHAsCl2), an organoarsenic vesicant (blister agent) and lung irritant. The stockpile was neutralized with bleach and dumped into the Gulf of Mexico in the 1950s. During the Vietnam War, the United States used Agent Blue, a mixture of sodium cacodylate and its acid form, as one of the rainbow herbicides to deprive North Vietnamese soldiers of foliage cover and rice. === Other uses === * Copper acetoarsenite was used as a green pigment known under many names, including Paris Green a" +949,"of natural origin, and is released from the sediment into the groundwater, caused by the anoxic conditions of the subsurface. This groundwater was used after local and western NGOs and the Bangladeshi government undertook a massive shallow tube well drinking-water program in the late twentieth century. This program was designed to prevent drinking of bacteria-contaminated surface waters, but failed to test for arsenic in the groundwater. Many other countries and districts in Southeast Asia, such as Vietnam and Cambodia, have geological environments that produce groundwater with a high arsenic content. Arsenicosis was reported in Nakhon Si Thammarat, Thailand, in 1987, and the Chao Phraya River probably contains high levels of naturally occurring dissolved arsenic without being a public health problem because much of the public uses bottled water. In Pakistan, more than 60 million people are exposed to arsenic polluted drinking water indicated by a 2017 report in Science. Podgorski's te" +950,"is extracted from underground aquifers.Newspaper article (in Hungarian) published by Magyar Nemzet on 15 April 2012. ==== San Pedro de Atacama ==== For several centuries, the people of San Pedro de Atacama in Chile have been drinking water that is contaminated with arsenic, and some evidence suggests they have developed some immunity. ==== Hazard maps for contaminated groundwater ==== Around one-third of the world's population drinks water from groundwater resources. Of this, about 10 percent, approximately 300 million people, obtains water from groundwater resources that are contaminated with unhealthy levels of arsenic or fluoride.Eawag (2015) Geogenic Contamination Handbook – Addressing Arsenic and Fluoride in Drinking Water. C.A. Johnson, A. Bretzler (Eds.), Swiss Federal Institute of Aquatic Science and Technology (Eawag), Duebendorf, Switzerland. (download: www.eawag.ch/en/research/humanwelfare/drinkingwater/wrq/geogenic-contamination-handbook/) These trace elements derive mainly" +951,"2007, 50% of the 5,280 metric tons of consumption was still used for this purpose. In the United States, the voluntary phasing-out of arsenic in production of consumer products and residential and general consumer construction products began on 31 December 2003, and alternative chemicals are now used, such as Alkaline Copper Quaternary, borates, copper azole, cyproconazole, and propiconazole. Although discontinued, this application is also one of the most concerning to the general public. The vast majority of older pressure-treated wood was treated with CCA. CCA lumber is still in widespread use in many countries, and was heavily used during the latter half of the 20th century as a structural and outdoor building material. Although the use of CCA lumber was banned in many areas after studies showed that arsenic could leach out of the wood into the surrounding soil (from playground equipment, for instance), a risk is also presented by the burning of older CCA timber. The direct or indir" +952,"Dr. Oz television show broadcast a program highlighting tests performed by an independent lab hired by the producers. Though the methodology was disputed (it did not distinguish between organic and inorganic arsenic) the tests showed levels of arsenic up to 36 ppb. In response, the FDA tested the worst brand from the Dr. Oz show and found much lower levels. Ongoing testing found 95% of the apple juice samples were below the level of concern. Later testing by Consumer Reports showed inorganic arsenic at levels slightly above 10 ppb, and the organization urged parents to reduce consumption. In July 2013, on consideration of consumption by children, chronic exposure, and carcinogenic effect, the FDA established an action level of 10 ppb for apple juice, the same as the drinking water standard. Concern about arsenic in rice in Bangladesh was raised in 2002, but at the time only Australia had a legal limit for food (one milligram per kilogram, or 1000 ppb). Concern was raised about people w" +953,"environmental contamination. According to Croal, Gralnick, Malasarn and Newman, [the] understanding [of] what stimulates As(III) oxidation and/or limits As(V) reduction is relevant for bioremediation of contaminated sites (Croal). The study of chemolithoautotrophic As(III) oxidizers and the heterotrophic As(V) reducers can help the understanding of the oxidation and/or reduction of arsenic. === Treatment === Treatment of chronic arsenic poisoning is possible. British anti-lewisite (dimercaprol) is prescribed in doses of 5 mg/kg up to 300 mg every 4 hours for the first day, then every 6 hours for the second day, and finally every 8 hours for 8 additional days. However the USA's Agency for Toxic Substances and Disease Registry (ATSDR) states that the long-term effects of arsenic exposure cannot be predicted. Blood, urine, hair, and nails may be tested for arsenic; however, these tests cannot foresee possible health outcomes from the exposure. Long-term exposure and consequent excretion t" +954,"Antimony is a chemical element; it has symbol Sb () and atomic number 51. A lustrous grey metal or metalloid, it is found in nature mainly as the sulfide mineral stibnite (Sb2S3). Antimony compounds have been known since ancient times and were powdered for use as medicine and cosmetics, often known by the Arabic name kohl.David Kimhi's Commentary on Isaiah 4:30 and I Chronicles 29:2; Hebrew: פוך/כְּחֻל, Aramaic: כּוּחְלִי/צדידא; Arabic: كحل, and which can also refer to antimony trisulfide. See also Z. Dori, Antimony and Henna (Heb. הפוך והכופר), Jerusalem 1983 (Hebrew). The earliest known description of this metalloid in the West was written in 1540 by Vannoccio Biringuccio. China is the largest producer of antimony and its compounds, with most production coming from the Xikuangshan Mine in Hunan. The industrial methods for refining antimony from stibnite are roasting followed by reduction with carbon, or direct reduction of stibnite with iron. The most common applications for metallic" +955,", , , and are all molecular compounds having trigonal pyramidal molecular geometry. The trifluoride is prepared by the reaction of with HF:Wiberg and Holleman, pp. 761–762 : + 6 HF → 2 + 3 It is Lewis acidic and readily accepts fluoride ions to form the complex anions and . Molten is a weak electrical conductor. The trichloride is prepared by dissolving in hydrochloric acid: : + 6 HCl → 2 + 3 Arsenic sulfides are not readily attacked by the hydrochloric acid, so this method offers a route to As-free Sb. The pentahalides and have trigonal bipyramidal molecular geometry in the gas phase, but in the liquid phase, is polymeric, whereas is monomeric.Wiberg and Holleman, p. 761 is a powerful Lewis acid used to make the superacid fluoroantimonic acid ( H2SbF7 ). Oxyhalides are more common for antimony than for arsenic and phosphorus. Antimony trioxide dissolves in concentrated acid to form oxoantimonyl compounds such as SbOCl and .Wiberg and Holleman, p. 764 ===Antimonides, hydrides, and orga" +956,"not found as metal , or not found unalloyed . Antimony in Kirk-Othmer Encyclopedia of Chemical Technology, 5th ed. 2004. However, ancient Greek would more naturally express the pure negative as α- ( not )., which considers the derivation a popular etymology . Edmund Oscar von Lippmann conjectured a hypothetical Greek word ανθήμόνιον anthemonion, which would mean floret , and cites several examples of related Greek words (but not that one) which describe chemical or biological efflorescence.von Lippmann, Edmund Oscar (1919) Entstehung und Ausbreitung der Alchemie, teil 1. Berlin: Julius Springer (in German). pp. 642–5 The early uses of antimonium include the translations, in 1050–1100, by Constantine the African of Arabic medical treatises. Several authorities believe antimonium is a scribal corruption of some Arabic form; Meyerhof derives it from ithmid;Meyerhof as quoted in , asserts that ithmid or athmoud became corrupted in the medieval traductions barbaro-latines . The OED asserts" +957,"In lead–acid batteries, this addition improves plate strength and charging characteristics. For sailboats, lead keels are used to provide righting moment, ranging from 600 lbs to over 200 tons for the largest sailing superyachts; to improve hardness and tensile strength of the lead keel, antimony is mixed with lead between 2% and 5% by volume. Antimony is used in antifriction alloys (such as Babbitt metal), in bullets and lead shot, electrical cable sheathing, type metal (for example, for linotype printing machines), solder (some lead-free solders contain 5% Sb), in pewter, and in hardening alloys with low tin content in the manufacturing of organ pipes. === Other applications === Three other applications consume nearly all the rest of the world's supply. One application is as a stabilizer and catalyst for the production of polyethylene terephthalate. Another is as a fining agent to remove microscopic bubbles in glass, mostly for TV screens antimony ions interact with oxygen, suppressi" +958,"unclear if antimony can enter the body through the skin. The presence of low levels of antimony in saliva may also be associated with dental decay. ==Notes== ==References== ==Cited sources== * * ==External links== * Public Health Statement for Antimony * International Antimony Association vzw (i2a) * Chemistry in its element podcast (MP3) from the Royal Society of Chemistry's Chemistry World: Antimony * Antimony at The Periodic Table of Videos (University of Nottingham) * CDC – NIOSH Pocket Guide to Chemical Hazards – Antimony * Antimony Mineral data and specimen images Category:Chemical elements Category:Metalloids Category:Native element minerals Category:Nuclear materials Category:Pnictogens Category:Trigonal minerals Category:Minerals in space group 166 Category:Materials that expand upon freezing Category:Chemical elements with rhombohedral structure" +959,"Actinium is a chemical element; it has symbol Ac and atomic number 89. It was first isolated by Friedrich Oskar Giesel in 1902, who gave it the name emanium; the element got its name by being wrongly identified with a substance André-Louis Debierne found in 1899 and called actinium. The actinide series, a set of 15 elements between actinium and lawrencium in the periodic table, are named for actinium. Together with polonium, radium, and radon, actinium was one of the first non-primordial radioactive elements to be isolated. A soft, silvery-white radioactive metal, actinium reacts rapidly with oxygen and moisture in air forming a white coating of actinium oxide that prevents further oxidation. As with most lanthanides and many actinides, actinium assumes oxidation state +3 in nearly all its chemical compounds. Actinium is found only in traces in uranium and thorium ores as the isotope 227Ac, which decays with a half-life of 21.772 years, predominantly emitting beta and sometimes alpha p" +960,"225 cF12 567 567 567 4 8.35 Ac2O3 white trigonal Pm1 164 hP5 408 408 630 1 9.18 Ac2S3 black cubic I3d 220 cI28 778.56 778.56 778.56 4 6.71 AcF3 whiteMeyer, p. 71 hexagonal Pc1 165 hP24 741 741 755 6 7.88 AcCl3 white hexagonal P63/m 165 hP8 764 764 456 2 4.8 AcBr3 white hexagonal P63/m 165 hP8 764 764 456 2 5.85 AcOF white cubic Fmm 593.1 8.28 AcOCl white tetragonal 424 424 707 7.23 AcOBr white tetragonal 427 427 740 7.89 AcPO4·0.5H2O unknown hexagonal 721 721 664 5.48 Here a, b and c are lattice constants, No is space group number and Z is the number of formula units per unit cell. Density was not measured directly but calculated from the lattice parameters. ===Oxides=== Actinium oxide (Ac2O3) can be obtained by heating the hydroxide at or the oxalate at , in vacuum. Its crystal lattice is isotypic with the oxides of most trivalent rare-earth metals. ===Halides=== Actinium trifluoride can be produced either in solution or in solid reaction. The former reaction is carried out at room te" +961,"of decay and nuclear fusion, such as thorium, polonium, lead and bismuth. The extraction can be performed with thenoyltrifluoroacetone-benzene solution from an aqueous solution of the radiation products, and the selectivity to a certain element is achieved by adjusting the pH (to about 6.0 for actinium). An alternative procedure is anion exchange with an appropriate resin in nitric acid, which can result in a separation factor of 1,000,000 for radium and actinium vs. thorium in a two-stage process. Actinium can then be separated from radium, with a ratio of about 100, using a low cross-linking cation exchange resin and nitric acid as eluant. 225Ac was first produced artificially at the Institute for Transuranium Elements (ITU) in Germany using a cyclotron and at St George Hospital in Sydney using a linac in 2000. This rare isotope has potential applications in radiation therapy and is most efficiently produced by bombarding a radium-226 target with 20–30 MeV deuterium ions. This reacti" +962,"Americium is a synthetic chemical element; it has symbol Am and atomic number 95. It is radioactive and a transuranic member of the actinide series in the periodic table, located under the lanthanide element europium and was thus named after the Americas by analogy. Americium was first produced in 1944 by the group of Glenn T. Seaborg from Berkeley, California, at the Metallurgical Laboratory of the University of Chicago, as part of the Manhattan Project. Although it is the third element in the transuranic series, it was discovered fourth, after the heavier curium. The discovery was kept secret and only released to the public in November 1945. Most americium is produced by uranium or plutonium being bombarded with neutrons in nuclear reactors – one tonne of spent nuclear fuel contains about 100 grams of americium. It is widely used in commercial ionization chamber smoke detectors, as well as in neutron sources and industrial gauges. Several unusual applications, such as nuclear batteri" +963,"products, but again this has not been confirmed. Existing americium is concentrated in the areas used for the atmospheric nuclear weapons tests conducted between 1945 and 1980, as well as at the sites of nuclear incidents, such as the Chernobyl disaster. For example, the analysis of the debris at the testing site of the first U.S. hydrogen bomb, Ivy Mike, (1 November 1952, Enewetak Atoll), revealed high concentrations of various actinides including americium; but due to military secrecy, this result was not published until later, in 1956. Trinitite, the glassy residue left on the desert floor near Alamogordo, New Mexico, after the plutonium-based Trinity nuclear bomb test on 16 July 1945, contains traces of americium-241. Elevated levels of americium were also detected at the crash site of a US Boeing B-52 bomber aircraft, which carried four hydrogen bombs, in 1968 in Greenland. In other regions, the average radioactivity of surface soil due to residual americium is only about 0.01 pic" +964,"was conducted using elemental barium as reducing agent in a water- and oxygen-free environment inside an apparatus made of tantalum and tungsten.Gmelin Handbook of Inorganic Chemistry, System No. 71, transuranics, Part B 1, pp. 57–67.Penneman, p. 3 : \mathrm{2\ AmF_3\ +\ 3\ Ba\ \longrightarrow \ 2\ Am\ +\ 3\ BaF_2} An alternative is the reduction of americium dioxide by metallic lanthanum or thorium: : \mathrm{3\ AmO_2\ +\ 4\ La\ \longrightarrow \ 3\ Am\ +\ 2\ La_2O_3} ==Physical properties== In the periodic table, americium is located to the right of plutonium, to the left of curium, and below the lanthanide europium, with which it shares many physical and chemical properties. Americium is a highly radioactive element. When freshly prepared, it has a silvery-white metallic lustre, but then slowly tarnishes in air. With a density of 12 g/cm3, americium is less dense than both curium (13.52 g/cm3) and plutonium (19.8 g/cm3); but has a higher density than europium (5.264 g/cm3)—mostly be" +965,"acidic aqueous solution the ion is unstable with respect to disproportionation.Greenwood, p. 1275 The reaction : is typical. The chemistry of Am(V) and Am(VI) is comparable to the chemistry of uranium in those oxidation states. In particular, compounds like and are comparable to uranates and the ion is comparable to the uranyl ion, . Such compounds can be prepared by oxidation of Am(III) in dilute nitric acid with ammonium persulfate. Other oxidising agents that have been used include silver(I) oxide, ozone and sodium persulfate. ==Chemical compounds== ===Oxygen compounds=== Three americium oxides are known, with the oxidation states +2 (AmO), +3 (Am2O3) and +4 (AmO2). Americium(II) oxide was prepared in minute amounts and has not been characterized in detail. Americium(III) oxide is a red-brown solid with a melting point of 2205 °C.Wiberg, p. 1972 Americium(IV) oxide is the main form of solid americium which is used in nearly all its applications. As most other actinide dioxides, it i" +966,"on the biosorption and bioaccumulation of americium by bacteria and fungi. ==Fission== The isotope 242mAm (half-life 141 years) has the largest cross sections for absorption of thermal neutrons (5,700 barns),Pfennig, G.; Klewe-Nebenius, H and Seelmann Eggebert, W. (Eds.): Karlsruhe nuclide, 7 Edition 2006. that results in a small critical mass for a sustained nuclear chain reaction. The critical mass for a bare 242mAm sphere is about 9–14 kg (the uncertainty results from insufficient knowledge of its material properties). It can be lowered to 3–5 kg with a metal reflector and should become even smaller with a water reflector. Abstract Such small critical mass is favorable for portable nuclear weapons, but those based on 242mAm are not known yet, probably because of its scarcity and high price. The critical masses of the two readily available isotopes, 241Am and 243Am, are relatively high – 57.6 to 75.6 kg for 241Am and 209 kg for 243Am.Institut de Radioprotection et de Sûreté Nucléaire" +967,"and high price of this particular nuclear isomer. In 2019, researchers at the UK National Nuclear Laboratory and the University of Leicester demonstrated the use of heat generated by americium to illuminate a small light bulb. This technology could lead to systems to power missions with durations up to 400 years into interstellar space, where solar panels do not function. ===Neutron source=== The oxide of 241Am pressed with beryllium is an efficient neutron source. Here americium acts as the alpha source, and beryllium produces neutrons owing to its large cross-section for the (α,n) nuclear reaction: : ^{241}_{95}Am -> ^{237}_{93}Np + ^{4}_{2}He + \gamma : ^{9}_{4}Be + ^{4}_{2}He -> ^{12}_{6}C + ^{1}_{0}n + \gamma The most widespread use of 241AmBe neutron sources is a neutron probe – a device used to measure the quantity of water present in soil, as well as moisture/density for quality control in highway construction. 241Am neutron sources are also used in well logging applications, a" +968,"Astatine is a chemical element; it has symbol At and atomic number 85. It is the rarest naturally occurring element in the Earth's crust, occurring only as the decay product of various heavier elements. All of astatine's isotopes are short-lived; the most stable is astatine-210, with a half-life of 8.1 hours. Consequently, a solid sample of the element has never been seen, because any macroscopic specimen would be immediately vaporized by the heat of its radioactivity. The bulk properties of astatine are not known with certainty. Many of them have been estimated from its position on the periodic table as a heavier analog of fluorine, chlorine, bromine, and iodine, the four stable halogens. However, astatine also falls roughly along the dividing line between metals and nonmetals, and some metallic behavior has also been observed and predicted for it. Astatine is likely to have a dark or lustrous appearance and may be a semiconductor or possibly a metal. Chemically, several anionic speci" +969,"21% less than that of iodine. In comparison, the value of Cl (349) is 6.4% higher than F (328); Br (325) is 6.9% less than Cl; and I (295) is 9.2% less than Br. The marked reduction for At was predicted as being due to spin–orbit interactions. The first ionization energy of astatine is about 899 kJ mol−1, which continues the trend of decreasing first ionization energies down the halogen group (fluorine, 1681; chlorine, 1251; bromine, 1140; iodine, 1008). == Compounds == Less reactive than iodine, astatine is the least reactive of the halogens; the chemical properties of tennessine, the next-heavier group 17 element, have not yet been investigated, however. Astatine compounds have been synthesized in nano-scale amounts and studied as intensively as possible before their radioactive disintegration. The reactions involved have been typically tested with dilute solutions of astatine mixed with larger amounts of iodine. Acting as a carrier, the iodine ensures there is sufficient material fo" +970,"Berkeley disproved Allison's method and the validity of his discovery. There was another claim in 1937, by the chemist Rajendralal De. Working in Dacca in British India (now Dhaka in Bangladesh), he chose the name dakin for element 85, which he claimed to have isolated as the thorium series equivalent of radium F (polonium-210) in the radium series. The properties he reported for dakin do not correspond to those of astatine, and astatine's radioactivity would have prevented him from handling it in the quantities he claimed. Moreover, astatine is not found in the thorium series, and the true identity of dakin is not known. In 1936, the team of Romanian physicist Horia Hulubei and French physicist Yvette Cauchois claimed to have discovered element 85 by observing its X-ray emission lines. In 1939, they published another paper which supported and extended previous data. In 1944, Hulubei published a summary of data he had obtained up to that time, claiming it was supported by the work of o" +971,"and most of the lighter isotopes exhibit beta plus decay (positron emission), astatine-217 and heavier isotopes except astatine-218 exhibit beta minus decay, while astatine-211 undergoes electron capture. The most stable isotope is astatine-210, which has a half-life of 8.1 hours. The primary decay mode is beta plus, to the relatively long-lived (in comparison to astatine isotopes) alpha emitter polonium-210. In total, only five isotopes have half-lives exceeding one hour (astatine-207 to -211). The least stable ground state isotope is astatine-213, with a half-life of 125 nanoseconds. It undergoes alpha decay to the extremely long-lived bismuth-209. Astatine has 24 known nuclear isomers, which are nuclei with one or more nucleons (protons or neutrons) in an excited state. A nuclear isomer may also be called a meta-state , meaning the system has more internal energy than the ground state (the state with the lowest possible internal energy), making the former likely to decay into the la" +972,"acid. Following this first step, the acid can be distilled away to leave behind a white residue that contains both bismuth and the desired astatine product. This residue is then dissolved in a concentrated acid, such as hydrochloric acid. Astatine is extracted from this acid using an organic solvent such as dibutyl ether, diisopropyl ether (DIPE), or thiosemicarbazide. Using liquid-liquid extraction, the astatine product can be repeatedly washed with an acid, such as HCl, and extracted into the organic solvent layer. A separation yield of 93% using nitric acid has been reported, falling to 72% by the time purification procedures were completed (distillation of nitric acid, purging residual nitrogen oxides, and redissolving bismuth nitrate to enable liquid–liquid extraction). Wet methods involve multiple radioactivity handling steps and have not been considered well suited for isolating larger quantities of astatine. However, wet extraction methods are being examined for use in producti" +973,"Atoms are the basic particles of the chemical elements. An atom consists of a nucleus of protons and generally neutrons, surrounded by an electromagnetically bound swarm of electrons. The chemical elements are distinguished from each other by the number of protons that are in their atoms. For example, any atom that contains 11 protons is sodium, and any atom that contains 29 protons is copper. Atoms with the same number of protons but a different number of neutrons are called isotopes of the same element. Atoms are extremely small, typically around 100 picometers across. A human hair is about a million carbon atoms wide. Atoms are smaller than the shortest wavelength of visible light, which means humans cannot see atoms with conventional microscopes. They are so small that accurately predicting their behavior using classical physics is not possible due to quantum effects. More than 99.94% of an atom's mass is in the nucleus. Protons have a positive electric charge and neutrons have no" +974,"form a ratio of 1:2:4. The respective formulas for these oxides are N2O, NO, and NO2.Dalton (1808). A New System of Chemical Philosophy vol. 1, pp. 316–319Holbrow et al. (2010). Modern Introductory Physics, pp. 65–66 === Discovery of the electron === In 1897, J. J. Thomson discovered that cathode rays are not a form of light but made of negatively charged particles because they can be deflected by electric and magnetic fields. He measured these particles to be at least a thousand times lighter than hydrogen (the lightest atom).In his book The Corpuscular Theory of Matter (1907), Thomson estimates electrons to be 1/1700 the mass of hydrogen. He called these new particles corpuscles but they were later renamed electrons since these are the particles that carry electricity. The Mechanism Of Conduction In Metals , Think Quest. Thomson also showed that electrons were identical to particles given off by photoelectric and radioactive materials. Thomson explained that an electric current is th" +975,"charge equal to the electron's negative charge.The Development of the Theory of Atomic Structure (Rutherford 1936). Reprinted in Background to Modern Science: Ten Lectures at Cambridge arranged by the History of Science Committee 1936: In 1919 I showed that when light atoms were bombarded by α-particles they could be broken up with the emission of a proton, or hydrogen nucleus. We therefore presumed that a proton must be one of the units of which the nuclei of other atoms were composed... He named this particle proton in 1920.Footnote by Ernest Rutherford: 'At the time of writing this paper in Australia, Professor Orme Masson was not aware that the name proton had already been suggested as a suitable name for the unit of mass nearly 1, in terms of oxygen 16, that appears to enter into the nuclear structure of atoms. The question of a suitable name for this unit was discussed at an informal meeting of a number of members of Section A of the British Association at Cardiff this year. The" +976,"elementary particles called quarks. There are two types of quarks in atoms, each having a fractional electric charge. Protons are composed of two up quarks (each with charge +) and one down quark (with a charge of −). Neutrons consist of one up quark and two down quarks. This distinction accounts for the difference in mass and charge between the two particles. The quarks are held together by the strong interaction (or strong force), which is mediated by gluons. The protons and neutrons, in turn, are held to each other in the nucleus by the nuclear force, which is a residuum of the strong force that has somewhat different range-properties (see the article on the nuclear force for more). The gluon is a member of the family of gauge bosons, which are elementary particles that mediate physical forces. === Nucleus === All the bound protons and neutrons in an atom make up a tiny atomic nucleus, and are collectively called nucleons. The radius of a nucleus is approximately equal to 1.07 \sqrt" +977,"energy to boost it into the new quantum state. Likewise, through spontaneous emission, an electron in a higher energy state can drop to a lower energy state while radiating the excess energy as a photon. These characteristic energy values, defined by the differences in the energies of the quantum states, are responsible for atomic spectral lines. The amount of energy needed to remove or add an electron—the electron binding energy—is far less than the binding energy of nucleons. For example, it requires only 13.6 eV to strip a ground-state electron from a hydrogen atom, compared to 2.23 million eV for splitting a deuterium nucleus. Atoms are electrically neutral if they have an equal number of protons and electrons. Atoms that have either a deficit or a surplus of electrons are called ions. Electrons that are farthest from the nucleus may be transferred to other nearby atoms or shared between atoms. By this mechanism, atoms are able to bond into molecules and other types of chemical com" +978,"of the distance out to which the electron cloud extends from the nucleus. This assumes the atom to exhibit a spherical shape, which is only obeyed for atoms in vacuum or free space. Atomic radii may be derived from the distances between two nuclei when the two atoms are joined in a chemical bond. The radius varies with the location of an atom on the atomic chart, the type of chemical bond, the number of neighboring atoms (coordination number) and a quantum mechanical property known as spin. On the periodic table of the elements, atom size tends to increase when moving down columns, but decrease when moving across rows (left to right). Consequently, the smallest atom is helium with a radius of 32 pm, while one of the largest is caesium at 225 pm. When subjected to external forces, like electrical fields, the shape of an atom may deviate from spherical symmetry. The deformation depends on the field magnitude and the orbital type of outer shell electrons, as shown by group-theoretical con" +979,"of electrons leads to an unpaired electron and a net overall magnetic moment. The orbitals of neighboring atoms overlap and a lower energy state is achieved when the spins of unpaired electrons are aligned with each other, a spontaneous process known as an exchange interaction. When the magnetic moments of ferromagnetic atoms are lined up, the material can produce a measurable macroscopic field. Paramagnetic materials have atoms with magnetic moments that line up in random directions when no magnetic field is present, but the magnetic moments of the individual atoms line up in the presence of a field. The nucleus of an atom will have no spin when it has even numbers of both neutrons and protons, but for other cases of odd numbers, the nucleus may have a spin. Normally nuclei with spin are aligned in random directions because of thermal equilibrium, but for certain elements (such as xenon-129) it is possible to polarize a significant proportion of the nuclear spin states so that they ar" +980,"chemical bonding between these elements takes many forms of electron-sharing that are more than simple electron transfers. Examples include the element carbon and the organic compounds. The chemical elements are often displayed in a periodic table that is laid out to display recurring chemical properties, and elements with the same number of valence electrons form a group that is aligned in the same column of the table. (The horizontal rows correspond to the filling of a quantum shell of electrons.) The elements at the far right of the table have their outer shell completely filled with electrons, which results in chemically inert elements known as the noble gases. === States === Quantities of atoms are found in different states of matter that depend on the physical conditions, such as temperature and pressure. By varying the conditions, materials can transition between solids, liquids, gases and plasmas. Within a state, a material can also exist in different allotropes. An example of" +981,"than ionization potential, the matter exists in the form of plasma—a gas of positively charged ions (possibly, bare nuclei) and electrons. When the temperature drops below the ionization potential, atoms become statistically favorable. Atoms (complete with bound electrons) became to dominate over charged particles 380,000 years after the Big Bang—an epoch called recombination, when the expanding Universe cooled enough to allow electrons to become attached to nuclei. Since the Big Bang, which produced no carbon or heavier elements, atomic nuclei have been combined in stars through the process of nuclear fusion to produce more of the element helium, and (via the triple-alpha process) the sequence of elements from carbon up to iron; see stellar nucleosynthesis for details. Isotopes such as lithium-6, as well as some beryllium and boron are generated in space through cosmic ray spallation. This occurs when a high-energy proton strikes an atomic nucleus, causing large numbers of nucleons to" +982,"Arable land (from the , able to be ploughed ) is any land capable of being ploughed and used to grow crops.Oxford English Dictionary, arable, adj. and n. Oxford University Press (Oxford), 2013. Alternatively, for the purposes of agricultural statistics,The World Bank. Agricultural land (% of land area) http://data.worldbank.org/indicator/AG.LND.AGRI.ZS the term often has a more precise definition: A more concise definition appearing in the Eurostat glossary similarly refers to actual rather than potential uses: land worked (ploughed or tilled) regularly, generally under a system of crop rotation .Eurostat. Glossary: Arable land. http://ec.europa.eu/eurostat/statistics-explained/index.php/Glossary:Arable_land In Britain, arable land has traditionally been contrasted with pasturable land such as heaths, which could be used for sheep-rearing but not as farmland. Arable land is vulnerable to land degradation and some types of un-arable land can be enriched to create useful land. Climate ch" +983,"for the production of cultivated crops, but is suitable for uncultivated production of forage usable by grazing livestock.Agricultural Land Commission. Agriculture Capability and the ALR Fact Sheet. http://www.alc.gov.bc.ca/alc/DownloadAsset?assetId=72876D8604EC45279B8D3C1B14428CF8&filename=agriculture_capability__the_alr_fact_sheet_2013.pdf Similar examples can be found in many rangeland areas elsewhere. == Changes in arability == === Land conversion === Land incapable of being cultivated for the production of crops can sometimes be converted to arable land. New arable land makes more food and can reduce starvation. This outcome also makes a country more self-sufficient and politically independent, because food importation is reduced. Making non-arable land arable often involves digging new irrigation canals and new wells, aqueducts, desalination plants, planting trees for shade in the desert, hydroponics, fertilizer, nitrogen fertilizer, pesticides, reverse osmosis water processors," +984,"Aluminium (or aluminum in North American English) is a chemical element; it has symbol Al and atomic number 13. Aluminium has a density lower than that of other common metals, about one-third that of steel. It has a great affinity towards oxygen, forming a protective layer of oxide on the surface when exposed to air. Aluminium visually resembles silver, both in its color and in its great ability to reflect light. It is soft, nonmagnetic, and ductile. It has one stable isotope, 27Al, which is highly abundant, making aluminium the twelfth-most common element in the universe. The radioactivity of 26Al, a more unstable isotope, leads to it being used in radiometric dating. Chemically, aluminium is a post-transition metal in the boron group; as is common for the group, aluminium forms compounds primarily in the +3 oxidation state. The aluminium cation Al3+ is small and highly charged; as such, it has more polarizing power, and bonds formed by aluminium have a more covalent character. The st" +985,"surface of the metal; this may be prevented if aluminium is anodized, which adds a protective layer of oxide on the surface. The density of aluminium is 2.70 g/cm3, about 1/3 that of steel, much lower than other commonly encountered metals, making aluminium parts easily identifiable through their lightness. Aluminium's low density compared to most other metals arises from the fact that its nuclei are much lighter, while difference in the unit cell size does not compensate for this difference. The only lighter metals are the metals of groups 1 and 2, which apart from beryllium and magnesium are too reactive for structural use (and beryllium is very toxic). Aluminium is not as strong or stiff as steel, but the low density makes up for this in the aerospace industry and for many other applications where light weight and relatively high strength are crucial. Pure aluminium is quite soft and lacking in strength. In most applications various aluminium alloys are used instead because of their" +986,"as such; and only incomplete hydrolysis takes place for salts with strong acids, such as the halides, nitrate, and sulfate. For similar reasons, anhydrous aluminium salts cannot be made by heating their hydrates : hydrated aluminium chloride is in fact not AlCl3·6H2O but [Al(H2O)6]Cl3, and the Al–O bonds are so strong that heating is not sufficient to break them and form Al–Cl bonds instead: :2[Al(H2O)6]Cl3 Al2O3 + 6 HCl + 9 H2O All four trihalides are well known. Unlike the structures of the three heavier trihalides, aluminium fluoride (AlF3) features six-coordinate aluminium, which explains its involatility and insolubility as well as high heat of formation. Each aluminium atom is surrounded by six fluorine atoms in a distorted octahedral arrangement, with each fluorine atom being shared between the corners of two octahedra. Such {AlF6} units also exist in complex fluorides such as cryolite, Na3AlF6. AlF3 melts at and is made by reaction of aluminium oxide with hydrogen fluoride gas" +987,"bonds. The industrially most important aluminium hydride is lithium aluminium hydride (LiAlH4), which is used as a reducing agent in organic chemistry. It can be produced from lithium hydride and aluminium trichloride. The simplest hydride, aluminium hydride or alane, is not as important. It is a polymer with the formula (AlH3)n, in contrast to the corresponding boron hydride that is a dimer with the formula (BH3)2. ==Natural occurrence== === Space === Aluminium's per-particle abundance in the Solar System is 3.15 ppm (parts per million). It is the twelfth most abundant of all elements and third most abundant among the elements that have odd atomic numbers, after hydrogen and nitrogen. The only stable isotope of aluminium, 27Al, is the eighteenth most abundant nucleus in the universe. It is created almost entirely after fusion of carbon in massive stars that will later become Type II supernovas: this fusion creates 26Mg, which upon capturing free protons and neutrons, becomes aluminium" +988,"independently developed in 1886 by French engineer Paul Héroult and American engineer Charles Martin Hall; it is now known as the Hall–Héroult process. The Hall–Héroult process converts alumina into metal. Austrian chemist Carl Joseph Bayer discovered a way of purifying bauxite to yield alumina, now known as the Bayer process, in 1889. Modern production of aluminium is based on the Bayer and Hall–Héroult processes. As large-scale production caused aluminium prices to drop, the metal became widely used in jewelry, eyeglass frames, optical instruments, tableware, and foil, and other everyday items in the 1890s and early 20th century. Aluminium's ability to form hard yet light alloys with other metals provided the metal with many uses at the time. During World War I, major governments demanded large shipments of aluminium for light strong airframes; during World War II, demand by major governments for aviation was even higher. By the mid-20th century, aluminium had become a part of everyd" +989,"spelling there outside science. In 1892, Hall used the spelling in his advertising handbill for his new electrolytic method of producing the metal, despite his constant use of the spelling in all the patents he filed between 1886 and 1903. It is unknown whether this spelling was introduced by mistake or intentionally, but Hall preferred aluminum since its introduction because it resembled platinum, the name of a prestigious metal. By 1890, both spellings had been common in the United States, the spelling being slightly more common; by 1895, the situation had reversed; by 1900, aluminum had become twice as common as aluminium; in the next decade, the spelling dominated American usage. In 1925, the American Chemical Society adopted this spelling. The International Union of Pure and Applied Chemistry (IUPAC) adopted aluminium as the standard international name for the element in 1990. In 1993, they recognized aluminum as an acceptable variant; the most recent 2005 edition of the IUPAC nom" +990,"aluminium that can be extracted industrially. The process produces aluminium billets, together with a highly complex waste material. This waste is difficult to manage. It reacts with water, releasing a mixture of gases (including, among others, hydrogen, acetylene, and ammonia), which spontaneously ignites on contact with air; contact with damp air results in the release of copious quantities of ammonia gas. Despite these difficulties, the waste is used as a filler in asphalt and concrete. == Applications == === Metal === The global production of aluminium in 2016 was 58.8 million metric tons. It exceeded that of any other metal except iron (1,231 million metric tons). Aluminium is almost always alloyed, which markedly improves its mechanical properties, especially when tempered. For example, the common aluminium foils and beverage cans are alloys of 92% to 99% aluminium. The main alloying agents are copper, zinc, magnesium, manganese, and silicon (e.g., duralumin) with the levels of o" +991,"doses (>50 g/day) can cause anemia. During the 1988 Camelford water pollution incident people in Camelford had their drinking water contaminated with aluminium sulfate for several weeks. A final report into the incident in 2013 concluded it was unlikely that this had caused long-term health problems. Aluminium has been suspected of being a possible cause of Alzheimer's disease, but research into this for over 40 years has found, , no good evidence of causal effect. Aluminium increases estrogen-related gene expression in human breast cancer cells cultured in the laboratory. In very high doses, aluminium is associated with altered function of the blood–brain barrier. A small percentage of people have contact allergies to aluminium and experience itchy red rashes, headache, muscle pain, joint pain, poor memory, insomnia, depression, asthma, irritable bowel syndrome, or other symptoms upon contact with products containing aluminium. Exposure to powdered aluminium or aluminium welding fumes" +992,"Advanced Chemistry is a German hip hop group from Heidelberg in Baden-Württemberg, South Germany. Advanced Chemistry was founded in 1987 by Toni L, Linguist, Gee-One, DJ Mike MD (Mike Dippon) and MC Torch. Each member of the group holds German citizenship, and Toni L, Linguist, and Torch are of Italian, Ghanaian, and Haitian backgrounds, respectively.Pennay, Mark Rap in Germany in Mitchell, Tony ed. Global Noise. Middletown: Wesleyan University Press, 2001. Influenced by North American socially conscious rap and the Native tongues movement, Advanced Chemistry is regarded as one of the main pioneers in German hip hop. They were one of the first groups to rap in German (although their name is in English). Furthermore, their songs tackled controversial social and political issues, distinguishing them from early German hip hop group Die Fantastischen Vier (The Fantastic Four), which had a more light-hearted, playful, party image. ==Career== Advanced Chemistry frequently rapped about their" +993,"by asserting their Germanness as a group composed primarily of ethnic others. The response to this pseudo-German authenticity can be seen in what Andy Bennett refers to as alternative forms of local hip hop culture which actively seek to rediscover and, in many cases, reconstruct notions of identity tied to cultural roots. Bennett, Andy. Hip-Hop am Main, Rappin' on the Tyne: Hip-hop Culture as a Local Construct in Two European Cities. In That's the Joint!: The Hip-hop Studies Reader, 177-200. New York; London: Routledge, 2004. These alternative local hip hop cultures include oriental hip hop, the members of which cling to their Turkish heritage and are confused by Advanced Chemistry's elicitation of a German identity politics to which they technically do not belong. This cultural binary illustrates that rap has taken different routes in Germany and that, even among an already isolated immigrant population, there is still disunity and, especially, disagreement on the relative importance" +994,"The Anglican Communion is the third largest Christian communion after the Roman Catholic and Eastern Orthodox churches. Formally founded in 1867 in London, the communion has more than 85 million members within the Church of England and other autocephalous national and regional churches in full communion. The traditional origins of Anglican doctrine are summarised in the Thirty-nine Articles (1571). The archbishop of Canterbury (, Justin Welby) in England acts as a focus of unity, recognised as ( first among equals ), but does not exercise authority in Anglican provinces outside of the Church of England. Most, but not all, member churches of the communion are the historic national or regional Anglican churches. The Anglican Communion was officially and formally organised and recognised as such at the Lambeth Conference in 1867 in London under the leadership of Charles Longley, Archbishop of Canterbury. The churches of the Anglican Communion consider themselves to be part of the one, hol" +995,"England. A crucial step in the development of the modern communion was the idea of the Lambeth Conferences (discussed above). These conferences demonstrated that the bishops of disparate churches could manifest the unity of the church in their episcopal collegiality despite the absence of universal legal ties. Some bishops were initially reluctant to attend, fearing that the meeting would declare itself a council with power to legislate for the church; but it agreed to pass only advisory resolutions. These Lambeth Conferences have been held roughly every ten years since 1878 (the second such conference) and remain the most visible coming-together of the whole communion. The Lambeth Conference of 1998 included what has been seen by Philip Jenkins and others as a watershed in global Christianity . The 1998 Lambeth Conference considered the issue of the theology of same-sex attraction in relation to human sexuality. At this 1998 conference for the first time in centuries the Christians of" +996,"Church of Ireland recognised that it will treat civil partners the same as spouses . The Anglican Church of Australia does not have an official position on homosexuality. The conservative Anglican churches encouraging the realignment movement are more concentrated in the Global South. For example, the Anglican Church of Kenya, the Church of Nigeria and the Church of Uganda have opposed homosexuality. GAFCON, a fellowship of conservative Anglican churches, has appointed missionary bishops in response to the disagreements with the perceived liberalisation in the Anglican churches in North America and Europe. In 2023, ten archbishops within the Anglican Communion and two breakaway churches in North America and Brazil from the Global South Fellowship of Anglican Churches (GSFA) declared a state of impaired communion with the Church of England and announced that they would no longer recognise the archbishop of Canterbury as the first among equals among the bishops in the Anglican Communion." +997,"It is held roughly every ten years and invitation is by the archbishop of Canterbury. # The Anglican Consultative Council (first met in 1971) was created by a 1968 Lambeth Conference resolution, and meets usually at three-yearly intervals. The council consists of representative bishops, other clergy and laity chosen by the 38 provinces. The body has a permanent secretariat, the Anglican Communion Office, of which the archbishop of Canterbury is president. # The Primates' Meeting (first met in 1979) is the most recent manifestation of international consultation and deliberation, having been first convened by Archbishop Donald Coggan as a forum for leisurely thought, prayer and deep consultation .Jeremy Morris, The Oxford History of Anglicanism, Volume IV: Global Western Anglicanism, c. 1910–Present (Oxford University Press, 2017), 320–22. Since there is no binding authority in the Anglican Communion, these international bodies are a vehicle for consultation and persuasion. In recent tim" +998,"Year Established Year Dissolved Chung Hua Sheng Kung Hui China 1912 1949 (1958) Church of Hawaii Hawaii 1862 1902 Church of India, Pakistan, Burma and Ceylon Bangladesh, India, Myanmar, Pakistan, Sri Lanka 1930 1970 Protestant Episcopal Church in the Confederate States of America Confederate States of America 1861 1865 United Church of England and Ireland England, Wales, Ireland 1800 1871 ===New provinces in formation=== In September 2020, the Archbishop of Canterbury announced that he had asked the bishops of the Church of Ceylon to begin planning for the formation of an autonomous province of Ceylon, so as to end his current position as metropolitan of the two dioceses in that country. ===Churches in full communion=== In addition to other member churches, the churches of the Anglican Communion are in full communion with the Old Catholic churches of the Union of Utrecht and the Scandinavian Lutheran churches of the Porvoo Communion in Europe, the India-based Malankara Mar Thoma Syrian" +999,"Arne Kaijser (born 1950) is a professor emeritus of history of technology at the KTH Royal Institute of Technology in Stockholm, and a former president of the Society for the History of Technology. Kaijser has published two books in Swedish: Stadens ljus. Etableringen av de första svenska gasverken and I fädrens spår. Den svenska infrastrukturens historiska utveckling och framtida utmaningar, and has co-edited several anthologies. Kaijser is a member of the Royal Swedish Academy of Engineering Sciences since 2007 and also a member of the editorial board of two scientific journals: Journal of Urban Technology and Centaurus. Lately, he has been occupied with the history of Large Technical Systems. == References == == External links == * Homepage * Extended homepage Category:1950 births Category:Living people Category:20th-century Swedish historians Category:Academic staff of the KTH Royal Institute of Technology Category:Members of the Royal Swedish Academy of Engineering Sciences Catego" +1000,"An archipelago ( ), sometimes called an island group or island chain, is a chain, cluster, or collection of islands, or sometimes a sea containing a small number of scattered islands. Examples of archipelagos include: the Indonesian Archipelago, the Aleutian Islands, the Lakshadweep Islands, the Galápagos Islands, the Japanese archipelago, the Philippine Archipelago, the Maldives, the Balearic Islands, the Åland Islands, The Bahamas, the Aegean Islands, the Hawaiian Islands, the Canary Islands, Malta, the Azores, the Canadian Arctic Archipelago, the British Isles, the islands of the Archipelago Sea, and Shetland. Archipelagos are sometimes defined by political boundaries. For example, while they are geopolitically divided, the San Juan Islands and Gulf Islands geologically form part of a larger Gulf Archipelago. ==Etymology== The word archipelago is derived from the Ancient Greek ἄρχι-(arkhi-, chief ) and πέλαγος (pélagos, sea ) through the Italian arcipelago. In antiquity, Archipelago" diff --git a/test/distributed/resources/fulltext2/wiki_zh_1k.csv b/test/distributed/resources/fulltext2/wiki_zh_1k.csv new file mode 100644 index 0000000000000..a1bbdb9283d12 --- /dev/null +++ b/test/distributed/resources/fulltext2/wiki_zh_1k.csv @@ -0,0 +1,1000 @@ +1,"数学是研究數量、结构以及空间等概念及其变化的一門学科,屬於形式科學的一種。數學利用抽象化和邏輯推理,從計數、計算、量度、對物體形狀及運動的觀察發展而成。數學家們拓展這些概念,以公式化新的猜想,以及從選定的公理及定義出發,嚴謹地推導出一些定理。 基礎數學的知識與運用是生活中不可或缺的一環。對數學基本概念的完善,早在古埃及、美索不达米亚及古印度历史上的古代數學文本便可觀見,而在古希臘那裡有更為嚴謹的處理。從那時開始,數學的發展便持續不斷地小幅進展,至16世紀的文藝復興時期,因为新的科學發現和數學革新兩者的交互,致使數學的加速发展,直至今日。数学并成为許多國家及地區的教育中的一部分。 数学在许多领域都有应用,包括科學、工程、醫學、經濟學和金融學等。數學對這些領域的應用通常被稱為應用數學,有時亦會激起新的數學發現,並導致全新學科的發展,例如物理学的实质性发展中建立的某些理论激发数学家对于某些问题的不同角度的思考。數學家也研究純粹數學,就是數學本身的实质性內容,而不以任何實際應用為目標。許多研究雖然以純粹數學開始,但其过程中也發現許多可用之处。 == 詞源 == 西方语言中“數學”()一詞源自於" +2,"数。」东汉的郑玄在他的《周礼注疏·地官司徒·保氏》中引郑司农(郑众)所言:「九数:方田、粟米、差分、少广、商功、均输、方程、赢不足、旁要,今有重差、夕桀、勾股也。」,數學一詞在西方有希腊语詞源(mathematikós),意思是“学问的基础”,源于(máthema,“科学,知识,学问”)。 史前的人類就已嘗試用自然的法則來衡量物質的多少、時間的長短等抽象的數量關係,比如时间单位有日、季節和年等。算術(加減乘除)也自然而然地產生了。古代的石碑及泥版亦證實了當時已有幾何的知識。 更進一步則需要寫作或其他可記錄數字的系統,如符木或於印加帝國內用來儲存數據的奇普。歷史上曾有過許多不同的記數系統。 在最初有歷史記錄的時候,數學內的主要原理是為了做稅務和貿易等相關計算,為了解數字間的關係,為了測量土地,以及為了預測天文事件而形成的。这些可以简单地被概括为数学对數量、结构、空间及时间方面的研究。 到了16世纪,算术、初等代数以及三角学等初等数学已大体完备。17世纪变量概念的产生使人们开始研究变化中的量与量的互相关系和图形间的互相变换,微积分的概念也在此時形成。随着數學轉向形式化,为研究数学基础而产生" +3,"通常亦有實際的用途,此一非比尋常的事實,被1963年諾貝爾物理獎得主維格納稱為「數學在自然科學中不可想像的有效性」 。 如同大多數的研究領域,科學知識的爆發導致了數學的專業化。主要的分歧為純數學和應用數學。在應用數學內,又被分成兩大領域,並且變成了它們自身的學科——統計學和電腦科學。 許多數學家談論數學的優美,其內在的美學及美。「簡單」和「一般化」即為美的一種。另外亦包括巧妙的證明,如歐幾里得對存在無限多質數的證明;又或者是加快計算的數值方法,如快速傅立葉變換。高德菲·哈羅德·哈代在《一個數學家的自白》一书中表明他相信單單是美學上的意義,就已經足夠成為數學研究的正當理由。 == 符號、語言與精确性 == 我們現今所使用的大部分數學符號在16世紀後才被發明出來的。在此之前,數學以文字的形式書寫出來,這種形式會限制了數學的發展。現今的符號使得數學對於專家而言更容易掌握,但初學者卻常對此望而却步。它被極度的壓縮:少量的符號包含著大量的訊息。如同音樂符號一般,現今的數學符號有明確的語法,並且有效地對訊息作編碼,這是其他書寫方式難以做到的。符号化和形式化使得数学迅速发展,并帮助各个科学领域建立基" +4,"不完備定理,每一相容且能蘊涵皮亞諾公理的公理系統必含有一不可決定的公式;因而所有數學的最終公理化是不可能的。儘管如此,數學常常被想像成只是某種公理化的集合論,在此意義下,所有數學敘述或證明都可以寫成集合論的公式。 == 數學作為科學 == 卡爾·弗里德里希·高斯稱數學為「科學的皇后」。在拉丁原文,以及其德語中,對應於「科學」的單字的意思皆為知識(領域)。而實際上,science一詞在英語內本來就是這個意思,且無疑問地數學在此意義下確實是一門「科學」。將科學限定在自然科學則是在此之後的事。若認為科学是只指物理的世界時,則數學,或至少是純數學,不會是一門科學。愛因斯坦曾如此描述:「數學定律越和現實有關,它們越不確定;若它們越是確定的話,它們和現實越不會有關。」許多哲學家相信數學在經驗上不具可否證性,且因此不是卡爾·波普爾所定義的科学。但在1930年代時,在數理邏輯上的重大進展顯示數學不能歸併至邏輯內,且波普爾推斷「大部份的數學定律,如物理及生物學一樣,是假設演繹的:純數學因此變得更接近其假設為猜測的自然科學,比它現在看起來更接近。」然而,其他的思想家,如較著名的拉卡托斯,便提供了一個關於數" +5,"。大学院系划分中常见「科学和数学系」,这指出了这两个领域被看作有緊密聯繫而非一樣。實際上,數學家通常會在大體上與科學家合作,但在細節上卻會分開。此爭議亦是數學哲學眾多議題的其中一個。 == 數學的各領域 == 如上所述,數學主要的學科最先產生於商業上計算的需要、了解數字間的關係、測量土地及預測天文事件。這四種需要大致地與數量、結構、空間及變化(即算術、代數、幾何及分析)等數學上廣泛的子領域相關連著。除了上述主要的關注之外,亦有用來探索由數學核心至其他領域上之間的連結的子領域:至邏輯、至集合論(基礎)、至不同科學的經驗上的數學(應用數學)、及較近代的至不確定性的嚴格研究。 === 基礎與哲學 === 為了闡明數學基礎,數學邏輯和集合論等領域被發展了出來。 數學邏輯專注於將數學置在一堅固的公理架構上,並研究此一架構的結果。就數學邏輯本身而言,其為哥德爾第二不完備定理所屬的領域,而這或許是邏輯學中最廣為流傳的成果:總是存在不能被證明的真命題。 現代邏輯被分成遞歸論、模型論和證明論,且和理論電腦科學有著密切的關連性,千禧年大獎難題中的P/NP問題就是理論電腦科學中的著名問題。 : P \Rig" +6,"b{R} \mathbb{C} ==== 結構 ==== 許多如數及函數的集合等數學物件都有著內含的結構。這些物件的結構性質被探討於群、環、-{zh-cn:域;zh-tw:體}-等抽象系統中,該些物件事實上也就是這樣的系統。此為代數的領域。在此有一個很重要的概念,即廣義化至向量空間的向量,它於線性代數中被研究。向量的研究結合了數學的三個基本領域:數量、結構及空間。向量分析則將其擴展至第四個基本的領域內,即變化。 创立于二十世纪三十年代的法国的布尔巴基学派认为:纯粹数学,是研究抽象结构的理论。 结构,就是以初始概念和公理出发的演绎系统。 布尔巴基学派认为,有三种基本的抽象结构:代数结构(群,环,域……),序结构(偏序,全序……),拓扑结构(邻域,极限,连通性,维数……)。 : 數論 群論 圖論 序理論 ==== 空間 ==== 空間的研究源自於幾何-尤其是欧几里得几何。三角學則結合了空間及數,且包含有著名的勾股定理。現今對空間的研究更推廣到了更高維的幾何、非歐幾里得幾何(其在廣義相對論中扮演著核心的角色)及拓撲學。數和空間在解析幾何、微分幾何和代數幾何中都有著很重要的角色。在微分幾何中有" +7,"微分方程 動力系統 混沌理論 複分析 === 離散數學 === 離散數學是指對理論電腦科學最有用處的數學領域之總稱,這包含有可計算理論、計算複雜性理論及資訊理論。可計算理論檢驗電腦的不同理論模型之極限,這包含現知最有力的模型——圖靈機。複雜性理論研究可以由電腦做為較易處理的程度;有些問題即使理論是可以以電腦解出來,但卻因為會花費太多的時間或空間而使得其解答仍然不為實際上可行的,儘管電腦硬體的快速進步。最後,資訊理論專注在可以儲存在特定媒介內的資料總量,且因此有壓縮及熵等概念。 作為一相對較新的領域,離散數學有許多基本的未解問題。其中最有名的為P/NP問題——千禧年大獎難題之一。一般相信此問題的解答是否定的。P=NP的民調 顯示2005年大眾相信它並不相等。(看section 5) : \begin{matrix} (1,2,3) & (1,3,2) \\ (2,1,3) & (2,3,1) \\ (3,1,2) & (3,2,1) \end{matrix} 組合數學 計算理論 密碼學 圖論 === 應用數學 === 應用數學思考將抽象的數學工具運用在解答科學、工商業及其他領域上之現實問" +8,":博弈论}- File:Front pareto.svg |數理經濟學 File:Signal transduction pathways (zh-cn).svg |生物數學 File:Linear programming example graph (zh).png |作業研究 File:Simple feedback control loop2.svg|控制論 == 数学奖项 == 數學獎通常和其他科學的獎項分開。數學上最有名的獎為菲爾茲獎,創立於1936年,每四年頒獎一次。它通常被認為是數學領域的諾貝爾獎。另一個國際上主要的獎項為阿貝爾獎,創立於2003年。兩者都頒獎於特定的工作主題,包括數學新領域的創新或已成熟領域中未解決問題的解答。著名的23個問題,稱為希爾伯特的23個問題,於1900年由德國數學家大衛·希爾伯特所提出。這一連串的問題在數學家之間有著極高的名望,且至少有九個問題已經被解答了出來。另一新的七個重要問題,稱為千禧年大獎難題,發表於2000年。對其每一個問題的解答都有著一百萬美元的獎金,而當中只有一個問題(黎曼猜想)和希爾伯特的問題重複。 *菲尔兹奖,由国际数学联盟" +9,"t of Proof: Mathematical Epiphanies, Oxford University Press, USA; New Ed edition (December 14, 2000). ISBN 978-0-19-513919-8. * Boyer, Carl B., A History of Mathematics, Wiley; 2 edition (March 6, 1991). ISBN 978-0-471-54397-8.—A concise history of mathematics from the Concept of Number to contemporary Mathematics. * Courant, R. and H. Robbins, What Is Mathematics? : An Elementary Approach to Ideas and Methods, Oxford University Press, USA; 2 edition (July 18, 1996). ISBN 978-0-19-510519-3. * D" +10,"of a Soviet mathematics encyclopedia, in ten (expensive) volumes, the most complete and authoritative work available. Also in paperback and on CD-ROM, and online . * Kline, Morris, Mathematical Thought from Ancient to Modern Times, Oxford University Press, USA; Paperback edition (March 1, 1990). ISBN 978-0-19-506135-2. * * Pappas, Theoni, The Joy Of Mathematics, Wide World Publishing; Revised edition (June 1989). ISBN 978-0-933174-65-8. * * == 外部链接 == * Rusin, Dave: The Mathematical Atlas(英文版)现代" +11,"哲學是研究普遍的、基本问题的学科,包括存在、知识、价值、理智、心灵、语言、人生、道德等领域。哲学与其他学科不同之處在於哲學有獨特之思考方式,例如批判的方式、通常是系统化的方法,并以理性论证為基礎。从历史上看,许多单独的学科,例如物理学、生物学等自然科学,或法学、政治学、心理学等社会科学,都曾被视作哲学的一部分或其分支学科。直至其得到后续发展后,才逐渐被视作现代意义上的独立学科。 哲学的词义在现代末期发生了变化,成为了今天常见的、更狭隘的含义。在这个新意义上,该术语主要与形而上学、认识论、伦理学和美学等哲学学科相关。除其他主题外,它涵盖了对现实、知识和价值观的理性研究。然而,它不同于其他理性探究学科,例如经验科学和数学。 == 词源 == 英語詞語()源于古希臘語中的,意思為「愛智慧」,有时也译为「智慧的朋友」,该词由(philos,爱)的派生词(philein,去爱)和(sophia,智慧)组合而成。一般认为,古希腊思想家毕达哥拉斯最先在著作中引入“哲学家”和“哲学”这两个术语。 “哲”一词在中国古代指那些善于思辨、学问精深者,如“孔门十哲”、“古圣先哲”等,“哲”或“哲人”意义类似西" +12,"C6F91CBD86DA5D3E578DE806F130F3A0 英国哲学家罗素对哲学的定义是: 胡適在《中国哲学史大纲》中称「凡研究人生切要的问题,从根本上着想,要寻一个根本的解决:这种学问叫做哲学」。 雖然哲學源自西方的傳統,但許多文明在歷史上都存在著一些相似的論題。東亞和南亞的哲學被稱之為東方哲學,而北非和中東則因為其和歐洲密切的互動,因此常被視為是西方哲學的一部份。 對哲學的主題亦存在許多看法。一些人認為哲學是對問題本身過程的觀察。 *後現代主義把哲學定義為創造概念的學術。 *哲学所涉及的研究范畴是其它学科的总和,它给出对世界本质的解释,在很大程度上影响着接受者的世界观。 *哲学是研究范畴及其相互关系的一门学问。范畴涉及到一门学科的最基本的定義、概念和内容,哲学具有一般方法论的功能。 *哲學和其他陈述問題方法的差異是有批判性的、有代入問題的方法以及以理性為答案的辯論。 === 研究基礎 === 古希臘哲學家經常提出問題,他們所提出的問題大概可以歸類為三類,這三類問題分別形成了哲學的基礎學科——分别是形而上学、伦理学、认识论 。 现代哲学上出現 不要求精确理由 之哲学理論,例如" +13,"、阿耆多·翅舍欽婆羅、波拘陀·迦旃延、富蘭那·迦葉、數論派等。 ==== 中国 ==== 中國哲學的主要部分起源東周時期,当时以诸子百家广为人知,以孔子的儒家、老子的道家、墨子的墨家及晚期的法家為代表,还有一些流派例如农家、阴阳家和名家在之后则名声不显。在秦朝焚书坑儒后除了法家、儒家、道家外其他流派都不再活跃。在當代,中國哲學仍然在亞洲文化扮演一定作用,但是學理上仍在爭辯中國哲學是否應歸為哲學。 如牟宗三曾對哲學下定義:「凡是對人性的活動所及,以理智及觀念加以反省說明的,便是哲學。」牟宗三認為,中國有數千年文化史,確有哲學。只是中國哲學重視主體性,西方重視客體性。因此如果以西方的邏輯和知識論等對哲學下定義,中國沒有這些。但如果以主體性方向對哲學下定義,中國文化就擁有哲學。反之西方對人生的哲學多表現在文學、藝術、音樂等等。就以西方哲學史來說,沒有一章特別談及耶穌。 ==== 古希腊-罗马 ==== 古希腊-罗马哲学是西方哲学的一个时期,时间为公元前6世纪[约585]到公元6世纪。它一般被分为三个时期:前苏格拉底时期、柏拉图和亚里士多德的古典希腊时期、和后亚里士多德(或希腊化)时期:有时候" +14,"熊燃烧的火焰,火就是一切事物的本质。他更加暗示世界中的变存在着秩序和理性。 * 多元论者,如恩培多克勒提出的“四元素”;阿那克萨戈拉的“种子”,即粒子论;德谟克利特又指出了“原子”,即原子论等。皆认为变是若干物体的不同组合。 公元前5世纪中期,普罗泰戈拉和高尔吉亚等所形成的辯士學派将研究的重点由自然转移到人类本身。认为“人才是万物之本”。他们都不相信有真正的存在和真理。普罗泰戈拉认为是非善恶都是相对于人的感觉而言,而高尔吉亚却认为所有的都是同样的假,这是怀疑论的雏形。 公元前6世纪末,以毕达哥拉斯为主的毕达哥拉斯学派所主张的哲学与前述的观点既相近又有不同。罗马古代的历史上记载毕达哥拉斯第一个称自己为哲学家,或者说是爱智慧。他认为“一切都是数字”。其意思就是说一切事物的实质和结构都是它们所包含的数字关系所决定的。他称平均、秩序和调和是宇宙的三大基调,并以音乐的调和说明宇宙的调和。他所在的学派将宇宙总结为十种性质相异的组合:有限与无限、奇与偶、少与多、左与右、男与女、静与动、直与曲、光明与黑暗、善与恶、方与圆。至此之后,数学的本质及其地位,一直都是哲学的主要问题之一,数学不受观察和实验造成" +15,"图被认为是西方哲学的创始人。哲学家阿爾弗雷德·諾思·懷特黑德评价柏拉图:“欧洲哲学传统最被普遍公认的特点,就是它包含了一系列对柏拉图的注脚。我的意思不是怀疑学者们系统体系的思想是提取自柏拉图的著作。我暗示的是那些他们散落的一般思想的财富。”換言之即使數千年後,人們依舊在試著回答他所提出的問題,這也代表著人們依然為這些問題或是這些問題所延伸的更多問題而感到困惑。 毕达哥拉斯的思想对柏拉图产生了显著的影响,并通过柏拉图影响了整个西方哲学。柏拉图和亚里士多德作为最早的古典希腊哲学家批判地引用了其它的一些“智者”,当时这些人在希腊被称为“辩士”并在毕达哥拉斯之前相当普遍。从他们的批判看来,在他们的古典时代一个在更高尚地、纯粹地“爱智慧”(真的哲学家)与那些更早更普遍的旅行教师——经常也通过自己的技艺来赚钱——之间的分水岭之后被建立。 ===== 希腊化时代 ===== 亚里士多德死后,整个哲学界陷入了独立时期,称为希腊化哲学时期。因为整个社会和政治陷入混乱。这段时期产生了斯多葛学派和伊壁鸠鲁学派,以及怀疑主义派、新柏拉图派和新毕达哥拉斯主义。这些学派的共同特点是伦理化。斯多葛学派主要是顺应自然" +16,"展。认为 在强度、复杂度还有成就上,可以确信地说哲学在十三世纪的兴盛能与公元前四世纪古希腊哲学的黄金时期媲美。 这个时代讨论的问题有信仰和理智的关系,神的存在与统一,神学话题和形而上学,关于知识、宇宙和个人的问题。 中世纪的哲学家包括基督教学者希波的奥古斯丁、波爱修斯、安瑟倫、、皮埃尔·阿伯拉尔、罗吉尔·培根、圣文德、托马斯·阿奎那、邓斯·司各脱、奥卡姆的威廉和让·布里丹等;犹太哲学家迈蒙尼德和吉尔松尼德;还有穆斯林哲学家肯迪、法拉比、海什木、伊本·西那、安薩里、伊本·巴哲、伊本·图费勒、伊本·赫勒敦和伊本·魯世德等。中世纪的经院哲学传统一直到17世纪仍在活跃,例如弗朗西斯科·苏亚雷斯和等人物。其中托马斯主义之父阿奎那极大地影响了整个天主教欧洲,他特别强调理性和论证,是最先开始使用亚里士多德形而上学和认识论的著作的新译本的学者之一。他的工作明显远离了统治大部分早期经院哲学的新柏拉图主义和奥古斯丁的思想。 ====文艺复兴==== 从文艺复兴开始,人们的思想开始从清净的僧院走出,来到喧嚣的世界。从而发展自然,也发展人类自身。从而形成人文主义和自然哲学两股既有联系又有区别的想法。 文艺复兴" +17,"焦爾達諾·布魯諾、波纳蒂特·特勒肖还有托马索·康帕内拉的著作中。在自然哲学中这样的运动与对神秘主义、魔法、赫尔墨斯主义还有占星学等兴趣重燃相契合,它们被认为隐藏着收获知识和掌控自然方法的大门。 这些新的哲学运动伴随着欧洲宗教和政治的剧变同时出现:宗教改革和封建制的衰落。虽然参与宗教改革的神学家们对哲学没有直接的兴趣,他们打破了神学和知识权威的传统基础。同时还伴随着信仰主义和怀疑主义的复兴,体现在伊拉斯谟,蒙泰涅和等思想家身上。同时,民族国家政治上逐步的中央集权的过程得到了世俗政治哲学的响应,如尼可罗·马基亚维利(常被描述为第一个现代政治思想家,或者是现代政治思想形成的关键点)、托马斯·莫尔、伊拉斯谟、尤斯图斯·利普修斯、让·博丹和胡果·格老秀斯等的著作。 ====东亚==== 先秦諸子之后的兩漢經學、魏晉玄學等都是中國哲學的一部份,自唐朝起佛教也开始对哲学产生重要影响;不过中世纪中国哲学最主要的部分是宋明理學的发展。 宋明理学反对汉代后开始影响儒学的道教和佛教中的迷信和神秘的元素,是一股倡导更加理性和世俗化儒学的哲学运动。尽管理学遭到道教和佛教徒的批评,理学仍借鉴了它们两个的部分术语和" +18,"和政治哲学的世俗基础 。这种潮流最早被鲜明地体现在弗兰西斯·培根的被称为用来扩展知识的新的、经验主义的程序,并很快在笛卡儿的机械主义物理学和理性主义的形而上学中建立了具有巨大影响力的形式。培根运用归纳法,第一个提出思维的主体“人”应该主动干涉自然来为人服务。 近现代政治哲学的鼻祖托马斯·霍布斯最早将这套方法论系统地应用在政治哲学上,包括 社會契約 的近代理论。早期近代哲学的学术经典一般包括笛卡尔、斯賓諾莎、莱布尼茨、洛克、贝克莱、休谟和康德。同时期的其他思想家也对哲学做出了贡献,例如伽利略、皮埃尔·伽桑狄、布莱兹·帕斯卡、马勒伯朗士、艾萨克·牛顿、克里斯蒂安·沃爾夫、孟德斯鸠、皮埃尔·贝尔、托马斯·里德、让·勒朗·达朗贝尔和亚当·斯密,而让-雅克·卢梭是反启蒙运动的开创性人物。早期近代哲学的大致结束通常被确定为伊曼努尔·康德的试图限定形而上学范围、证明科学知识并用道德和自由来调和两者的体系。 理性主义者中勒内·笛卡儿认为物质世界是由数学关系组成的单一体系,他企图将物理学转化为数学。他在其著作中,对整个经院哲学以及在他那个时代流行的教育与哲学体系加以讽刺。其认为“我思故我在”是认识论的无" +19,"。 经验论与唯理论的争论也包含了唯物主义与唯心主义的争论。在18世纪时,法国的拉美特利公开宣布唯物主义是唯一的,而百科全书的主编德尼·狄德罗也怀疑神的存在。 另外伏尔泰,孟德斯鸠和其他百科全书派的学者都有涉及政治和伦理领域。他们都认为机械主义才是最终形式——物质是唯一的且处于一直运动的,精神只是人脑的属性。因此他们认为无机物与有机物不可逾越,人的思维是人感官的结果。不过他们仍然是相信经验是一直累積的,在因果性上,他们认为只有必然性才是唯一的,这就成为唯心主义的观念。 ==== 德国古典哲学 ==== 从18世纪中后期开始,直到19世纪初,哲学便进入了近代哲学的总结时期,这就是德国古典哲学时期。有两条线索标志着转折的到来:一、思维与存在的关系更加明确;二、产生了系统辩证法。其代表人物有I.康德、J.G.费希特、F.W.谢林、G.W.F.黑格尔等。 ===== 康德 ===== 康德给哲学带来了三个标志性的创造: *哲学意义的新规定; *哲学方法的新概念; *哲学写作的新结构。 他受到休谟的诸多影响,并为西方哲学带来一次革命。他认为哲学的研究核心就是规定理性的任务。 康德同意休谟的理论并认" +20,"的出现将西方的哲学推上一个新高度,他创立了西方哲学史上最庞大的客观唯心主义体系,并系统地阐述了辩证法。他的理论和学说对近现代哲学产生了很深远的影响,并被称为黑格尔主义。 从黑格尔的思想体系中发展而成的多种哲学运动。其重点就是以历史和逻辑为主,历史方面,它从不同角度理解“凡是合理的就是现实的”;逻辑方面,它有发现其中所说的“真理即整体”。 黑格尔认为哲学的重点是放弃分裂,达到统一。他把以前的时代说成是思维与存在、理想与现实分裂,自由与必然,个人与社会、无限与有限、统一性与多样性分裂之时代。 他从康德的“心灵的合理性以及在经验中的积极作用”的概念出发,但反对康德的“超越经验世界和‘物自身’的世界”,并认为心灵和世界一样具有相同基础理性结构。他所认为的普遍性不是脱离特殊的抽象普遍,而是包含特殊在内之普遍,即为具体普遍;他所认为的统一也非脱离矛盾、对立的抽象统一,而是包含它们在内的统一,即为对立统一。上述综合在一起即是他的理论:最真实的无所不包的整体即是“绝对精神”,又是对立的统一。 他认为,为了达到这个“绝对精神”,需要经过三个阶段,从逻辑、自然到精神,即是从思维到存在,再到两者统一的过程," +21,"具有影响力,不仅启发了布拉德利、罗伊斯等人的观念论哲学,还形成了一种哲学气候。即形式有所修正,但是本质不变。比如怀特海的“历程哲学”。 主流马克思主义: :K.马克思的著作本身不能说成是一种哲学,更不是一种哲学体系,但他的全部论述是对哲学,特别是对黑格尔体系中的唯心主义进行的激进批判。他认为哲学必须与现实取得联系。 “哲学只是持续地解释世界,而与当下关联的则是改变世界。” :但是尽管马克思本人批判黑格尔的观念论,但其思想却深受黑格尔的特别是辨证论的影响。并将其基本本质作了自然主义的改造。这之后,从19世纪末期到20世纪初期,马克思的哲学思想才在恩格斯、普列汉诺夫和列宁等人的阐释和补充下,为辩证唯物论和历史唯物论,并形成新的世界观。 :另外马克思主义也指各种不同的社会主义运动,如由列宁所创立而被斯大林修改的苏联马克思主义,称为马克思列宁主义,为俄国革命以及后来建立的各种共产党之教义。它的旁系包括反斯大林的托洛茨基及其追随者的马克思主义、毛泽东的马克思列宁主义和其他第三世界马克思主义。 西方马克思主义: :此外,比上述类型更为复杂的版本有阿尔都塞等人做出详尽阐述。然而,在卢卡奇和布洛赫等人" +22,"== 分析哲学: :后古典主义是种哲学观,从培根,笛卡尔到休谟和康德,这股浪潮获得科学的倾向,后来又获得逻辑和语言的,逐渐形成一种基本的分析哲学。 实证主义: :19世纪上半叶英美,“苏格兰常识学派”很普及,后来英国摩尔的思想和20世纪初美国“新实在论者”都发展了它。孔德的实证主义在法国引起了广泛的注意。在美国得到米尔格拉姆的发展,成为“实验主义”,又形成科学性的实证主义。 新康德主义: :实证主义在德国由陆宰和不伦瑞克等人的发展下,形成“新康德主义”,并经由重新发现的休谟主义和黑格尔主义的影响,在英美等地也流行起来。 逻辑实证主义: :一种很注重逻辑的学说,在德摩根、布伦塔诺、迈农和弗雷格等人的努力下,取得了进展。罗素和摩尔发动了分析运动,由此形成分析哲学的雏形。又在维特根斯坦的研究下,将分析哲学进行了重新解释,变成了语言分析问题,从而产生逻辑实证主义。 语言哲学: :二战后,分析哲学更加分化,并形成多个不同的派系。但其本质相差无几,都集中研究“分析”这一概念上来,后来有不少开始研究语言哲学这一分支。 现象学: :而胡塞尔将严谨的数学思想赋予哲学,使得笛卡尔主义进一步激进化,在海德格" +23,"括传统从宗教、艺术到道德、社会,再到科学和认识本身。 实用主义: :因为达尔文发现的进化论,让斯宾格勒发展为一个哲学表达,而柏格森为其发展提供促进作用。在一战后,舍勒、普莱斯纳和盖伦等采用上述学说,开创了“哲学人类学”。 :上列观点在桑塔亚那、杜威等人和怀特海的影响下,重新对黑格尔主义进行理解。由皮尔士和詹姆士等思想家开创了实用主义运动,对“人类行动、利益和目的作出实际考虑”。 存在主义: :而齐克果对人类生命诠释的过程中比实用主义更激进,他通过“信仰跳跃”来获得与上帝的关联。 :海德格尔和雅斯培也对齐克果的“主体性”和实用主义进行研究。雅士培强调人类存在不可能被化解到只能以客观角度加以了解的地步。海德格尔更进一步对“存在的意义”作了研究,他主要对人类存在的特性和结构做了奇特的分析。 :在萨特和加缪的发展下,使之流行并衍生出“存在主义”。萨特发展了海德格尔的理论,他认为人们在对“塑造人类自己”这个问题存在本质性的不确定。 解释学: :研究存在主义的哲学家多采用现象学来解释人类生命和人类条件的问题。而庞蒂使用存在主义和现象学对人类存在作研究。 :从齐克果以来的这些哲学家多注重以文学风格对" +24,"在内的统一,即为对立统一。上述综合在一起即是他的理论:最真实的无所不包的整体即是“绝对精神”,又是对立的统一。黑格尔认为需要经过三个阶段来达到这个“绝对精神”,从逻辑、自然到精神,即是从思维到存在,再到两者统一的过程,从而完成他的统一论。他还将伦理学划归到这个领域,从而在伦理学理论和对思想的理解中提出重要的路线。 ===马克思主义=== 马克思主义哲学是马克思和恩格斯建立的以辩证唯物主义为核心的哲学体系。其认为实践是检验哲学之真理性的唯一标准,哲学应伴随着社会、科学技术和文化的发展而不断发展。其主要思想体系在19世纪70年代主要由恩格斯创立,20世纪20年代在苏联形成完整体系——辩证唯物主义和历史唯物主义,这个体系在后来的社会主义国家推动下得以发展。马克思主义哲学宣称自己的理论体系具有科学性,认为哲学可以成为科学的一部分。同时马克思主义哲学认为哲学还具有意识形态的性质。 另外马克思主义在政治上也指各种不同的共产主义运动,如由列宁所创立而被斯大林修改的苏联马克思主义,称为马克思列宁主义,为俄国革命以及后来建立的各种共产党之教义。它的旁系包括反斯大林的托洛茨基及其追随者的马克思主义、毛泽东" +25,"纪晚期到二十世纪的一些哲学家的工作上,尽管他们的学说相差巨大,但他们都相信哲学思考开始于人类主体——而不仅仅是思维主体,而且包括行为、感知、人类个体。存在主义强调个人、獨立自主和主观经验,認為人存在的意義是無法經由理性思考而得到答案。在存在主义中,个体的出发点的特征是被称为“存在的态度”,或一种面对显然是一个无意义的或荒谬的世界的迷失和混乱的感觉。很多存在主义者还认为传统的体系和哲学学术无论是内容和风格都过于抽象并远离人类经验。 19世纪哲学家克尔凯郭尔和尼采被看作存在主义的先驱,尽管他们没有使用这个术语。然而他们的影响延伸出了存在主义思想。克尔凯郭尔著作主要针对的是黑格尔的唯心主义哲学体系,他认为其忽视或排除了人类的内在主观生命。相反克尔凯郭尔认为 真理是主观的 ,主张对一个现实的人类来说最重要的问题是处理个人与存在内在关系的问题。克尔凯郭尔作为一个基督徒相信宗教信仰的真相是一个主观问题,而且人应该用热情去深思这个问题。 == 理論 == * 利他主義 * * 享樂主義 * 唯物主義 * 唯心主義 * 理想主義 * * 逻辑实证主义 * 悲觀主義 * 乐观主义 * 儒家思想 * 道家" +26,"ng.: Fontana Press, 1992, cop. 1983. xxv, 867 p. ISBN 978-0-00-636965-3 * 艾倫·布洛克, and Oliver Stallybrass, jt. eds. The Harper Dictionary of Modern Thought. New York: Harper & Row, 1977. xix, 684 p. N.B.: First published in England under the title, The Fontana Dictionary of Modern Thought. ISBN 978-0-06-010578-5 * Julia, Didier. Dictionnaire de la philosophie. Responsible éditorial, Emmanuel de Waresquiel; secretariat de rédaction, Joelle Narjollet. [Éd. rev.]. Paris: Larousse, 2006. 301, [1] p." +27,"on, 1994, ISBN 978-0-691-00067-1 * Durant, Will, Story of Philosophy: The Lives and Opinions of the World's Greatest Philosophers, Pocket, 1991, ISBN 978-0-671-73916-4 * Harrison-Barbet, Anthony, Mastering Philosophy. ISBN 978-0-333-69343-8 * Higgins, Kathleen M. and Solomon, Robert C. A Short History of Philosophy. ISBN 978-0-19-510196-6 * Philosophy Now magazine * Russell, Bertrand. The Problems of Philosophy. ISBN 978-0-19-511552-9 * Sinclair, Alistair J. What is Philosophy? An Introduction," +28,"Philosophy, 2012, ISBN 978-1-78046-004-8 === 专题介绍 === * Copleston, Frederick. Philosophy in Russia: From Herzen to Lenin and Berdyaev. ISBN 978-0-268-01569-5 * Critchley, Simon. Continental Philosophy: A Very Short Introduction. ISBN 978-0-19-285359-2 * Hamilton, Sue. Indian Philosophy: a Very Short Introduction. ISBN 978-0-19-285374-5 * Harwood, Sterling, ed., Business as Ethical and Business as Usual(Belmont, CA: Wadsworth Publishing Co., 2000); www.sterlingharwood.com * Imbo, Samuel Oluoch. '" +29,"losophy. ISBN 978-0-19-505292-3 * Scruton, Roger. A Short History of Modern Philosophy. ISBN 978-0-415-26763-2 * Smart, Ninian. World Philosophies. ISBN 978-0-415-22852-7 * Tarnas, Richard. The Passion of the Western Mind: Understanding the Ideas That Have Shaped Our World View. ISBN 978-0-345-36809-6 === 选集 === * Classics of Philosophy(Vols. 1 & 2, 2nd edition)by Louis P. Pojman * Classics of Philosophy: The 20th Century(Vol. 3)by Louis P. Pojman * The English Philosophers from Bacon to Mill by" +30,"Ed.(1999). Metaphysics: An Anthology. Blackwell Philosophy Anthologies. Oxford, Blackwell Publishers Ltd. * The Oxford Handbook of Free Will(2004)edited by Robert Kane * Husserl, Edmund and Donn Welton (1999). The Essential Husserl: Basic Writings in Transcendental Phenomenology, Indiana University Press. ISBN 978-0-253-21273-3 * Cottingham, John. Western Philosophy: An Anthology. 2nd ed. Malden, MA: Blackwell Pub., 2008. Print. Blackwell Philosophy Anthologies. === 参考著作 === * * * The Oxford Com" +31,"ophers. Charlottesville, Philosophy Documentation Center. * Directory of American Philosophers. Charlottesville, Philosophy Documentation Center. * Routledge History of Philosophy(10 vols.)edited by John Marenbon * History of Philosophy(9 vols.)by Frederick Copleston * A History of Western Philosophy(5 vols.)by W. T. Jones * History of Italian Philosophy(2 vols.)by Eugenio Garin. Translated from Italian and Edited by Giorgio Pinton. Introduction by Leon Pompa. * Encyclopaedia of Indian Philosoph" +32,"iedrichs * Companion Encyclopedia of Asian Philosophy by Brian Carr, Indira Mahalingam * A Concise Dictionary of Indian Philosophy: Sanskrit Terms Defined in English by John A. Grimes * History of Islamic Philosophy edited by Seyyed Hossein Nasr, Oliver Leaman * History of Jewish Philosophy edited by Daniel H. Frank, Oliver Leaman * A History of Russian Philosophy: From the Tenth to the Twentieth Centuries by Valerii Aleksandrovich Kuvakin * Ayer, A.J. et al., Ed.(1994)A Dictionary of Philosophi" +33,"Companions to Philosophy. Oxford, Blackwell Publishers Ltd. * Hoffman, Eric, Ed.(1997)Guidebook for Publishing Philosophy. Charlottesville, Philosophy Documentation Center. * Popkin, R.H.(1999). The Columbia History of Western Philosophy. New York, Columbia University Press. ==外部連結== *倪梁康:〈哲學何為?哲人何為?〉 (2010) *倪梁康:〈哲學的定位:玄思的文學?隨意的史學?〉 (2009) *倪梁康:〈我所理解的「哲學」〉 (2010) *陳嘉映:〈哲學是什麼〉 (2000) *陳嘉映:〈哲學之為窮理與哲學之為對話〉 (2011) *陳嘉映:〈維特根斯坦的哲學觀〉 (2006) *高宣揚:〈當代西方哲學的基本論題〉 (2007) *張汝倫:〈哲學的出路在於智慧〉 (2011) *張汝倫:〈哲學的批判" +34,"文學(),在狭义上,是一种语言艺术,亦即使用语言文字为手段,形象化地反映客观社会生活、表达主观作者思想感情的一种艺术。文学不仅强调传达思想观念,更强调传达方式的独特性,且讲究辞章的美感。 文学,在广义上,是任何單一的書面作品,或用文字写作来传达思想、表达情感、发挥想像的所有著作,此时又称文学作品。文學寫作則是一種藝術形式,或被認為具有藝術或智力價值的任何單一作品,通常是由於以不同於普通用途的方式部署語言。中国先秦时期将以文字写成的作品都统称为文学,魏晋以后才逐渐将文学作品单独列出。在现代,則通常将文学分为韻文、散文、劇本、小说四大类别,是文化的重要表现形式,以不同的流派表现内心情感和再现一定时期和一定地域的生活。這個概念,又隨著時間的推移而改變了意義:現在它可以擴大到非書面的口頭藝術形式,可以與語言或文字本身配合,因此很難就其起源達成一致。印刷技術的發展使得書面作品的分佈和擴散成為可能,最終導致了網絡文學。 英语 literature,它的拉丁詞根是 literatura/litteratura(本身起源於littera:letter或handwriting),被用來指代所有的書面記錄" +35,"and Chinese Dictionary》。羅存德在literature詞條給了2個義項7個解釋「文,文學,文字,字墨;ancient literature,古文,今文」。 == 文學史 == 文學的歷史和文明發展有密切的關係。若將文學定義為用文字記錄的作品,最早的古代文學作品一般認為是古埃及文學Forster, Ancient Egyptian Literature: An Anthology, xix及Black et al. The Literature of Ancient Sumer, xix。古埃及文學中主要的文類(讚美詩、祈禱文及故事)幾乎都是以詩的方式寫成的Forster, Ancient Egyptian Literature: An Anthology, vii,不過雖然可以清楚看出有使用詩歌技巧(poetic devices),但詩歌的韻律不明Forster, Ancient Egyptian Literature: An Anthology, viii–ix。最早已知的文學作品是公元前2700年一篇由蘇美人创作的《吉爾伽美什史詩》,當中描述英雄主義、友誼、損" +36,"arold’s Pilgrimage: Canto I. Lord Byron: The Major Works. ed. McGann, J.J. New York: Oxford University Press。藉由文學人們可以繼續的發現有關歷史的新資訊,這個從各個學科領域都有和文學相關的子領域可以看出English: a degree for the curious. (2013, September 16). UWIRE Text, p. 1. 。當人們將資訊用文字的方式紀錄下來,就比較容易從這一代流傳到下一代,留下來的資訊會越來越多。從這些資料,人們可以研究文學、提昇想法、擴展知識、也可以開始像醫學或是貿易等專業領域的研究。而隨著現代人們學習內容的增加及拓展,文學也會有一些不同,成為以後人們研究的基礎。 許多古文明都有其對哲學或是相關觀點的文學,像是古中國、古印度、波斯及希臘羅馬古典时代的作品。許多古代的作品,就算是敘事的形式,都還是有道德或是教誨上的目的,像梵語的《五卷书》或是奥维德的《变形记》,後來戏剧及讽刺作品的受眾也變多,因此也開始有類似性質的文學創作。抒情诗常常是" +37,"后来诗、词、曲、小说等文学形式分别在唐、宋、元、明清达到高峰。民國時期由胡適和陳獨秀推動的新文學運動,認為作品不應只講求形式,應注重內容的充實、表達及情感,也推動白話文學。民国时期,武侠小说风靡海内外,成为当时最受欢迎的通俗小说。 中华人民共和国时期,在文化大革命後,出現相關的反思文學及伤痕文学,也有一批白话文诗人进行大量创作,也取代古诗成为当时最受欢迎的诗歌作品。後來網路文學興起,成為受歡迎的商業作品。 中華民國在撤退台灣後,在50及60年代出現了以四大抗戰小說為代表的戰鬥文藝小說,都是以抗戰時期為背景,後來又有反共文學的出現,而60年代開始,以瓊瑤為代表的言情小說也開始流行。70年代起逐漸開始有對於台灣社會研究的新現代文學,以及強調鄉土的鄉土寫實文學,1990年後也開始了網路文學的興起。 == 文学类别 == * 按载体分为:口头文学、书面文学、网络文学三大类。 * 按时间分为:古代文學,近代文学,现代文学和当代文学。他们的范围都是随着读者而不断迁移的。 * 按地域分为:外国文学,中国文学等等。 * 按读者分为: ** 按年龄有儿童文学,青少年及青壯年人的文學等。 ** 按读者群体" +38,"以及奏议文告等应用文体,对后代产生了深远影响。 #应用文是針對特定需求而有的文體,例如公告、廣告等。 #小品文是从作者的观点来讨论某一议题。 #文言文,常指非八股文的文體。 #骈文是句式多為四六句及對仗的文言文文體。 #随笔,兼有議論和抒情兩種特性的散文。 #笔记 #遊记 === 劇本 === 劇本是另一种古老的文学形式,主要通过不同角色之间的对话来表达作者的思想和感情。劇本可以用于舞台的表演,也可以阅读。像元曲、京剧、昆剧都屬於這個部份。西方的戲劇許多都伴隨著音樂和舞蹈,例如歌劇及音樂劇,古希腊戏剧是目前已知最早期的西方戲劇,有悲劇、喜劇、悲喜劇等。 === 小說 === #小说是一种叙事性的文学体裁,通过人物的塑造和情节、环境的描述来概括地表现社会生活。“小说”一词最早记载于《庄子·外物》:“饰小说以干县令,其于大达亦远矣。”中国现代意义上的小说,是从先秦散文中的寓言滥觞,经过汉末六朝的笔记,到唐传奇宋话本拟话本而成熟,再传至明清以四大奇書(后《金瓶梅》被《红楼梦》取代,成“四大名著”)为代表的长篇小说和以聊斋志异为代表的短篇小说。西方文學一般會依小說長度區分為长篇小说、中篇小說及" +39,"用第一人稱或是其他人稱、用傳統的線性敘事或是、或是文類選擇都包括在內。這可以讓讀者感受到一些熟悉的結構及架構,例如傳統犯罪小說,不過有些作者會特別選擇一些文學技巧來讓讀者有意外的感受。 文學技巧的使用也可能會產生新的文類,就像塞繆爾·理查森寫的早期現代小說《帕梅拉》一様。《帕梅拉》是用許多的信件組成,稱為「書信體技巧」(epistolary technique)。因此《帕梅拉》讓大家再次注意到,一個以往曾出現,但沒有這麼受注意的文類。 文學技巧和文学手段(literary device)不同,有點類似軍事戰略和軍事戰術之間的關係。文学手段是在敘述中用的特殊結構,像是隐喻、明喻、省略、敘事及託寓等,甚至單純的諧音都可以作為文学手段。也可以視為是文学手段,例如意識流敘事。 文學批評是指文學批評者對其他人作品的評論和評估,有時也會用來改進及提昇文學作品。也可以對作者帶來類似的作用。有許多不同種類的文學批評,背後會有其理論基礎,不同種類的文學批評可以評論文學作品的各個部份或是各個層面。 == 註解 == == 參考文獻 == == 延伸阅读 == == 外部連結 == * 張隆溪:〈文學理論的" +40,"歷史(现代汉语词汇,古典文言文称之为史),指人类社会过去的事件和行动,以及对这些事件行为有系统的记录、诠释和研究。歷史可提供今人理解過去,作為未來行事的參考依據,与伦理、哲学和艺术同属人类精神文明的重要成果。历史的第二个含义,即对过去事件的记录和研究,又称“历史学”,或简称“史学”。隶属于历史学或与其密切相关的学科有年代学、编纂学、家谱学、古文字学、计量历史学、考古学、社会学和新闻学等,参见历史学。记录和研究历史的人称为历史学家,简称“史学家”,中国古代称为史官。记录历史的书籍称为史书,如《史記》、《汉书》等,粗分為「官修」與「民載」兩類。 广义的历史,泛指一切事物的发展過程,包括自然史和社会史。不一定同人类社会发生联系。在哲学上,这种含义下的历史称为历史本体,例如宇宙历史、地球历史、世界历史、中国历史等等。通常仅指人类社会的发展過程,它是史学研究之對象;一般説來,关于历史的记述和闡釋,也称為历史。而狭义的历史则必须以文字记录为基础,即文字出现之后的历史才算历史,在此之前的历史被称为史前史。又可以称为人类史或社会史,而脱离人类社会的过去事件称为自然史。一般来说,历史学仅仅研究前者,即社" +41,"代史書之意袁了凡〈歷史綱鑑補引〉:「弟兄不合併之一家可為大方家乎。余是以不揣鄙陋,取諸史而採輯之,以綱標其大義,以目與鑑詳其嶺末。」。 1895年,禮部侍郎于式枚在奏章中,提到歷史這個名詞,將它作為歷史事件之意《清史稿》〈列傳〉〈于式枚〉:「臣遍考東西歷史,參校同異。」。1896年,皮錫瑞《經學歷史》出版,其中的歷史二字,也明確將它定位為歷史事件之意皮錫瑞《經學歷史》:「古來國運有盛衰,經學亦有盛衰;國統有分合,經學亦有分合。歷史具在,可明徵也。」。因此,在1890年代前後,歷史這個名詞在中國開始被明確當作歷史事件來使用。 明治維新後,日本學者為翻譯,譯為歷史二字,使其成為對應詞。1870年代成為流行用語。 1902年,光緒皇帝接受吏部尚書張百熙建議,頒佈〈欽定學堂章程〉,其中尋常小學課目中,有史學、輿地二項。張百熙派吳汝綸赴日本考察教育後,1903年,負責教育改革的張百熙、張之洞、榮慶向皇帝建議重訂學堂章程《清史稿》〈志‧選舉二‧學校二〉:「博考外國各項學堂課程門目,參酌變通,擇其宜者用之,其於中國不相宜者缺之,科目名稱不可解者改之,過涉繁重者減之。」。在重訂章程後,尋常小學依日本語" +42,"社会具体的和多样性的过去之学科,以解释人类社会具体的现在和未来远景作为宗旨。” * 錢穆說:「知道歷史,便可知道裏面有很多的問題。一切事不是痛痛快快一句話講得完。歷史終是客觀事實,歷史沒有不對的,不對的是在我們不注重歷史,不把歷史作參考。至少我們講人文科學方面的一切,是不該不懂歷史的。」「歷史所以記載人事,故著史必首重紀事,次及傳人。而瞭解歷史最基本的開始,亦在明事變,知人物。於不斷的事變與人物之背後有時代。能知事變、人物與時代,此乃歷史學之初步入門,亦為歷史學之主要骨幹。事有成敗得失,人有賢愚忠奸,時代則有治亂興衰,教歷史者,首貴能於此分析指點,可以使學者長見識,增修養,此乃歷史教育最大之功能。」 * 黃仁宇說:「盲目的恭維不是可靠的歷史,謾罵尤非歷史。」 * 呂思勉說:「歷史上的事實,所傳的,總不過一個外形,有時連外形都靠不住,全靠我們根據事理去推測他、考證他、解釋他。……從前論史的人,多說史事是前車之鑒。其意以為一件事辦好了,我們就當取以為法,摹仿他;一件事辦壞了,我們就當引以為戒,不可再蹈其覆轍。這話很易為人們所贊許,其實似是而非的。史事哪有真相同的?我們所謂相同,都不過察之不" +43,"学中,而是其间的桥梁,合并了两大领域的研究方法。一般来说,史学家通过研究各种书面文字但并不局限於此,努力并尝试解答和历史有关的问题。历史知识的原始资料分为三种:文字记载的、口头流传的、保留下来的历史遗迹,通常历史学家会综合三种方法进行研究,而文字记载经常被作为强调的重点,因为它普遍纪录了发展的时间。这种强调引申出了一个新领域,史前史,也可称为史前学,研究的是没有书面纪录的那一个时期。由于世界各地文字出现的时间各不相同,所以史前史和历史的主要区别是根据具体的论题而决定。学者们为了易于研究,根据过去人类的范围将其划分为不同的阶段。划分过去的方法繁多,包括按年代分类,按文化习俗分类,按不同主题分类。这三种分类经常会有重叠,比如“阿根廷的劳工运动的演变,1930-1945”。 尽管历史研究倾向于一些专门的地点、时间和主题,历史学家也同时会关心其他普通的一些内容。而对于其他人来说,历史已经成为一个非常普通的词语,就是研究过去人类的所有事情,甚至于现在更兴起了一门所谓的广义历史。过去研究历史都是为了应用或者理论的目的,而现在还多了一条:那就是对人类过往的好奇。 == 描述 == 历史学家在自己的时" +44,"伍德出版社。第416页。 在20世纪,法国历史学家 费尔南·布罗代尔通过使用经济学,人类学和地理学等外部学科,使历史学发生了革命性的变化。传统上,历史学家以书面形式或通过口述传统记录过去的事件,并试图通过研究书面文件和口头陈述来回答历史问题。从一开始,历史学家就已经使用了诸如纪念碑,题词和图片之类的资料。但写作是区分历史和过去的标志。 考古学是一门特别有助于处理埋藏的遗址和物体的学科,这些遗址和物体一旦出土,将有助于历史的研究。 但是考古学很少独立存在。它使用叙事来补充其发现。但是,考古学是由一系列独立于历史的方法论和途径构成的。也就是说,考古学并不是在文本资料中“填补空白”。事实上,“历史考古学”是考古学的一个特定分支,经常将其结论与当代文献资料相比较。例如,,美国历史的挖掘者和口译员,已经试图理解文本文件与材料记录之间的矛盾,尽管目前书面文件中固有“自由”意识形态,但通过研究整个历史环境,证明了奴隶的拥有和财富的不平等现象仍然存在。 历史的组织方式有多种,包括按时间顺序,文化,按地区和按主题排列。 这些划分不是相互排斥的,并且经常存在重要的交叉点。尽管现代趋势已趋向于专业化,但历史" +45,"是把实际发生的事件转换成以意念和文字形式存在的历史的过程和方法。关于历史学的目的和方法的研究探讨,在西方属于历史哲学的范畴,历史哲学的出现和发展,意味着历史学从单纯的历史纪录发展成为对历史的解释和对历史规律的探求阶段。一开始,历史哲学仅仅关心如何改进历史研究的方法,但认为被研究和记录的历史就是真实的历史。在新康德主义和新黑格尔主义的影响下,人们对自身的认识过程有了重新的理解,哲学家开始重新定义历史学。意大利哲学家克罗齐提出“一切真历史都是当代史”的命题,认为往事只有在当代人生活中发挥作用才成为历史,否则是“死的历史”,即编年史。因此,同样的历史在不同的时期会被不断的改写。英国哲学家柯林武德又进一步认为“一切历史都是思想史”,即历史是历史学家思想的反映,不仅因时代而异,也因人而异。而唯物主义的历史观认为历史事件是客观存在的,历史则是历史学家主观对客观的历史事件的认识。由于人主观的局限性,对客观的历史事件的认识是有限的,主观的认识不能完全符合客观的历史,因此只有不断改进逐渐逼近,这一过程同自然科学的过程一致。这种历史学称为“历史科学”。 == 历史编撰学 == 历史编撰学()具有许多相关的" +46,"的“第六次也是最后一次询问”是指对消息来源的最后一次确认。历史学家在研究历史事件时,需要对消息来源进行仔细的分析和评估,以确保其内容的可靠性和准确性。同时,历史学家还需要考虑消息来源的背景和意图,以便更好地理解历史事件的真实含义和影响。因此,对消息来源的评估和分析是历史学家工作中不可或缺的一部分。 史学方法是历史学家进行研究和书写历史的技术和指南。其中,第一手资料和其他证据是不可或缺的。 哈利卡那索斯的希罗多德(公元前484年-公元前425年) 通常被誉为“历史之父”。 然而,与他同代的修昔底德(约前460年–前400年)凭借他的作品《伯罗奔尼撒战争史》成为了首个用完备的历史方法研究历史的人。 修昔底德与希罗多德不同,认为历史是人类的选择和行动的产物,并着眼于因果关系,而不是认为历史是神圣干预的结果(尽管希罗多德并不完全致力于这个想法)。在他的历史方法中,修昔底德强调年代学、名义上中立的观点、以及人类世界是人类行动的结果。 希腊历史学家也认为历史是周期性的。 希波的奥古斯丁在中世纪初期对基督教和西方思想产生了影响。在中世纪和文艺复兴时期,人们通常通过神圣或宗教观点来研究历史。1800年" +47,"年,赫伯特·斯宾塞总结了这些方法: 斯宾塞所说的“富矿”是指科学的历史理论。 同时,亨利·巴克尔表达了历史成为科学那一天的梦想: 与巴克尔的梦想相反,对方法影响最大的19世纪历史学家在德国成为利奥波德·冯·兰克。 他将历史限制为“真正发生的事情”,从而使该领域远离科学。对于兰克,应仔细收集历史数据,进行客观检查并结合严格的要求。但是,这些程序“仅仅是科学的前提和前提。科学的核心是寻找被检查数据的顺序和规律性,并制定关于它们的概括或定律。” 《历史的缪斯》,第147页。 在20世纪,学术历史学家较少关注史诗般的民族主义叙事,而这些叙事往往倾向于美化国家或伟人理论,对社会和知识力量进行更为客观和复杂的分析。 20世纪历史方法论的一个主要趋势是趋向于将历史更多地视为一种社会科学而不是一种艺术,传统上就是这样。 作为社会科学的历史学的主要倡导者是各种各样的学者,其中包括费尔南·布劳岱尔、埃马纽埃尔·勒鲁瓦·拉迪里、漢斯-烏爾里希·韋勒、罗伯特·福格尔、呂西安·費夫爾、劳伦斯·斯通。 作为社会科学的许多历史倡导者因其跨学科的方法而受到关注。布劳德尔将历史与地理相结合,布莱克尔将历史与政治学相结合" +48,"社会经济群体。在第二次世界大战后时代出现的另一种社会历史流派是“日常生活史” (Alltagsgeschichte)。Martin Broszat,Ian Kershaw和Detlev Peukert等学者试图研究20世纪德国,特别是纳粹德国时期。 马克思主义历史学家,例如埃里克·霍布斯鲍姆、爱德华·帕尔默·汤普森、、Herbert Aptheker、和克里斯托弗·希尔试图验证马克思的理论是从马克思主义的角度分析历史。为了回应马克思主义对历史的解释,诸如FrançoisFuret,Richard Pipes,Roland Mousnier,和罗伯特·康奎斯特提供了反马克思主义的历史解释。女权主义者历史学家,例如Joan Wallach Scott,Natalie Zemon Davis,Sheila Rowbotham,Elizabeth Fox-Genovese和Lynn Hunt都认为,研究过去女性的经验很重要。近年来,后现代主义者质疑历史的有效性和历史研究的必要性,因为所有历史都基于对来源的个人解释。Richard J. Evans在1997年的著作《捍卫历史》中捍卫了历史的价值" +49,"实际情况的具体研究。 特别请参阅, Marx and Engels, The German Ideology 马克思和恩格斯的《德国意识形态》马克思主义史学曾经是苏联的正统思想,但自从1991年苏联的共产主义垮台以来,米哈伊尔·科罗姆说,其已经衰退到了学术领域。米哈伊尔·克罗姆(Mikhail M. Krom),《从中心到边缘:当代俄罗斯史学中的马克思主义命运》,《斯托里亚格拉托里亚传》(2012年),第62期,第121–130页 ==历史产生中的潜在缺陷== 许多历史学家认为,历史的产生是有偏见的,因为历史上的事件和已知事实可以用多种方式来解释。认为,历史通过沉默本身的实践与政治联系在一起。 “关于历史与政治之间联系的第二个共同观点是,历史学家经常受政治影响。”的说法,历史过程植根于档案中,因此沉默或被遗忘的历史部分可能是叙事策略中有意组成的部分,它决定了如何记住历史领域。 历史遗漏可以多种方式发生,并且可以对历史记录产生深远的影响。信息也可以有意排除或意外遗漏。历史学家创造了多个术语来描述省略历史信息的行为,包括:“沉默”,“选择性记忆”,和“擦除”。 二十世纪的历史学家将她的大部" +50,"务印书馆,1992年。 原始社会中人类没有文字,只能通过诸如结绳记事和口传等方法作记录,一些历史的痕迹通过“传说”保存了下来,例如中国上古传说“黄帝战蚩尤”、“女娲补天”、“大禹治水”等。国家出现后,则开始有掌管祭祀的“巫”,他们同时兼任记录时事、起草公文和掌管文书等相关职能,可以说是最早的史官。之后随着国家职能的不断演进与发展,出现了职能独立的史官,专门记录历史事件,掌管典籍。在这个时期,中国出现了世界上最早的史书《尚书》,内容是历代政治文件汇编,并无特定的历史记录体裁。从西周共和元年(公元前841年)起,中国有了按年记载的编年史,从此有了连续不断的历史纪录,而且差不多每年都有史可查。这在世界各国范围内也是极其罕见的。春秋战国时期的史学家如孔子(编订《春秋》)和左丘明(著《左传》)等重视人类社会活动,从而使历史基本摆脱了神学和宗教的影响。张知寒主编,《历史学概论·第十二章 中国史学史概述》,1984年 西汉时司马迁撰写了《史记》,创建了纪传体的历史记录体裁,《史记》的规模在当时世界范围内是空前的。之后东汉班固著《汉书》,延续发展了《史记》的体例,是中国第一部纪传体断代史。这两部历史著" +51,"是否存在着一个设计,目的,指导原则或是定局。 == 参见 == * 历史学家:研究和记录历史的人。 ** 历史学家列表 * 正史:官方或历史学家编纂的规范的历史,在中国通常指二十四史,延伸含义为可信的历史。 * 野史:正史上沒有紀錄,主要來自民間的街談巷說、遺聞佚事的“历史”,部份带有传说性质 === 学科 === * 歷史學:对历史的研究和记录 * 历史哲学:对历史学方法、目的、规律的研究和探讨 * 年代学:确定历史事件发生时间的学科 * 文献学:对资料文献的管理和检索等的研究 * 古文字学:研究古代的文字、手稿的学科 * 新闻学:报告事件发生,社会现象,政治时局等的学科 * 社会学:研究现代(当代)人类社会與社會生活的学科 * 考古學:发掘和调查古代遗物和文献 * 心理史学:研究歷史事件背後的心理激励作用 * 家谱学:研究家庭及其历史渊源 * 未来学:对未来的预测和研究 === 体裁 === * 编年体:以时间为顺序编撰和记述历史 ** 历史年表:依时间先后排列的历史资料 * 纪传体:以人物传记的方式记叙历史 ** 傳記(本纪):个人历史的记录 * 纪事本末体:以事件为主的历史记录" +52,"计算机科学(,有时缩写为)是系统性研究信息与计算的理论基础以及它们在计算机系统中如何实现与应用的实用技术的学科。 它通常被形容为对那些创造、描述以及转换信息的算法处理的系统研究。计算机科学包含很多分支领域;有些强调特定结果的计算,比如计算机图形学;而有些是探討计算问题的性质,比如计算复杂性理论;还有一些领域專注于怎样实现计算,比如程式語言理論是研究描述计算的方法,而程式设计是应用特定的程式語言解决特定的计算问题,人机交互则是專注于怎样使计算机和计算变得有用、好用,以及随时随地为人所用。 有时公众会误以为计算机科学就是解决计算机问题的事业(比如信息技术),或者只是与使用计算机的经验有关,如玩游戏、上网或者文字处理。其实计算机科学所关注的,不仅仅是去理解实现类似游戏、浏览器这些软件的程序的性质,更要通过现有的知识创造新的程序或者改进已有的程序。 Common myths and preconceptions about Cambridge Computer Science Computer Science Department , University of Cambridge 尽管计算机" +53,"和计算机系统通常被认为是非计算机科学学科的领域。例如,研究计算机硬件被看作是计算机工程的一部分,而对于商业计算机系统的研究和部署被称为信息技术或者信息系统。然而,现如今也越来越多地融合了各类计算机相关学科的思想。计算机科学研究也经常与其它学科交叉,比如数学、工程学、认知科学和经济学等。 计算机科学被认为比其它科学学科与数学的联系更加密切,一些观察者说计算就是一门数学科学。 早期计算机科学受数学研究成果的影响很大,如库尔特·哥德尔、艾伦·图灵、,阿隆佐·邱奇等数学家的研究,这两个领域在某些学科,例如数理逻辑、范畴论、域理论和代数,也不断有有益的思想交流。 == 历史 == 早期计算机科学建立的基础得追溯到最近电子计算机的发明。那些计算固定数值任务的机器,比如算盘,自古希腊时期即已存在,而用于加快计算的算法更是在算盘等古老的计算仪器发明之前就已被广泛使用。 Wilhelm Schickard在1623年设计了世界上第一台机械计算器,但没有完成它的建造。布莱兹·帕斯卡在1642年设计并且建造了世界上第一台可以工作的机械计算器Pascaline。1673年,莱布尼兹开始了的建造工作并于1694" +54,"科学革命的参与者之一。在那段探索时期,IBM(International Business Machines的缩写)发布的IBM 704以及之后的IBM 709计算机被广泛使用。“不过,使用IBM电脑工作仍然是一件很沮丧的事情。如果你弄错了一条指令中的一个字母,程序将会崩溃,而你也得从头再来。”20世纪50年代后期,计算机科学学科還在发展階段,这種問題在当时是一件很常見的事情。 随着时间的推移,计算机科学技术在可用性和有效性上都有显著提升。现代社会见证了计算机从仅仅由专业人士使用到被广大用户接受的重大转变。最初,计算机非常昂贵,要有效利用它们,某种程度上必须得由专业的计算机操作员来完成。然而,随着计算机变得普及和低廉,已经几乎不需要专人的协助,虽然某些时候援助依旧存在。 === 主要成就 === 虽然计算机科学被认定为一门正规学科的历史很短暂,但它对科学和社会做出了许多根本性的贡献ー事实上,计算机科学与电子学一样,是信息时代的奠基科学,也是資訊革命的驱动力,被视为继工业革命(公元1750-1850年)和新石器革命(公元前8000-5000年)之后人类技术进步的第三次重大飞跃。 这些贡献" +55,"r games)。现代计算机能够对这些设计进行优化,如飞机设计。尤其在电气与电子电路设计中,SPICE软件对新的物理实现(或修改)设计具有很大帮助。Muhammad H. Rashid, 2016. SPICE for Power Electronics and Electric Power. CRC Press. p. 6. . 它包含了针对集成电路的基本设计软件。 * 人工智能变得越来越重要,因为它变得更加高效和复杂。人工智能的应用有很多,其中一些可以在家里看到,比如機器人吸塵器。它也出现在视频游戏和现代战场上的无人机、反导弹系统和中。Marko B. Popovic, 2019. Biomechatronics. Elsevier Science. p. 501. . * 人机交互将新颖的算法与设计策略相结合,可实现快速的人类绩效,低错误率,易于学习和高度满意度。研究人员利用人种学观察和自动化数据收集来了解用户需求,然后进行可用性测试来完善设计。关键的创新包括、可选择的网页链接、触摸屏设计、移动应用程序和虚拟现实。 == 哲学 == 提出计算机科学可以分成三个领域:数学、工程学、" +56,"==== 数据结构和算法 ==== O(n^2) 算法分析 算法 数据结构 计算几何 图论 算法指定义良好的计算过程,它取一个或一组值作为输入,经过一系列定义好的计算过程,得到一个或一组输出。《算法导论》1.1[美]Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein著, 潘金贵、顾铁成、李成法、叶懋译,机械工业出版社 2011年7月第1版算法是计算机科学研究的一个重要领域,也是许多其他计算机科学技术的基础。算法主要包括数据结构、计算几何、图论等。除此之外,算法还包括许多杂项,如模式匹配、部分数论等。 ==== 计算理论 ==== 按照Peter J. Denning的说法,计算机科学的最根本问题是“什么能够被有效地自动化?”计算理论的研究就是專注于回答这个根本问题,关于什么能够被计算,去实施这些计算又需要用到多少资源。为了试图回答第一个问题,递归论检验在多种理论计算模型中哪个计算问题是可解的。而计算复杂性理论则被用于回答第二个问题,研究解决一个不同目的的计算问题的时间与空间消耗。 著名的“P=" +57,"发以及驗證。在软件和硬件设计方面,形式化方法的使用动机,如同其它工程学科,是通过适当的数学分析便有助于设计的可靠性和健壮性的期望。但是,使用形式化方法会带来很高的成本,意味着它们通常只用于高可靠性系统,这种系统中安全或保安(security)是最重要的。对于形式化方法的最佳形容是各种理论计算机科学基础种类的应用,特别是计算机逻辑演算,形式语言,自动机理论和形式语义学,此外还有类型系统、代数数据类型,以及软件和硬件规范和验证中的一些问题。 === 计算机系统 === ==== 计算机体系结构与计算机工程 ==== 计算机系统结构,或者数字计算机组织,是一个计算机系统的概念设计和根本运作结构。它主要侧重于CPU的内部执行和内存访问地址。这个领域经常涉及计算机工程和电子工程学科,选择和互连硬件组件以创造满足功能、性能和成本目标的计算机。 数字电路 微架构 多处理机 ==== 操作系统 ==== 操作系统是管理电脑硬件与软件资源的电脑程式,同时也是电脑系统的核心与基石。作业系统需要处理如管理与配置记忆体、决定系统资源供需的优先次序、控制输入与输出装置、操作网路与管理档案系统等基本事务。作业系统" +58,"对于数字视觉内容的研究,涉及图像数据的合成和操作。它跟计算机科学的许多其它领域密切相关,包括计算机视觉、图像处理、计算几何与可视化,同时也被大量运用在特效和电子游戏。 ==== 科学计算 ==== 科学计算(或者计算科学)是关注构建数学模型和量化分析技术的研究领域,同时通过计算机分析和解决科学问题。在实际使用中,它通常是计算机模拟和计算等形式在各个科学学科问题中的应用。 数值分析 计算物理学 计算化学 生物信息学 ==== 多媒体技术 ==== 多媒体技术是指通过计算机对文字、数据、图形、图像、动画、声音等多种媒体信息进行综合处理和管理,使用户可以通过多种感官与计算机进行实时信息交互的技术,又称为计算机多媒体技术。 ==== 人工智能 ==== 这个计算机科学分支旨在创造可以解决计算问题,以及像动物和人类一样思考与交流的人造系统。无论是在理论还是应用上,都要求研究者在多个学科领域具备细致的、综合的专长,比如应用数学,逻辑,符号学,电机工程学,精神哲学,神经生理学和社会智力,用于推动智能研究领域,或者被应用到其它需要计算理解与建模的学科领域,如金融或是物理科学。人工智能领域开始变得正式源" +59,"cial Intelligence) ACL(Annual Meeting of the Association for Computational Linguistics) NAACL(The North American Chapter of the Association for Computational Linguistics) 其为ACL的北美分会 EMNLP(Conference on Empirical Methods in Natural Language Processing) IJCAI( International Joint Conference on Artificial Intelligence) === 期刊 === == 作为高等教育的二级专业 == 主要进行计算和算法推理的研究。其中包括计算理论、算法分析、形式化方法、并行理论、数据库、计算机图形学以及系统分析等。通常也教授程序设计,但仅仅将它看作是支持计算机科学其它领域的媒介,而不是高级研究的重心。 的计算机科学课程则主要侧重于训练高级编程,而不是算法和计算理论。这些课程着重教授那些对于软件工业很重" +60,"体系结构:关于计算机或计算机信息系统的“硬件”。 == 工业界 == == 参见 == * 未解決的计算机科學問題 * 计算机科学家的学术家谱 * 计算机科学家 * 计算 * 计算机科学的历史 * 中国计算机科学大事年表 * 信息学 * 学术计算机科学系列表 * 计算机科学会议列表 * 计算机科学家列表 * 计算机科学开放问题列表 * 计算机科学出版物列表 * 计算机科学先驱列表 * 软件工程主题列表 * 计算机科学的哲学 * 计算机领域中的女性 * 计算机 * 计算尺 * 算盤 * 語言 * 科学 * 科学家 * 算籌 * 科學會議 * 计算机遊戲 * 按揭计算机 * 计算机程式 * CASIO 计算机 * 计算机工程 * 桌上型计算机 == 参考文献 == == 延伸阅读 == ; 概述 * ** Within more than 70 chapters, every one new or significantly revised, one can find any kind of information and references about computer science" +61,"ic, or by instructors wishing to find timely information on a subject they are teaching outside their major areas of expertise. (Rocky Ross, SIGACT News) * ** Since 1976, this has been the definitive reference work on computer, computing, and computer science. [...] Alphabetically arranged and classified into broad subject areas, the entries cover hardware, computer systems, information and data, software, the mathematics of computing, theory of computation, methodologies, applications, and comp" +62,". Bernard, Zentralblatt MATH) ; 文章 * Peter J. Denning. Is computer science science?, Communications of the ACM, April 2005. * Peter J. Denning, Great principles in computing curricula, Technical Symposium on Computer Science Education, 2004. * Research evaluation for computer science, Informatics Europe report. Shorter journal version: Bertrand Meyer, Christine Choppy, Jan van Leeuwen and Jorgen Staunstrup, Research evaluation for computer science, in Communications of the ACM, vol. 52, no. 4, p" +63,"puter Science * Best Papers Awards in Computer Science since 1996 * Photographs of computer scientists by Bertrand Meyer ; 参考文献和学术搜索引擎 * CiteSeerx: search engine, digital library and repository for scientific and academic papers with a focus on computer and information science. * DBLP Computer Science Bibliography: computer science bibliography website hosted at Universität Trier, in Germany. * The Collection of Computer Science Bibliographies ; Webcast * Directory of free university lectures in" +64,"民族是一个具有多重含义的词汇。其源自19世紀的歐洲與美國,經日本傳入中國,在意義上為國族()、 種族()與族群()的混合。 == 詞源 == 在漢語中,民族一詞具有多重指涉,可以表達種族、國族、族群等近似概念,用法受到政治意識型態的影響,容易相互混淆。不同學科對於民族的定義也有許多歧異。民族一詞在以中文翻譯外語時也十分容易混淆。例如民族主義()精確翻譯應為「國族主義」,民族學()精確翻譯應為「族群學」等。 中华民族這一概念本身较为偏向国族概念。辛亥革命之前,革命党人试图以當時漢族地區为界建立一个汉民族国家來反抗清廷的民族壓迫,此时的中华和汉族的意义等同,为族群概念。而革命党人提出五族共和后,中华民族轉變為表示全中国内的所有族群,成为国族概念。 “民族”一词可以追溯至南朝梁时期萧子显《南齊書·高逸傳》:「今諸華士女,民族弗革」,以及唐朝李筌《太白阴经》:“愚人得之,以倾宗社,灭民族”,但其含义为民众。 近代日本譯者翻譯歐洲著作(主要是德人著作),以民族()一詞對應 「國族」() 或「族群」(),之後傳入中國。這使漢語的「民族」一詞具有多重指涉,「民族」的概念又開始分化,與「種族」、「國族" +65,"級的壓迫出現之後,《共產黨宣言》中所謂「統一的民族階級利益」就讓位給了階級利益;在階級對立中,只要能消滅最基本壓迫型式的階級壓迫,其他型式的壓迫(包括民族壓迫)就會隨之解決。等到那個時候,民族、國族將會像國家一樣,失去存在的必要而自然消亡。 1912年,時任真理報編輯的史達林,為了解決第二國際及俄共內部對民族自治、國族自決的爭議,被列寧派到維也納去寫國族問題的小冊子。其成果就是被馬克思主義國族理論引為「民族」定義聖經的《馬克思主義和民族問題》。從現代的國族研究看起來,這個定義相當粗糙。但史達林從歷史唯物論的角度,以明確的客觀特徵首次為「國族」提出了定義: 日後,中華人民共和國建國之後,即引史達林定義的這四項國族特徵,為中國境內的少數民族進行民族識別和民族認定。 2005年,中華人民共和國對民族的定義: 中華人民共和國將血統視為認定公民的民族成份的大前提。 ==參考資料== ===參考文獻=== (按照作者姓氏漢語拼音順序排列) *朱浤源,1988,從民族到國家:論Nation意義的蛻變 。中山社會科學譯叢 3:118-32。 *中共中央馬克思恩格斯列寧斯大林著作編譯局 編,《馬克思恩格" +66,"戏剧()是演員將某個故事或情境,以對話、歌唱或動作等方式所表演出來的藝術。戏剧有四個元素,包括了「演員」、「故事(情境)」、「舞台(表演場地)」和「觀眾」。「演員」是四者當中最重要的元素,他是角色的代言人,必須具備扮演的能力,戲劇與其他藝術類最大的不同之處便在於扮演了,透過演員的扮演,劇本中的角色才得以伸張,如果拋棄了演員的扮演,那麼所演出的便不再是戲劇。 即使在動畫、木偶戲、皮影戲等沒有演員現身的戲劇中,演員不過是隱藏在觀眾的視覺之外的地方演出。 戏剧的表演形式多种多样,常见的包括话剧、歌剧、舞剧、音乐剧、木偶戏等。由于文化背景的差别,不同文化所产生戏剧形式往往拥有独特的传统和程式,比如西方歌剧、中国戏曲、印度梵剧、日本能劇、歌舞伎等。 戏剧的起源實不可考,目前有多种假说。比较主流的看法有二:一為原始宗教的巫术仪式。比如在上古汉语中,“巫”、“舞”、“武”三字可能同源(上古汉语拟音分别为:/*ma/,/*maʔ/,/*maʔ/)。其有可能是对一种乞求战斗胜利的巫术活动的合称,即戏剧的原始形态。另一為劳动或庆祝丰收时的即兴歌舞表演,这种说法主要依据是古希腊戏剧被认为起源于酒神祭祀。 现" +67,"), Goldhill (1997, 54), and Ley (2007, 206). Taxidou, p. 104 notes that most scholars now call 'Greek' tragedy 'Athenian' tragedy, which is historically correct (2004, 104). Brown writes that ancient Greek drama was essentially the creation of classical Athens: all the dramatists who were later regarded as classics were active at Athens in the 5th and 4th centuries BC (the time of the Athenian democracy), and all the surviving plays date from this period (1998, 441). The dominant culture of Athe" +68,"紀),“中間的喜劇“(公元前4世紀)和”新喜劇“(公元前2世紀至4世紀)。 雅典的戲劇文化可分為三種風格:悲劇、喜劇、和撒特劇。 (1) 悲劇:探討自由意志與神諭命定對立等宗教、哲學議題。參考:Sophocles《伊底帕斯王》 王尚德. 第十章第二節〈撒特劇〉//《希臘文明》. 2010. 237-238頁.。 (2) 喜劇:古典時期的舊喜劇多議論當代社會、時政及文學、哲學思潮。參考:Aristophanes《利西翠妲》。希臘化時期的新喜劇則更為寫實,以浪漫的家庭糾葛為題材 。參考:Menander 《壞脾氣的老人》 (3) 撒特劇:以之前上演的悲劇或神話故事為題材,旨在透過撒特歌隊嘲諷擔任主角的貴族或神祇。 ===羅馬時期=== 希臘戲劇繼續在整個羅馬時期進行的,在公元前240標誌著定期羅馬戲劇的開端。古羅馬時期的娛樂活動相當豐富多元,除了官方支持的正規戲劇以及形制短小的亞提拉鬧劇、優劇、默劇之外,還包含了雜耍、賽車、賽馬、水戰、獸鬥…等訴諸感官刺激的娛樂競技。這些誇奇、腥膻的活動一方面反映出古羅馬觀眾的口味,一方面也影響了正規戲劇的風格。 古羅馬藉由取消歌舞隊、將音樂打散至全劇,並" +69,"先得到士族大夫的追捧和喜爱,他们大量创造剧本,不断修改曲谱,同时修正昆曲的戏剧理论,并使得传奇剧本成为一种新的主流文学形式。随后昆曲又得到晚明和清代宫廷皇室的喜爱,成为贵族生活的一部分,成为获得官方肯定的戏剧艺术,故称“雅”;而以各地方言为基础的地方戏,广受民间喜爱,则称“花”。于是在清代形成了“花雅之争”,实际上是戏曲共同繁荣的局面。这丰富了戏曲艺术的门类,也形成了各自的艺术特色。 近年來台灣的戲劇研究學者曾永義提出一套說法,認為在討論中國戲劇起源時,應該要區分“大戲”與“小戲”,大戲是成熟的戲曲,而小戲則是戲劇的雛型。大戲是在到了金元雜劇之後才發展完成,而之前的宋雜劇、唐代的代面、踏搖娘、缽頭、參軍戲、樊噲排君難等,都可列入小戲的行列,而中國在非常早之前,就有小戲。 中国在与近代西方有文化接触前,没有西方意义上的“戏剧”(主要指话剧)传统。中国传统的戏剧为一种有剧情的,“以歌舞演故事”的,综合音乐、歌唱、舞蹈、武术和杂技等的综合艺术形式,也就是戏曲曲艺。 == 类型 == 根据不同的分类标准,戏剧可以被分成不同的類型: * 按容量大小,戏剧文学可分为多幕剧、独幕剧和小品。 * 按表" +70,"能引發觀眾的感動與省思…進而成為一種意識或感受或達到相當程度的娛樂效果! ==文学== 戏剧文本(即“剧本”)是一出戏剧的基本要素,是一台戏的先决条件。 剧本最重要的是能够被舞台上搬演。戏剧文本不算是艺术的完成,直到舞台演出之后(即“演出文本”)才是最终艺术的呈现。历代文人中,也有人创作过不适合舞台演出,甚至根本不能演出的剧本。这类的戏剧文本则称为案头戏(也叫书斋剧)现代戏剧中也出现了没有剧本的演出实例。 戏剧的文学本,在不演出的状态下,可以作为单独的文学样式欣赏。 ==表演艺术== 隨著西方現代戲劇在導演以及演員训练體系上的实践,戲劇理论逐渐从戏剧文本的討論擴大到劇場整體,進而產生“場面調度”等新觀念。其中,比较著名的提法是20世纪70年代英国戏剧导演彼德·布鲁克在其专著《空的空间》(The Empty Space)中提出的观念:「一个演员,走过一个空荡荡的舞台,这就是一出戏的全部。」 ===必要条件=== 无论戏剧一词如何定义,只有满足以下的条件,才可视为一次戏剧活动。 # 戏剧交流的四个参与者:觀眾、故事,演員、舞台 # 观众与演员必须同时在场。(疫情時代出現線上觀演形式) #戏" +71,"。在中國歷史上,還有許多稱呼戲劇表演者的稱呼,例如戲子、優伶、娼優、伶人、散樂等,而因為在中國過去戲劇的表演者身份並不高,這些稱呼許多也有著刻意貶損的意味,而以「娼」稱呼戲劇表演者,固然是因為表演者是歌者,但是在歷史上有長的時期戲劇表演與娼妓之間的確關係密切。 而戲劇表演者所組成的組織,多稱為戲團,過去多稱為是戲班,而如果是某一戶富有人家自己經營,為了自己的娛樂所訓練出的戲班,叫做家班,家班在明代之後普遍。 ==研究書目== * 孫惠柱:《戲劇的結構:敘事性結構和劇場性結構》(台北:書林出版有限公司,1993)。 * 孫惠柱:《第四堵牆:戲劇的結構與解構》(上海:上海書店出版社,2011)。 * 余秋雨:《世界戏剧学》(武汉:长江文艺出版社,2013)。 * 谭霈生:《论戏剧性》(北京:北京大学出版社,2009)。 * 董每戡:《西洋戲劇簡史》(台北市)。 *《古希臘文學史》(上海譯文出版社,2007)。 *陆润棠:〈中西戏剧的起源比较 〉。 ==参看== * 藝術 * 劇場藝術 * 戲曲 * 曲藝 * 雜劇 * 南戲 * 話劇 * 布袋戲 * 崑劇 * 粵劇 * 京劇 * 古希臘戲劇" +72,"電影(/ ),特点是运动/移动的画面(),是一种视觉艺术作品,用来模拟透過使用动态图像来传达思想、故事、感知、感觉、美或氛围的体验。这些图像通常伴有声音,更少有其他感官刺激。 電影中看起來連續的畫面,是由一幀幀單獨的照片完成的,至於關於電影中運動的感覺,是因為人們因為視覺上的飛現象(Phi phenomenon),使得對一連串靜態圖片卻會造成移動的錯覺。傳統對電影中運動感知的理解是因為視覺暫留,使得圖像離開後,仍能在眼睛保留「視像」約十分之一秒。因此大腦感覺到圖像是「運動」的。但在1916年出版的德國心理學家雨果·明斯特伯格(Hugo Münsterberg)的《電影:一次心理學研究》中第三章《深度感和運動感》中,雨果·明斯特伯格證明了外觀運動絕不是影像滯留(即視覺暫留)的結果,而是(但不僅僅是)對運動的連續階段的感知。 電影製作本身是藝術也是產業。電影可以由電影攝影機拍攝真實影像再製作而成,也可以利用傳統的動畫技巧繪製圖畫再拍攝圖畫而成,甚至可以利用電腦成像及计算机动画製作電影,也可以在電影中利用上述所有的技術及其他視覺效果。電影技術發展初期有各種不同的放映速度,但現時電影都多以每秒" +73,"ess Book of Records, all editions.。但在這之前埃德沃德·邁布里奇在1878年拍攝的Sallie Gardner at a Gallop有時也被視為早期電影的先驅。而后,在1895年12月28日,由卢米埃尔兄弟在法国巴黎卡普幸路14号一家名为“大咖啡馆”的地下室首次公开放映了他们的短片,这使电影进入公众的视野。 ==歷史== === 初期 === 1895年,「火車進站」是盧米埃兄弟製作的黑白無聲的紀錄片。這部紀錄火車駛抵希歐達(la Ciotat)車站的影片,是架在月台上所拍攝的。全長五十秒的內容,描寫了蒸汽火車牽引著客車,從遠處漸漸地駛進車站。同年,盧米埃兄弟亦映演「離開工廠」,一套真正有演員演出的電影出現,拍攝當時工人下班的景象。 === 早期 === * 早期電影拍攝描述的是日常生活,包括由路易斯·普林斯拍摄的《郎德海花园场景》以及卢米埃尔兄弟拍摄的Repas de bébé(1895)、Exiting the Factory(1895)、Démolition d'un mur(1895)、L'Arrivée d'un train en gare" +74,"界上第一部公认的彩色电影片是《浮华世界》。在彩色電影取代黑白電影更是個逐漸轉變的過程中,隨著彩色技術的改良,越來越多的彩色電影,至今可以說已經基本上沒有黑白電影了。除了個別的例外,例如史匹堡的電影《辛德勒的名單》、前蘇聯電影《這裡的黎明靜悄悄》、2011年的法國電影《大藝術家》以及2013年的波蘭電影《依達的抉擇》等。這通常都是出於藝術或戲情的考量,特意拍成黑白電影。 === 电脑電子計算機特效 === 在電影製作初期,電影的特別效果鏡頭加以各項技術來配合。一般意义上的特效镜头,使用合成遮片,多重曝光,微缩模型,逐格动画等多种技术实现。到如今,计算机生成图像已大量配合應用在電影製作上。计算机生成的图像具有完全的可操作性,数字化后可合成或者完全替换电影中景物与角色。电影特效业进入计算机时代以后,几乎百分之百的特效依靠计算机生成,甚至可以整部電影完全只使用計算機生成,只有少数电影,因特殊的美学追求,还保持传统物理特效的运用。 ==理論== 電影理論的目的是以電影為一個藝術來研究,發展簡明且系統化的概念。以電影為藝術形式的概念起源自的的《The Birth of the Sixth Art》" +75,"當的剪輯,電影可以讓觀眾有似乎身在現場,觀看主角之間對話的能力。经典好莱坞电影就包括了這種敘事理論,因為在電影經典時期,位于好莱坞的各片廠早已反覆的使用此一手法。另一個電影語言是將鏡頭拍攝一個演員,畫面無聲,然後鏡頭切換到另一個較年輕,長相類似的另一個演員,這代表了演員在回憶以前的情形,電影中敘述一些在較早時間發生的事情。 ===評論=== 電影評論(影評)是有關電影的分析以及評估。一般而言電影評論會分為二類:由電影研究者提出的學術電影評論,以及在報紙、雜誌或其他媒體上常見到的雜誌電影評論。 撰寫報紙、雜誌及廣播媒體上影評的電影評論者多半會看許多新上映的電影。一般每部片只會看一次,而且一天只看一部到二部片,才有足夠時間來撰寫電影評論。影評對電影本身有很大的影響,尤其對特定片種的電影更是如此。一般大眾口味的動作片、恐怖片、喜劇片的票房不太會受到電影評論的影響。電影評論的主要內容是劇情概要及對電影的描述,這也是大部份人決定是否要看特定電影的原因。對於一些經典電影(例如劇情片),電影評論的影響相當大,糟糕的電影評論可能會毀掉一部電影,而且造成電影公司的經濟損失。 電影評論是否影響票房表現一事" +76,"電影會需要三年的時間,頭一年腳本發展,第二年是前期製作及電影製作,第三年是後期製作及電影發行。 ==發行== 電影發行是指讓閱聽人可以看到電影的過程。一般會由專門的進行,他們會決定電影的行銷策略、影片将要展出或可供观赏的式,以及決定時間等。電影除了在電影院供人觀看外,放在隨選視訊上,或是透過廣播聯賣方式作成电视节目。 其他發行的方式包括個人購買或是出租電影的VHS、DVD或藍光光碟,是在互联网上下載。 == 參見 == * 電影史 * 電影節 * 電影獎 * 電影公司 * 電影演員 * 電影導演 * 電影編劇 * 電影監製 * 电影类型 * 好萊塢 * 中國電影 * 香港電影 * 台灣電影 * 華語電影 * 台語電影 * 电影类型 * 電影技術 ** 長鏡頭 ** 蒙太奇 ** 電影特技 **3D電影 * 電影分級制度 * NG片段 * IMDB ==注释== == 參考資料 == == 外部連結 == * Internet Movie Database * IMPAwards * Filmfestivals.com * 中國影視資料館 * adjaranet * adjaranet" +77,"音樂,廣義而言,就是指任何以聲音組成的藝術。英文Music一詞源於古希臘語的μουσική(mousike),意即缪斯(muse)女神的藝術。而中文的音樂二字,東漢許慎《說文解字》解釋為「音,聲也。生於心,有節於外,謂之音。」認為音樂和聲音的區別,在於音樂需要透過人心去想像和創造。 音樂可分為創作、演奏、聆聽三個過程,在不同文化和社會,對於音樂的過程及其重要性都有不同的理解。 音樂的主要元素有音高、節奏和音色。音樂可以分為不同種類,但每種種類的區別常常是含糊和具爭議的,而不同種類的音樂會不同程度地強調或忽略某些音樂元素。至於何謂聲音,或者樂音和噪音的區別,則沒有公認的標準,也因著文化與社會背景而有所不同。許多在當時代具突破性或前衛的音樂被時人批評為「非音樂」,例如貝多芬的大賦格以及早期的爵士樂等等。但近代有不少音樂家不認同傳統的理解,例如二十世紀美國作曲家約翰·凱吉認為任何聲音和靜默皆是音樂。 音樂可以用樂譜描述,依據樂譜演奏,但也有不少音樂類型如民歌或爵士樂是由演奏者即興創作的。樂譜作為一種符號的語言,只能描述聲音的屬性或指示演奏所需的技巧,卻無法記錄聲音本身。因此在錄音技術出現之前" +78,"音乐。每個國家對於音樂要素的官方定義不盡相同,但大致共有的要素包括: * 音高(旋律、和弦) * 節奏(速度、節拍、銜接) * 音色 不同頻率的聲音在不同時間所發出,形成旋律;不同頻率的聲音在同一時間或在極短時段內發出,形成和弦。拍子的快慢構成速度,規律性的強拍和弱拍形成節拍,音符之間的連貫狀況則謂之銜接。 常用的音高形成音階和調性;若要豐富音樂的織體,可以藉由對位法與和聲法。對位是令多條不同的旋律線同時和諧奏出,和聲則探討多個和弦的連續搭配,這兩種手法某程度是相對的。 音樂的織體主要有三種類型。單音音樂(Monophony)是最簡單的音樂織體,僅有單一旋律線,只包含了旋律和節奏兩項要素。如果有一條居支配地位的主旋律配以不同的和弦,便是主音音樂(Homophony)。若有兩條以上彼此獨立的旋律線交織,則是複音音樂(Polyphony)。 最後,每种乐器和每个人声都有自己独特的音色,不同音色的乐器组合賦予音樂独特的音響效果、情緒,以及象徵意義;安排这些乐器的组合叫做配器。 == 功能 == 音乐是一种符号,一种声音符号,表达人的所思所想。是人们思想的载体之一。音乐是有目的的,是有内涵的," +79,":用人声唱的声乐和用乐器奏的器乐,不论是声乐或器乐都有许多种结合的方式,如独唱、合唱、对唱,独奏、协奏,交响乐等。两者也可以混合,声乐用乐器伴奏,或在器乐交响乐中混入人声等等。 ===声乐=== 聲樂是由一個或多個歌手表演的音樂形式,使用樂器伴奏與否皆可,而人聲是作品的重點。含有人聲但重點不在其上的樂曲一般稱作器樂(如古斯塔夫·霍爾斯特的《行星組曲》中沒有歌詞的女聲合唱)。沒有樂器伴奏的合唱曲則通常稱為無伴奏合唱(a cappella,又稱阿卡貝拉)。 對於聲樂而言,相當多聲樂作品(部份或全部)為模仿其他聲音或純粹內容抽象,而運用非語言音節甚或是噪音,然而一般人特別注意歌詞的使用。較短小且含有歌詞的聲樂作品常稱為歌曲。 聲樂也許是最古老的音樂形式,因為它不需要任何樂器,僅僅人聲便可。在各種音樂文化中都存在著某種形式的聲樂。語言和音樂目的雖不同,皆有共同的音樂因子。 人聲又因性別、音域的高低、音質等不同,分成以下幾種声部: * 女高音(soprano) * 女中音(Mezzo-soprano,又作次女高音) * 女低音(alto/contralto) * 男高音(tenore/tenor" +80,"具有教化啟迪的作用,這在《詩經》大序、《禮記・樂記》、《荀子・樂論篇》、《史記・樂書》等作品中均可了解。甚至十三經中的《樂經》對音樂的功能也做了更深入的說明,惜今已失傳。 中国地域广阔,民族繁多,各地的民间音乐素材非常丰富。现代中国吸收了西方音乐的理论来整理古代的文献和民间音乐,编写民族乐器的曲目,使中国音乐有了很大的发展。 中国古代音乐家的地位不高,没有统治阶级对音乐的有效保护,所以虽然很早就发明了记谱法,但许多有名的作品没有能够流传下来,大部分流传下来的作品都是依靠民间艺人口传心授。但另一方面,古代大量士人官僚本身却精通音乐,如孔子、周瑜、阮籍、苏轼等皆是有名的琴人。 * 中国音乐史 ** 秦、汉以前音乐 ** 汉、唐时期音乐 ** 宋、元时期音乐 ** 明、清时期音乐 ** 中国近代音乐 ** 中国现代音乐 * 中国民族乐器 中国音乐分类 * 按类型 ** 戏剧音乐 ** 宗教音乐 ** 宫廷音乐 ** 民间音乐 ** 中国民族器乐 ** 民歌 * 按地域 ** 广东音乐 ** 江南丝竹 ** 东北秧歌 ** 西北民歌 ** 定县音乐 === 其他地区 === 音乐是自从人类出现" +81,"和技术 == 作曲家创作的音乐可以通过几种媒体传播;最传统的是现场体验,作为观众或者演奏者之一。现场音乐也可以通过无线电、电视或者因特网广播。有些音乐风格注重为表演而发音,有些则注重产生录音然后用于后期混响合成,它们从不被“直播”。现场表演风格的音乐在录制时也经常采用编辑和分离的功能以产生听起来比实际演出“更好”的效果。 由于引入了保护演奏者,作曲家,发行商和制作人的法律,譬如美国的1992年家庭录制法案(Home Recording Act),以及英国1979年修订的用于保护文学和艺术作品的Berne规范,录音和现场演出也变得更易通过计算机,设备,和因特网用一种通常称为音乐随选(music-on-demand)的形式获得。 在很多文化中,有些音乐的表演和聆听没有很大的区别,因为现场每个人都以集体形式通过某种方式涉及到音乐活动中来。在工业化的国家,通过录制形式,譬如听音乐录音或是观看音乐录像,成了最为普遍的感受现场演出的形式,这种形式大致开始于20世纪中叶。 有时,现场演出采用了预先录制的声音。例如,DJ可以使用唱片来制造刮碟效果,而有些20世纪的作品用独唱或独奏表演和预制的磁带音乐同" +82,"Needs Classical Music?: Cultural Choice and Musical Value. Oxford University Press. ISBN 0-19-514681-6. * Kertz-Welzel, Alexandra. Piano Improvisation Develops Musicianship. Orff-Echo XXXVII No. 1(2004):11-14. * Kertz-Welzel, Alexandra. The Singing Muse: Three Centuries of Music Education in Germany. Journal of Historical Research in Music Education XXVI no. 1(2004):8-27. * Kertz-Welzel, Alexandra. Didaktik of Music: A German Concept and its Comparison to American Music Pedagogy. International J" +83,"3. * Lee, Yuan-Yuan and Sinyan Shen(1999), Chinese Musical Instruments(Chinese Music Monograph Series), Chinese Music Society of North America Press, Chicago. ISBN 1-880464-03-9. * Molino, Jean (1975). Fait musical et sémiologue de la musique , Musique en Jeu, no. 17:37-62. * Nattiez, Jean-Jacques (1987). Music and Discourse: Toward a Semiology of Music (Musicologie générale et sémiologue, 1987). Translated by Carolyn Abbate (1990). ISBN 0-691-02714-5. * Owen, Harold (2000). Music Theory Resourc" +84,"經濟學(),為研究商品和服務關係的社會科學,包括其中所有的購買、生產、分配和消費等行為。 普通經濟學側重於研究經濟主體和客體之間的實力變化,並在經濟繁榮或衰退時總結出歷史規律,找到最優解。個體經濟學會分析何者才是經濟中對人類有利的要素,如市場機能、財產權、貸款、欠債及其間的交互作用;個體經濟以家庭、公司、買家和賣家為基本單位。總體經濟學則會分析國家範圍的銀行、儲蓄、股票和投資之間的交互作用,以及勞動力、資本、土地資源和通貨膨脹的利用,以便做出對國家有利的公共政策。 經濟學中還包括實證經濟學和規範經濟學,實證經濟學重點描述“是什麼、不是什麼”,而規範經濟學提倡“該做什麼、不該做什麼”;以及包括經濟理論與應用經濟學間、理性經濟學和行為經濟學間、以及主流經濟學和異端經濟學間的關係。 經濟分析可以應用於整個社會,包括商業、金融、網絡安全、醫療保健、工程 和政府。它也適用於犯罪、教育、家庭、女性主義 、法律、哲學、政治、宗教、社會制度、戰爭、科學 (Note the page is broken in some browsers but is still readable through the" +85,"On the Definition of Economics , Journal of Economic Perspectives, 23(1), pp. 221–33 .,蘇格蘭哲學家和經濟學家亞當·斯密在1776年將政治經濟學定義為「國民財富的性質和原因的研究」。他說: 讓-巴蒂斯特·賽伊在1803年將經濟學從公共政策裡獨立出來,並定義其為對於財富之生產、分配、和消費的學問Say, Jean-Baptiste (1803). A Treatise on Political Economy; or the Production, Distribution, and Consumption of Wealth, trans. 1834, C. C. Biddle, ed., Grigg and Elliot.。另一方面,托马斯·卡莱尔則譏諷經濟學為「憂鬱的科學」(Dismal science),不過這一詞最早是由马尔萨斯在1798年提出Carlyle, Thomas (1849). Occasional Discourse on the N[egro] Question , Frase" +86,"ics , Journal of Economic Perspectives, 23(1), p. 225. [pp. 221–33. : 羅賓斯認為,此定義注重的不是以經濟學「研究某些行為」,而是要以分析的角度去「研究行為是如何被資源有限的條件所改變」Robbins, Lionel (1932). An Essay on the Nature and Significance of Economic Science, p. 16 .。一些人批評此定義過度廣泛,而且無法將分析範疇侷限在對於市場的研究上。然而,自從1960年代起,由於理性選擇理論和其引發的賽局理論不斷將經濟學的研究領域擴張,這個定義已為世所認Backhouse, Roger E., and Steven G. Medema (2009). Defining Economics: The Long Road to Acceptance of the Robbins Definition , Economica, 76(302), V. Economics Spreads Its Wings . [Pp. 805–820 ." +87,"經濟學的目的在於,透過分析形成衆多物品和服務相對價格的市場機制,以及這些有限資源如何配置在不同的用途上,在這裏效率問題並非首要。微觀經濟學並不太重視經濟效率問題,因爲那可從規範經濟學獲得答案。不僅如此,微觀經濟學還分析市場失靈,究竟市場哪處無法產生有效率的結果及在完全競爭的條件下,描述市場的理論上應當情形。這學科的重要範圍包括:一般均衡理論、信息不對稱下的市場、博弈論的經濟活動應用和市場系統內的經濟物品彈性問題。 ===經濟發展=== 經濟發展指的不只是一個經濟體裡生產的增加和總體成長,也包括了這些發展對於體制內所帶來的改變,亦即均衡變動及新均衡的達成。經濟發展為個體經濟學的概念,並且與經濟成長有密切關係,經濟成長可以在沒有經濟發展的情況下達成,但經濟發展永遠會導致經濟成長。 ===市場=== 個體經濟學以經濟角度研究個體決策者在市場上的行為和市場形式,含括私人、民間、政府等決策單位,個體經濟學研究這些決策單位如何在市場上互動(假設交易品有稀缺性)。一個市場有可能是在交易產品(例如蘋果、鋁、或手機),或是交易生產要素的服務。個體經濟學認為,市場總體由眾多個體的買家和賣家所組成,便可對財" +88,"ics , Microeconomics, The New Encyclopædia Britannica, v. 27, pp. 347–49. Chicago. ISBN 978-0-85229-423-9 Varian, Hal R. (1987). microeconomics , The New Palgrave: A Dictionary of Economics, v. 3, pp. 461–63. London and New York: Macmillan and Stockton.ISBN 978-0-333-37235-7。 ===生產、成本和效率=== 在個體經濟學裡,生產指的是從投入到輸出的過程,這是一種透過投資來創造可以直接使用或用以交易的財貨的過程。生產是一種具有時間性的過程,也因此並不是即時的,而是經過一段時間產出某種數量的產品。這個過程中,可以區分的包括了消費產品(食物、理髮等)以及投資產品(機械、建築、道路等)、公共物品(警察、消防局等)或私有物品(電腦、香蕉等)、以及國防生產與民間生產的取捨。 機會成本指的是生產的經濟成本,即選擇不進行某項生產、而" +89,"產品的效能。倘使同樣投入下產出提高,經濟學就稱此為效率提升,即「浪費」減少。帕累托效率指的就是當效率被提到「最高」的情形,在那種情況下,任何改變都將導致效率減低。 生產可能性曲線(PPF)是一種用以說明稀缺、成本和效率的方式,在最簡單的例子裡,一個經濟體只生產兩種財貨(比如,槍械對比麵包)。生產可能性曲線以圖表(如右圖所示),顯示出了在既定的投入和技術之下,於兩種財貨生產中取捨所會產生的不同產出。在圓弧上的點代表了可能的產出量,也就是說,若是一種產品生產多少、另一種產品就只能生產多少的情況。 稀缺指的是人們想要達成、但是現有情況和資源下無法達成的生產情況,亦即超出了生產可能性曲線圓弧的地方,例如圖中的X點Montani, Guido (1987), scarcity , The New Palgrave: A Dictionary of Economics, v. 4, p. 254.。如果生產某產品的輸出增加,那麼生產另一項產品的輸出必然會減少,於是形成互相排斥的情況。這是因為生產某樣產品所需的投入會占用到生產另一樣產品的資源,也因此減少了另一樣產品的產出。生產一樣產品所帶來的另一樣" +90,"一國或有更多森林和煤礦。專精理論指出,在機會成本差異存在時,生產各方可以透過分工來達成比較優勢,而使得生產一定產品的成本相對降低。要注意的是,就算某個國家在所有產品的投入和輸出效率上佔有絕對優勢,它仍然可以透過專精和與他國貿易,來達成更高的輸出總和。因此,即使是最有效率、最發達的國家,仍然可以與最沒效率、最落後的國家一同分工生產。 在觀察上,許多擁有類似科技水準和多元投入要素的高發展國家(例如西歐),仍然彼此保持大量的貿易來往,使得許多經濟學研究專注在經濟規模和聚集,以解釋在分工的狀況下,生產不同產品的國家是如何促成貿易各國的利益Krugman, Paul R. (1980). Scale Economies, Product Differentiation, and the Pattern of Trade , American Economic Review, 70(5), pp. 950–59. William C. Strange, 2008, urban agglomeration , The New Palgrave Dictionary of Economics, 2nd" +91,"市場價格決定了分工的各方要如何分配生產的投入,廉價的投入會被用於較廉價的產出上,單在這個過程中,產出總合就有可能因為設計或附加價值而有所提升Cameron, Rondo (1993, 2nd ed.). A Concise Economic History of the World: From Paleolithic Times to the Present, Oxford, pp. 25, 32, 276–80.。這種生產上的分工造就了貿易利得的機會,使得資源的擁有人透過貿易把某種投入換成更為具有價值的產品,也因此而獲利。要衡量在貿易中獲得的利益,則是以衡量一個貿易所帶來的收入水準的增加Samuelson, Paul A., and William D. Nordhaus (2004). Economics,ch. 2, Trade, Specialization, and Division of Labor section, ch. 12, 15, Comparative Advantage among Nations section , Glossary of Terms , G" +92,"這種情況也被稱為是「有限情況下的利益最大化」(一個人的財富和收入則是決定限制的主因)。利益於此,乃指個體消費者如何在眾多產品之間做取捨,而其取捨結果將令產品排序,並且以此進行購買。 需求定理,指的是在一般情況下,一個市場裡的需求的數量與價格成反比。也就是說,一個產品的價格越高,越少人會想進行購買(假設其他情況不變)。隨著一個產品的價格下降,消費者會更願意购買它,而不是其他較為昂貴的替代品。另外,因為價格下降而導致的購買力上升連帶使得消費者購買能力提高。其他的因素可能改變需求,舉例來說,收入的增加會使得對於正常物品的需求曲線向外擴張。在進行這種分析的時候,經濟學家假設其他的因素保持不變。 供給指的是一個產品的價格以及在那個價格下會出現在市場上的產品數量,供給也可以由圖表方式形容價格和數量。在這種模型下生產者(比如公司)被假設是在追求利潤的最大化,也就是說,他們會試著生產和提供市場的產品數量是他們認為會取得最大利潤的。供給通常被表達為價格和供給數量之間的關係(假設其他情況不變),換句話說,一個產品可以被販賣的價格越高,生產者便會越樂意提供這樣產品,如右圖所示。更高的價格使得生產這樣產品可以" +93,"itannica, v. 17, p. 719.。供給曲線上的點則代表了邊際成本,也就是對生產者來說提供一個產品需要付出的成本。在均衡點上的價格是由供給與需求所決定,而在完全競爭的假設下,供給與需求將會等同均衡點上的邊際成本和邊際效用。 而在市場的供給面,影響生產的因素則被稱為是生產變數,這些變數在短期內決定了產出的數量。有些投入變數可以被輕易更換,例如電力、原始資源、加班工作等等,其他的變數則比較難更換,例如工廠和機械或是重要的資深員工。在長期來說,所有的投入都可以透過管理加以更改。短期和長期的分別又構成了供給的價格彈性,也就是供給面如何應對供給或需求方的更動而產生新的價格均衡點。 邊際主義描述消費者如何在不同的財富和收入水準限制下達成對他們最有利的位置,而生產者也會在各自生產的限制下(如產品需求、科技、價格等等)試圖最大化他們的利益。對消費者來說,他們利益最大化的位置在於當一個產品的邊際效用相對於其價格達到零的時候,用簡單的方式形容,這時消費者就買到了市場上對他而言「最划算的產品」,而沒有比這個產品更為有價值的選擇存在。相對的,生產者則會比較他們生產一個產品的邊際成本與邊際收益(亦" +94,"nary of Economics, 2nd Edition, Abstract. Hicks, J.R. (1963, 2nd ed.). The Theory of Wages. London: Macmillan.。 供給與需求是用來解釋完全競爭下的市場行為,然而,它仍然可以用來比較其他型態的市場。它也可以被用來解釋經濟體各層面的變因,舉例來說,這包含了總體經濟學下研究的國內生產毛額以及物價Blanchard, Olivier (2006, 4th ed.). Macroeconomics, ch. 7, Putting All Markets Together: The AS–AD Model , Prentice-Hall.。經濟學家也可以透過供給與需求來說明要在何種特定條件下市場才能有效的分配資源Jordan, J.S. (1982). The Competitive Allocation Process Is Informationally Efficient Uniquely. Journal of Economic Theory, 28(1), p. 1–18. Ab" +95,"學的研究,以及其他統計方法例如在沒有完整資訊和確定性下的回歸分析。其研究的特色是對於商業決策最佳化的追求,包括了如何降低成本、最大化利益等,當然,這是假設在其他情況例如科技和市場需求不變的條件下NA (2007). managerial economics . Hughes, Alan (1987). managerial capitalism , The New Pal grave: A Dictionary of Economics, v. 3, pp. 293–96.。 ===不確定性和賽局理論=== 不確定性在經濟學上,是指對於未來收入或損失無法預測的本質,無論是否為風險。若是沒有了不確定性,個體的經濟行為就會完全脫離工作和收入的不確定性,金融和資本市場也會淪為單純的市場階段媒介,而通訊產業則會完全消失Machina, Mark J. and Michael Rothschild (2008). risk , The New Palgrave Dictionary of Economics, 2nd Edition. Abstract. 。由於不確定性的型態眾多,在經濟學上有許" +96,"n F. Camerer (2003). Behavioral Game Theory Description and ch. 1 link (scroll down). Princeton.。透過這種分析,它可以將個體尋求利益最大化的舉動用來研究例如供給與需求的模型,同時也考慮到資訊的不確定性。這門研究最早追溯至1944年约翰·冯·诺伊曼的理論。賽局理論對於眾多領域都產生影響,包括了核子戰略、倫理學、政治學、甚至是演化生物學Aumann, R.J. (2008). game theory , The New Palgrave Dictionary of Economics. Abstract. 。 風險厭惡也會在一個發達的經濟裡促使專門管理風險和提供風險資訊的產業,例如保險業市場、期貨交易、金融商品、金融經濟學、或純粹的金融市場便是針對風險而生的產業。這塊領域同時也研究金融市場和公司的資本結構、金融市場的效率和健康性Bernanke, Ben and Mark Gertler (1990). Financial Fragility and Economic Performance ," +97,"uality Uncertainty and the Market Mechanism , Quarterly Journal of Economics, 84(3), pp. 488–500. 。資訊不對稱使得賣家即使有這方面的資訊,也沒有動機告知買家。相同的保險業在這方面的問題則是逆向選擇,也就是說那些很容易冒風險的人(也因此最容易出事),往往是那些最傾向於投保的人,也因此導致了道德風險,使得保險反倒促使更冒險的行為。這些問題都可能造成保險成本的上升,也因此驅逐了部分原本可以從此服務獲利的人(不完整市場)。而且,若是試圖解決單一問題,例如強制所有人保險,那麼仍然不能解決、反而會創造出更多道德風險的問題。資訊經濟學便是對於這些問題的研究,這在許多領域如保險、契約法、机制设计、貨幣經濟學、以及健保上Lippman, S.S., and J.J. McCall (2001). Information, Economics of , International Encyclopedia of the Social & Behavioral Sciences, pp. 7480–7486." +98,"ed., Ch.4, W.W. Norton).: 資訊不對稱和不完整市場可能會導致經濟的低效率,但這有可能透過市場、法律、或其他政策辦法加以修正。 自然壟斷或者稱為「技術上壟斷」是一種競爭失靈的極端例子。這種問題描述了既然生產一個產品時生產越多單一成本越低,那麼依此推斷每個產品最終應該只有一個生產者才是最有效率的。 公共物品依據市場失靈理論是一般市場是所缺乏供給的,這種失靈的最主要特色是人們可以使用公共物品而不必直接付費。 外部性指的則是市場價格所沒有反映出來的外部社會成本或利益。舉例來說,汙染會造成負面的外部性,而(政府付費的)教育則會創造正面外部性(如更高技術勞工)。政府經常向有製造負面外部性可能的經濟活動課稅或進行禁止,並且向正面外部性的受益者課稅以企圖矯正市場Laffont, J.J. (1987). externalities , The New Palgrave: A Dictionary of Economics, v. 2, p. 263–65.。另外,基本的供給與需求理論解釋了均衡的達成,但並沒有解釋達到供需均衡所需要花費的時間Blaug, Mark (2007)." +99,"Environment. McGraw-Hill.。 儘管如此,許多經濟學家的研究則提出質疑市場失靈的理論。例如公共選擇學派便提出了與市場失靈相反的例子-政府失靈,認為政治上的經濟干預和特殊利益才是導致市場失靈的主因Buchanan, James M. (2003). Public Choice III. Cambridge: Cambridge University Press. Description and preview. 。而奧地利經濟學派則更進一步,主張低效率的壟斷只有在政府主導之下才會形成,並且主張市場的低效率只有透過尋求利潤的企業家精神才能消除,而政府並不能做出正面影響。 ===公共產業=== 公共財政是研究政府公家機關如何進行預算和稅收的經濟學領域,研究的包含了稅制的制定(那些人在繳那些稅)、政府開支活動的成本效益分析、金融政策、政府對於經濟效率和收入分配的影響。公共選擇理論便是以個體經濟學角度研究公共產業的學派,分析各自尋求利益最大化的政治個體,例如政客、選民、官僚、和利益團體,以及他們在政治過程中的互動Musgrave, R.A. (1987). public f" +100,"60年代以來,總體經濟學也被引進一些個體經濟學的劃分方式,例如分析個體的理性、效率市場假說、以及不完全競爭等Ng, Yew-Kwang (1992). Business Confidence and Depression Prevention: A Mesoeconomic Perspective , American Economic Review 82(2), pp. 365–371. ,這些發展解決了部份對於這些領域的不一致性質疑Howitt, Peter M. (1987). Macroeconomics: Relations with Microeconomics .。 總體經濟學同時考量影響成長和國家收入的因素,這些因素包括了資本累積、科技進步、以及勞動力的成長等Blaug, Mark (2007). The Social Sciences: Economics , Macroeconomics, The New Encyclopædia Britannica, v. 27, p. 349.。 ===增长=== thumb|顯示世界各國GDP实际增长率的圖表。|alt=A" +101,"時,约翰·梅纳德·凯恩斯出版了《就业、利息和货币通论》一書,建構了凱恩斯主義的主要理論。凱恩斯認為在經濟蕭條時,對於產品的總需求可能有不足的現象,導致高失業率以及可能的產能流失。他於是認為應該增加政府在公共產業的活動,包括了以中央銀行實行擴張性的貨幣政策和財政政策以穩定景氣循環。也因此,凱恩斯主義的一大結論為,在某些情況下,市場本身並不能將產出和雇傭推到充分就業的程度。约翰·希克斯的IS-LM模型便是解釋凱恩斯理論的經典例子。 隨著時間發展,對於景氣循環的認知也逐漸增進並擴張到其他領域,大多是作為對凱恩斯理論的回應。新古典綜合學派便是綜合凱恩斯經濟學與新古典主義經濟學,認為凱恩斯主義雖然在短期上是正確的,但新古典主義則在中長期更有解釋說服力Blanchard, Olivier Jean ([1987] 2008), neoclassical synthesis, The New Palgrave Dictionary of Economics, 2nd Edition. Abstract. 。新興古典經濟學派則從凱恩斯景氣循環理論中脫出,認為市場的蕭條是因為不完全資訊所造成的,持這種立" +102,"投資和資本的錯誤分配,因而形成信用泡沫。他們認為景氣循環裡的擴張期是對於資本的濫用期,而緊接而來的衰退期才是經濟復甦的開始。奧地利學派的理論不被凱恩斯學派或新古典主義學派所重視,然而在學界仍然有部分支持者。 ===失業=== 一個經濟體裡的失業人數可以由失業率來計算,亦即有多少勞工在積極的尋找工作,退休者、正接受教育者、或因為其他因素而放棄尋找工作者並不包含在失業者裡。失業可以依據幾種型態來分類,古典式失業指的是當工資太高因而導致雇主不願雇用更多勞工時,工資太高的原因可能是最低工資法規、或是工會的活動導致。依據古典式失業說法,摩擦性失業則是當市場有應對的工作存在,但在配對工作的過程中勞工所需要尋找和等待的時間Dwivedi, 443.。結構性失業則包含一系列導致失業的原因,比如勞工有的技能與市場需要的技能不同時Freeman (2008). http://www.dictionaryofeconomics.com/article?id=pde2008_S000311 .。當一個經濟體的產業正進行大規模的轉變時,因為許多勞工既有的技能與新興產業不同,許多結構性失業便有可能發生Dwived" +103,"配對交易雙方的需求(比如蘋果交換書本)。由於貨幣的通用性,它使得交易的交易成本得以降低,如此一來生產者便可以輕易將產出換為市場通用的價值,而不需轉換必須進行二次交易的物品Tobin, James (1992). Money (Money as a Social Institution and Public Good), The New Palgrave Dictionary of Finance and Money, v. 2, pp. 770–71.。 對一個經濟體而言,貨幣供給與總生產的面值和物價水準有密切關係,也因此,管理貨幣供給是貨幣政策的重要課題Friedman, Milton (1987). quantity theory of money , The New Palgrave: A Dictionary of Economics, v. 4, pp. 15–19. Samuelson, Paul A., and William D. Nordhaus (2004). Economics, ch. 2, Money: The Lubroicant of Exchange s" +104,"來,並且研究國際貿易可以帶來的貿易利得,重要的政策研究包括了關稅以及貿易壁壘對於進出口和經濟成長的影響。國際金融學則是整體經濟學裡一門專門研究資本如何在各國之間流動的領域,並且研究匯率。國際貿易的增長、各國分工的增強和密集化是全球化重要的來源Anderson, James E. (2008). international trade theory , The New Palgrave Dictionary of Economics, 2nd Edition.Abstract. Venables, A. (2001), international trade: economic integration , International Encyclopedia of the Social & Behavioral Sciences, pp. 7843–7848. Abstract. Obstfeld, Maurice (2008). international finance , The New Palgrave Dictionary of Economics, 2nd Edition." +105,"治經濟學,「經濟體系比較」指的則是衡量不同經濟體系之間的經濟行為和成長差異Heilbroner, Robert L. and Peter J. Boettke (2007). Economic Systems , The New Encyclopædia Britannica, v. 17, pp. 908–15. • NA (2007). economic system , Encyclopædia Britannica online Concise Encyclopedia。 ==研究方法== 當代的經濟學經常使用數學作為研究手段之一,經濟學家使用的研究工具包括了微積分、線性代數、統計學、賽局理論、以及電腦科學Debreu, Gérard (1987). mathematical economics , The New Palgrave: A Dictionary of Economics, v. 3, pp. 401–03.。學術界的經濟學家一般都有使用這些工具的基本能力,少數經濟學家則專注在數理經濟學和其他數學的方式。 ===理論=== 主流經濟學理論依靠的是先驗的經濟模型,並" +106,"貨幣主義主張的貨幣數量論指出對貨幣的供給擴張會因為市場的理性預期而造成通貨膨脹。在發展經濟學中,已開發國家一般被預測會有比較小幅度的經濟成長,因為資本投資的邊際回收會隨著發展程度而開始下降,這種趨勢就在亞洲四小龍的發展上被觀察到。有時候這些經濟假說所主張的只是一種「定向」或「趨勢」,而非絕對的數據改變Quirk, James (1987). qualitative economics , The New Palgrave: A Dictionary of Economics, v. 4, pp. 1–3.。 在近幾十年,賽局理論於社會科學領域的急速擴張也對經濟學產生巨大影響,透過例如納什均衡點作為研究經濟個體決策的基準,經濟學家可以在不依靠實際世界資料的情況下對許多領域做出分析和預測R.J. Aumann (2008). game theory, The New Palgrave Dictionary of Economics, 2nd Edition. Abstract. Martin Shubik (1981). Game Theory Models and Methods in" +107,"rics and truth: the methodology of econometrics, Cambridge University Press, p. 13.,也因此,經濟學家通常倚賴觀察既有資料的方式來測試他們的理論,雖然這樣觀察實驗的結果沒有封閉實驗來的準確、也比要容易被質疑。儘管如此,實驗經濟學是一門逐漸在成長的領域,而且對既有模型的觀察也在增加。 統計學的研究方法例如回歸分析也很常見,使用者用此方法來估計規模、經濟重要性、以及統計顯著性,藉此來提出假說、並且控制變因的影響。透過這種作法,一個假說有可能因此獲得支持,儘管其準確性仍然只是機率上的、而不是絕對的,判斷研究的標準則是依靠其可否證性以及證偽結果。由於實驗結果的不確定性,這些觀察研究並不一定要對其研究問題提出決定性的結論,甚至也不一定要提出主要的發現。在實用經濟學領域上,以線性規劃來進行投入-產出模型的研究相當常見,為了研究某種政策對各方面的影響,經濟學家常將大量資料交由電腦統計分析。 專業的同行評審和對於結果的重新生成也能進一步確保觀察實驗不會產生有偏見的、錯誤的、或輕率概化的結果Blaug, Mark (200" +108,"and Significance Testing in Economics , Journal of Economic Methodology, 15(1), pp. 1–37 (2005 prepubication version). Reply of McCloskey and Ziliak and rejoinder, pp. 39–68.,儘管如此,在過去40年間,單純為批評其他研究結果的文章在如美國經濟評論的期刊上一直減少,這被認為是期刊試圖增加其引用數量的一種結果。 實驗經濟學也造就了許多以封閉式對照實驗為方法的研究,直接測試一般被視為真理的一些經濟學理論,這將傳統上經濟學與自然科學的區分給縮小了Bastable, C.F. (1925). experimental methods in economics , Palgrave's Dictionary of Economics, reprinted in The New Palgrave: A Dictionary of Economics (1987, v. 2, p. 241. Smith, Vernon L. (1" +109,"張經濟學也是「真正的科學」的一部分。 ===職業=== 經濟學這一領域的專業化,反映在這個領域的研究所課程的成長上,並且被稱為是「經濟學自1900年來的重大改變」O. Ashenfelter (2001), Economics: Overview , The Profession of Economics, International Encyclopedia of the Social & Behavioral Sciences, v. 6, p. 4159.。大多數主要的大學和院校都有一個專精的經濟院所並頒發經濟學的學位,不論是在通識教育、商業或其他專業領域上,經濟學課程往往是必修的課程。 諾貝爾經濟學獎在每年會依據其專家評審頒發給對這個領域有重大貢獻的經濟學家。在私人企業,經濟學家通常擔任顧問的角色,尤其是在銀行業和金融業。經濟學家也替眾多政府部門服務,例如國庫、中央銀行、和統計組織。 ===相關領域=== 經濟學是社會科學的一門,同時也影響了眾多其他領域的學術發展,例如經濟地理學、經濟史、公共選擇理論、家庭經濟學、以及制度經濟學。 法律經濟學是一種以經濟理論角度切入研究法學的方" +110,"政策的關係Groenewegen, Peter (2008). 'political economy' , The New Palgrave Dictionary of Economics. Krueger, Anne O. (1974). The Political Economy of the Rent-Seeking Society , American Economic Review, 64(3), pp.291–303。歷史學家有時會使用政治經濟學一詞來描述研究在歷史上有特定利益的個人或團體是如何利用政治手段來取得利益McCoy, Drew R. The Elusive Republic: Political Ecocomy in Jeffersonian America , Chapel Hill, University of North Carolina, 1980.。 經濟學的社會學分支也透過經濟社會學發展出來,主要是根基在爱米尔·涂尔干、马克斯·韦伯、格奥尔格·齐美尔等人的作品上,用以分析經濟發展和經濟現象對於社會傳統的影響。重要的作品包括了马克斯·韦伯的《新教倫理與" +111,"的,而沒有天然金礦銀礦的國家則只能靠著出口產品並且限制進口金銀以外貨物來累積財富。重商主義主張進口便宜的原始資源以用來生產出口的貨物,並且以關稅防止來自國外產品的競爭NA (2007). mercantilism , Blaug, Mark (2007). The Social Sciences: Economics . The New Encyclopædia Britannica, v. 27, p. 343.。 重農主義則是一群18世紀的法國思想家組成,並認為經濟體是一個收入和產出不斷循環的圈圈,他們主張只有農業生產才能夠產出超過成本的價值,也因此農業是所有產業的根基。他們反對重商主義對製造業和出口的重視而忽略了農業。重農主義者並且主張淘汰耗費大量成本的稅金收取制度,改以地主的所得稅替代之。比起重商主義,重農主義者更支持自由放任的政策,認為政府不該干預經濟NA (2007). physiocrat , • Blaug, Mark (1997, 5th ed.) Economic Theory in Retrospect, pp, 24–29, 82–84. Cambridge.。" +112,"分工可以帶來的經濟好處,包括了勞動產能的上升以及貿易利得,無論是鄉村或是都市、或是不同國家之間都可以藉此獲利Deardorff, Alan V., 2006. Glossary of International Economics, Division of labor .。他的理論認為分工的程度是由市場的需求所決定,這也成為了後代經濟學家對於公司和產業組織的主要理論來源Stigler, George J. (1951). The Division of Labor Is Limited by the Extent of the Market. Journal of Political Economy, 59(3), pp. 185–193. 。亞當·斯密提出了被廣泛認為是經濟學裡最重要的命題:亦即在競爭之下,資源的持有人(無論是勞動力、土地、還是資本)會尋求利益最大化的途徑,於是在均衡之下所有同一資源的持有人都會獲得相同的報酬(排除失業等因素外)Stigler, George J. (1976). The Successes and Failures of Professor Smit" +113,"Hill, ch. 2, Markets and Government in a Modern Economy , The Invisible Hand, p. 30.。斯密並且主張個體尋求利潤的動機是生產過程中所不可或缺的,他們獲得的利潤就代表了他們所生產出的價值Smith, Adam (1776). The Wealth of Nations, Bk. IV: Of Systems of political Œconomy, ch. II, Of Restraints upon the Importation from Foreign Countries of such Goods as can be Produced at Home , para. 3-5 and 8-9 .。他這樣說道: 因此,由於每個個人都會盡力把他的資本用以支持並管理國內的產業,這些產業的生產於是便能達到最大的價值;每個個人也必然竭力的使社會的年收入盡量擴大。確實,他通常並沒有打算要促進公共的利益,也不知道他自己促進了這種利益至何種程度。由於寧願支持國內的產業而非國外的產業,他只是盤算著他自己的安全;他管理" +114,"這在他心目中才是「所有生產的唯一目的」Smith, Adam (1776). The Wealth of Nations, Bk. IV, ch. 8, para. 49 .Samuelson, Paul A., and William D. Nordhaus (2004). Economics. 18th ed., McGraw-Hill, ch. 2, Markets and Government in a Modern Economy , The Invisible Hand, p. 30. Blaug, Mark (2008). invisible hand , The New Palgrave Dictionary of Economics, 2nd Edition, v. 4, pp. 564–66. Abstract .。斯密並且以看不見的手來評論眾多的經濟學領域,包括了政府對於競爭、貿易的限制Smith, Adam (1776). The Wealth of Nations, Bk. IV: Of Systems of political Œconomy, ch. II" +115,"76 , and 239-40 .Blaug, Mark (2007). The Social Sciences: Economics , The New Encyclopædia Britannica, v. 27, Analysis of the market, pp. 343–44. _____ (1997). Economic Theory in Retrospect, 5th ed., in ch. 2, sect. 19, Adam Smith as an Economist, pp. 56–62. Vaughn, Karen I. (1987), invisible hand , The New Palgrave: A Dictionary of Economics, v. 2, pp. 997–99. Link . Skinner, Andrew (2008). Smith, Adam (1723–1790) , Policy, The New Palgrave Dictionary of Economics, 2nd Edition, v. 7, pp. 559–61" +116,"漸進的增加,如此一來有限的土地便會導致暴增的人口勞動力產能報酬遞減,於是便會導致生活水平大幅退步的人口過剩出現Malthus, Thomas (1798). An Essay on the Principle of Population.。馬爾薩斯並且質疑市場經濟的調節性能,批評市場本身無法達成完全就業,並且稱經濟體傾向於過度存款而導致消費不足,這一理論後來也被凱恩斯加以利用。 亞當·斯密注重在財富的生產上,而大衛·李嘉圖則注重在收入的分配。李嘉圖認為地主與資本和勞工之間必然會有衝突存在,他主張隨著人口的成長和生產增加,有限的土地必然會使得租金逐漸上漲,於是減低資本獲得的利潤、以及勞工的工資。李嘉圖是第一個提出了比較優勢的經濟學家,依據此理論,無論一國是否在生產上每個層面都比他國優越或落後,邊際的生產成本差異使得他們都可以從國際貿易過程中獲利David Ricardo, 1817. On the Principles of Political Economy and Taxation.。比較利益到今天一直被經濟學家認為是解釋貿易利得的最基本框架Ronald Findlay, 2008." +117,"論,主張勞動是價值的主要或唯一來源。 馬克思主義經濟學是從古典經濟學裡衍生而出的,卡爾·馬克思提出了其理論的基本模型。馬克思在1867年出版了《資本論》一書,以勞動價值理論為基礎,主張資本主義的生產模式必然會產生剩餘價值,也就是資本對於勞工的剝削Roemer, J.E. (1987). Marxian value analysis . The New Palgrave: A Dictionary of Economics, v. 3, 383. Mandel, Ernest (1987). Marx, Karl Heinrich , The New Palgrave: A Dictionary of Economics, v. 3, pp. 372, 376.。依據勞動價值理論,一個產品的價值是由生產過程中所必需的社會勞動所決定的,而剩餘價值理論則主張勞工的工資只構成了產品價格裡的勞動力价格,即让劳动力再生产的部分,從此認為有剝削存在。 ===邊際革命=== 從1870年至1910年之間,一系列經濟學家開始對古典經濟學的假設提出挑戰,這一系列的變革後來被稱為是「邊際革命」Screpan" +118,"Labor and the Method of Economics. Princeton: Princeton University Press, p. 55-64.。 ===凯恩斯主义=== 凯恩斯主义经济学發展自約翰·梅納德·凱因斯1936年出版的《就業、利息與貨幣的一般理論》一書,此書確立了總體經濟學一領域的獨立 Blaug, Mark (2007). The Social Sciences: Economics , The New Encyclopædia Britannica, v. 27, p. 347. Chicago.。凱恩斯在此書中研究許多在短期(亦即,長期的經濟均衡達到之前)影響總收入和生產的因素,他認為高度的失業可能會因為市場缺乏有效需求而無法自行調整,並且主張為了增加總需求,政府應該透過擴張性的財政政策來克服這種情況。在當時經濟大恐慌的環境下,凱恩斯的此一理論對經濟學界產生重大影響Tarshis, L. (1987). Keynesian Revolution , The New Palgrave: A Dictionary of Economics, v. 3" +119,"持其存在的本質。 ===新古典主義=== 邊際革命促生了主導當代主流經濟學的新古典主義經濟學,阿爾弗雷德·馬歇爾提出了「經濟學」一詞來形容對於「經濟科學」的研究,用以對照之前的政治經濟學。這種轉變就有如在當時數學模型對於自然科學的影響Clark, Barry (1998). Political Economy: A Comparative Approach, Praeger.。新古典主義經濟學將供給與需求作為分析市場均衡下價格和供給數量的模型,以此解釋產出以及收入的分配。比起古典經濟學,新古典主義重視的是市場的邊際需求Campos, Antonietta (1987). marginalist economics , The New Palgrave: A Dictionary of Economics, v. 3, p. 320。到了20世紀,新古典主義經濟學遠離了古典經濟學以基數作為分析個體之間生產關係和社會總和利益的方式R.D. Collison Black (1987 [2008])). utility , The New Palgrave: A Dictionary of E" +120,"和其衍生的貨幣主義開始挑戰凱恩斯主義。依據該學派的米爾頓·傅利曼等人的說法,市場經濟在貨幣供給持平(亦即,沒有過多的膨脹或緊縮)的條件下,可以自行調整供需。依據這種理論,政府的角色是透過中央銀行,調控在經濟體中流通的貨幣數量,除此之外,不需要干預自由市場的活動。芝加哥學派以及後來小罗伯特·卢卡斯提出的理性預期理論,大幅度取代了凱恩斯主義在總體經濟學的支配地位。前美國聯邦準備理事會主席本·伯南克便是支持傅利曼解釋景氣循環說法的經濟學家之一。當代的主流經濟學經常被二分為美國東海岸與西海岸大學代表的鹹水學派與芝加哥等內地大學代表的淡水學派。 米爾頓·傅利曼基本上採納了許多亞當·斯密和其他古典經濟學家提出的個體經濟學概念並且將之現代化,比如他在1970年曾主張一個企業的社會責任是要「在自由市場的競爭下,努力利用既有資源進行可以最大化利潤的商業活動」Friedman, Milton. The Social Responsibility of Business is to Increase its Profits. The New York Times Magazine 13 Sep. 1970." +121,"The Origin of Economic Ideas, Guy Routh (1989)。 在《國富論》一書中,亞當·斯密大力抨擊那些他認為是依賴政治權力來影響市場運作的個人或團體,在斯密的年代這些人通常被稱為「派系」,今天則是我們所熟知的利益團體例如政黨、銀行、公司、工會等等。作為社會科學的一部分,經濟學研究應該要獨立於政府政策或其他決策團體的影響之外,然而,政策制定者和政治人物經常以特定經濟學的理論或說詞來正當化他們推動的政治議程,許多時候往往脫離他們公職的負責領域Sara Ledwith and Antonella Ciancio, Special Report: Crisis forces dismal science to get real , Reuters (July 3, 2012)。經濟學理論在實踐上與政治不可避免的密切關連經常被拿來做為批評的對象,批評者往往將經濟學作為一個學術領域的角色與特定議題或價值觀混為一談。儘管如此,經濟學對於政府經濟政策的形成是理所當然有影響力的,而且在某種程度上可以說是政治經濟學的分支。許多學術界的期刊經常探討經濟學者之間對於某種政策" +122,"Integration of Social and Economic Policy , The Social Development Review,乃至總體經濟學上的眾多政策,也經常是爭議的焦點和對於主流經濟學批評的來源Bernd Hayo (Georgetown University & University of Bonn), Do We Really Need Central Bank Independence? A Critical Re- examination , IDEAS at the Department of Economics, College of Liberal Arts and Sciences, University of Connecticut Gabriel Mangano (Centre Walras-Pareto, University of Lausanne BFSH 1, 1015 Lausanne, Switzerland, and London School of Economics), Measuring Central Bank Ind" +123,"e McCloskey則提出許多透過數據觀察進行的經濟學實證研究在方法或結果上都有問題,此點批評在經濟學界受到不少重視,不過,也有某些依賴先驗而拋棄實證觀察作為研究方法的經濟學者和學派存在,例如奧地利經濟學派便是一例。 一些人則責怪經濟學家無法準確預言未來的經濟走向或經濟危機的到來,舉例來說,在2007年-2013年环球金融危机便引發許多人批評主流經濟學的教科書與現實脫離。國際貨幣基金組織在2002便曾發表他們對一群經濟學家調查他們對未來經濟蕭條的預測結果,提出97%在1990年代受調查的經濟學家沒有成功預測未來一年的經濟緊縮,而即使成功預測的人,也經常低估了緊縮的嚴重性 How Accurate Are Private Sector Forecasts? Cross-Country Evidence from Consensus Forecasts of Output Growth , by Prakash Loungani, International Monetary Fund (IMF), December 2002。 ===質疑=== 經濟學長久來持續被質疑是依靠不實際的、無" +124,"2nd Edition Online abstract. Accessed May 30, 2008.。不過,資訊經濟學包含的就是這方面以數學研究和行為經濟學來研究個體在利潤最大化以外的行為。另外一些主流的經濟學家例如凱恩斯也曾觀察到大部分的經濟學領域都是透過概念上的而非數據上的證據來建立,也因此無法像其他自然科學一樣透過數據來驗證。 近年來,批評新古典主義經濟學的女性主義者也開始浮現,促使了女性主義經濟學的誕生England, Paula (1993). The Separative Self: Androcentric Bias in Neoclassical Assumptions , Beyond Economic Man: Feminist Theory and Economics, University of Chicago Press, pp. 37–53.。女性主義經濟學批評經濟學一般對於實證和客觀的假設,認為現有的模型和方法只反映出男性社會的偏好。女性主義經濟學經常提出的批評包括經濟學忽視家庭主婦的家事勞動價值、家庭的經濟掌管和收支Marilyn Waring (19" +125,"149–54. Mayumi, K. 1995. Nicholas Georgescu-Roegen (1906–1994): an admirable epistemologist. Structural Change and Economic Dynamics 6: 115–120. Mayumi, K. and Gowdy, J. M. (eds.) 1999. Bioeconomics and Sustainability: Essays in Honor of Nicholas Georgescu-Roegen. Cheltenham: Edward Elgar. Mayumi, K. 2001. The Origins of Ecological Economics: The Bioeconomics of Georgescu-Roegen. London: Routledge.。 一些人例如納西姆·尼可拉斯·塔雷伯則批評經濟學的理論方式,認為經濟學過度倚賴帕累托的理型論研究模型,並主張經濟學理論錯誤的結果可能會對人類整體造成龐大災害Cox, Adam. Blame Nob" +126,"会计学、审计学 == 參考文獻 == == 外部連結 == ;一般資訊 * * 網路上的經濟學期刊 * Economics 大英百科有關經濟學的條目 * 經濟學家資源 : 美國經濟學會維持的資料庫 ;機構和組織 * 世界各地的經濟學系所、部門、和研究中心 * (OECD) Statistics 經濟合作與發展組織發表的各國經濟數據 * United Nations Statistics Division 聯合國經濟數據資料 * World Bank Data 世界銀行經濟資料 ;學習資源 * Social Science Research Network 經濟學研究發表特別突出的社會科學研究網路 * MERLOT Learning Materials: Economics 美國的經濟學資源網站 * 麻省理工「開放式課程」經濟學首頁 麻省理工學院的開放式經濟學課程 * 麻省理工「開放式課程」經濟學首頁 中文版本 * Online Learning and Teaching Materials 英國的經濟學線上學習資料庫 * The Library of Economics and Lib" +127,"政治學,或稱政治科學等,是一門对政治、治理和權力體系进行科學研究,分析政治活動、政治制度、政治思想和行為以及相關憲法和法律的社會科學。 政治學涉及政治過程、結構和內容以及政治現象等主題的科學教學和研究。 現代政治學大致可分為比較政治學、國際關係學和政治理論三個分支學科。 其他值得注意的子學科是公共政策和公共行政、國别政治和政府、政治經濟學和政治方法論,提及政治社會學、公法和政治領域分析等。政治學與經濟學、法學、社會學、歷史學、哲學、人文地理學、政治人類學、心理學和性別研究等領域相關,並借鑑了這些領域。 政治學在方法論上是多種多樣的,並且採用了許多源自心理學、社會研究和政治哲學的方法,包括實證主義、解釋主義、理性選擇理論、行為主義、結構主義、後結構主義、實在論、歷史制度主義和多元主義。 政治學作為社會科學之一,使用的方法和技術與所尋求的查詢類型相關,包括歷史文獻和官方記錄等一次文獻和學術期刊文章、調查研究、統計分析、 案例研究、實驗研究和模型構建等二次文獻。 == 政治學者 == 现代政治学的学术规范要求政治学家对作为研究对象的政治事务保持严格的价值中立。不过,由于政治學家自身对政治過程" +128,"马、赫西俄德、修昔底德和欧里庇得斯等人的著作裡。後來柏拉圖開始分析希腊城邦的政治制度,將他們原本以文學和歷史為方向的研究分析抽取出來,應用至我們目前所稱的哲學上。同樣地,亞里士多德在柏拉圖的分析上又建立了以歷史經驗為根據的研究,及發展了以比較方式研究政治,也就萌生了日後的比較政治理論,被譽為政治學之父。而柏拉图的《理想国》和亚里士多德的《政治學》更被认为是政治学的创始之作。尤其是亚里士多德在相当程度上指出了后来西方政治学研究的正确方向。 在羅馬統治時期,随着斯多葛学派的兴起,著名的歷史學家如波里比阿、蒂托·李维、普魯塔克也記載了羅馬共和崛起過程,和其他國家的組織及歷史,以及恺撒和西塞罗等政治家,使我們得以更加了解當時羅馬的政治和戰爭。在當時對政治的研究主要是以對歷史的學習、政府治理的手段、和描述政府運作的情況為方向。而西塞罗更是对国家(Res Publica,“公众事物”)概念进行了界定,设计出了权力制衡的运作模式,并发展了自然法思想。 === 中世纪时期 === 隨著羅馬帝國的衰落,政治研究的舞台更加擴散。一神教的崛起,尤其是西方文化裡基督教的崛起,在政治和政治行為上產生更多研究空間" +129,"機構和政治參與者做直接的觀察,其著作《君主論》,教導君主其只顧後果不顧過程的政治手段,內容與基督教教義衝突甚大。《君主論》的完成,被視為政治學由基督教中完全分離的一個里程碑。後來在啟蒙時代裡,學術領域的擴張進一步的拓廣政治學的研究範圍,超過了原本只是針對決策基準的研究。 法国政治思想家让·布丹则对主权问题进行了系统地探讨。他在《》中认为主权是国家的基本属性,是至上和不可分割的。并且认为为了避免社会动荡,有必要加强君主的绝对权威。他的政治理论仍未摆脱中世纪的影响,但是他的主权理论促进了现代民族国家的形成,也是理性主义政治学的先驱。 在18世紀牛頓的出現,開始建構現代科學理論的時候,政治學也跟隨其他學科一樣開始進行科學化的研究,在牛頓的世界裡,利用實驗方法得出的結果來歸結為知識、相信自然、訴諸數學解釋等等,都對政治學的研究開出了一條新的方向。与此同时出现的一大批思想家、政治家的各种政治思想主张,包括了天赋人权、社会契约、分权制衡等学说以及自然法、自然权利、主权、自由、民主、平等、幸福等观念,成为了其后政治学研究的主导框架。当霍布斯和约翰·洛克从不同的假设出发探讨政府的起源及其正当性问题时," +130,"学有了较大的差别。 ==== 行為主義革命與新制度主義 ==== 在1950年代及1960年代,一場強調對個人和群體行為進行系統和嚴格科學研究的行為主義革命席捲了整個學科。專注於研究政治行為,而不是制度或法律文本的解釋,是早期行為主义政治學的特徵。代表成果包括Robert Alan Dahl、Philip Converse的研究工作,以及社會學家Paul Lazarsfeld和民意學者Bernard Berelson的合作。當時,針對個人和團體的科學研究從原本有系統而嚴厲地學科裡逐漸游離。同時分析領域也朝向更深層和複雜的部分,也使政治學與其他不同的學科的研究關係更加緊密,尤其是社會學、經濟學、歷史、人類學、和统计学。政治行為的研究者也越來越倚賴以科學的方法做學術研究,如量化研究、方法學等,根基在對政治走向的經驗證實和推斷的假設前提上,並歸納針對人類與團體的政治行為解釋。學科越來越注重與現實間的關聯,或以使用新的方法和途徑來研究如何解決政治和社會上的問題。 1960年代末和1970年代初見證了演繹、博弈論形式模型建立技術的使用的興盛,目的是在該學科中生成更具分析性的知識庫。這一時期發生借" +131,"politics . In 同時,當代政治學家們專注研究社會上對決策權力的轉移和分配,以及政治的制度和其所扮演的角色—包括一般的政府和國際組織、政治行為和公共政策。他們藉由檢驗許多因素來評估治理和特定政策的成功性,包括穩定性、司法、物質財富、以及和平。一些政治學家試圖分析政治來追求進一步的實證科學題材。其他的政治學家則以基準的題材分析政治,以提出特定的政策建議。 == 研究对象 == 基于对政治的不同看法,对政治学的研究对象也有不同的见解。 国家科学认为政治本身是一种国家活动,因此政治学是研究国家现象的科学。而以大卫·伊斯顿为首的政治学者则认为政治学的研究对象是权力(power)與权威性的价值與利益分配,这种观点對於後來的政府研究以及多元主義的發展都有極大影響p5, Heywood, Andrew. Politics, Palgrave Foundations, (1997)。另外一个影响较大的观点是,认为政治学是研究人类社会的政治关系的科学。也有人认为政治学的研究对象是公共事務。而《布莱克维尔政治学百科全书》则认为政治学的研究对象是政府與公共政策。 == 研究方法 == 传统上,政治" +132,"==== 行为主义 ==== 行为主义反对政治哲学研究中的思辩与演绎方法,也反对制度主义的静态描述和简单写实,认为政治学的研究对象应该是实际存在并且可以观察到的政治行为。行为主义注重数据的收集和整理,常常运用数学模型、模拟实验、直接探查、实地观察技术手段进行研究。主要原则包括了精确性、纯科学、量化研究和价值去除。其主要理论包括: * 政治系统论:采用系统论和控制论的方法,不以国家为对象,而以政治系统为研究对象。注重从宏观角度考察政治过程,尤其是政策制定和执行中的价值分配问题。主要代表人物有大卫·伊斯顿、莫顿·卡普兰。 * 结构功能主义:发展自政治系统论,研究政治系统的功能和结构,及其相互关系。代表人物有加布里埃尔·阿尔蒙德。 * 政治沟通理论:采用控制论、信息论的方法,把政治系统的运行过程看作是信息的变换和控制过程,注重研究政治系统中的决策活动和信息沟通。主要代表人物有卡尔·多伊奇。 * 政治文化理论:研究影响人们政治行为的文化因素,包括政治认知、政治感情、政治态度和政治价值等。主要代表人物有悉尼·唯巴。 * 政治发展理论:继承了制度主义中的历史比较分析方法,同时结合了行为主义中的新方" +133,"象無法避免平衡量方面的因素。 ==== 新制度主义 ==== 1980年代以来,美国兴起了新制度主义学派,重新强调制度主义的研究路径,注重研究制度的动态运动对政治活动的影响,尤其是制度与行为和观念的关系。作为对行为主义的反动,新制度主义认为从个体出发从而研究整体的观念是错误的,整体并不是个体的简单相加。其主要流派有: * 理性选择制度主义:采取了理性选择理论的方法,但认为其不够现实,因此认为政治研究的核心是既定制度约束下个人寻求利益最大化的过程,以及制度对个人选择过程的影响。 * :从历史分析的角度,分析既定社会制度对其历史发展道路的影响。认为在一定历史发展中形成的稳定的制度是推动历史沿着某条道路前进的重要因素。 * 社会学制度主义:将特定社会的文化结构也看成是既定的社会制度,认为个体的政治行为不是自身的偏好,而是受到既定社会制度的制约。特定制度的采纳不一定是效率问题,更大程度上是体现了既定文化下的合法性问题。合法性与社会适应性是其研究重点。 == 学科分支 == * 比較政治:以各種方法研究不同的政府形式間的差異。以美國和加拿大為例,還會包括對於當地區域的研究;也就是對於特定州、省或" +134,"阿倫特: On Violence 1970, Harvest Book * Crick, Bernard: In Defence of Politics , University of Chicago Press; 4th edition (1993年10月25日) * 奧托·馮·俾斯麥: remark, 1867年8月11日 * Dickerson: Mark O. & Flanagan; Thomas, 1990 Introduction to Government & Politics: A Conceptual Approach, 3rd edition * Gompers: Samuel; Men of Labor! Be Up and Doing, editorial, American Federationist, 1906年5月, 第319頁 * 吉曼·基爾: Sexual Politics * 馬克斯·韋伯: Economy and Society: An Outline of Interpretive Sociology, New York, Bedminster" +135,"法學(jurisprudence、legal theory),法律学、法律科学,是社會科學中的1門學科,研究法律此一特定社會現象、其本质与規律。所有的秩序都可以說是種「法律」,如自然規律、倫常、邏輯法則、美學,而法學就是研究法律的法則。雖然許多法學家,如、,認定法學沒有學術性質,然而法學演進至今仍包含有法律哲學、法律史學、法律科學等3大部門。此外,又因為研究對象都是社會上的現象,因此法學、政治學、經濟學、財政學、社會學、心理學等社會科學有相當程度的密切關連。法學思想最早淵源於東周時期的法家哲學思想,如管子主張:「法者,所以興功懼暴也;律者,所以定分止爭也」。“法学”一词最早见之于南齐孔稚珪《请置律学助教表》“寻古之名流,多有法学”。不过,其含义不同于近代之“法学”。在歐美地區的傳統中,古羅馬法學家烏爾比安對法學一詞的定義是:「人事、神事的概念,正義、非正義之學」,荷蘭法學家格勞秀斯認為法學是人類研究遵從正義而生活的學科。文藝復興時期,法學於大學中備受重視,為當時學術界最具地位的科系之一,與醫學、神學三足鼎立。在歷史演進過程中先有習俗,然後由倫理分離出道德,最後才有法律,但是相對於習俗、" +136,"古今之平。」可以說是中華傳統中對於法律研究的清楚標準,也就是法古字「灋」在《說文》中的解釋:「刑也。平之如水,故从水。廌所以觸不直者去之,故从廌从去。」 ==法的概念== 人的行為中都有某種應然的法則,而規範人類意志和行為的倫理法則可以區分為道德、習俗以及法律。前述法律是三者之中唯一具有下列四項特性的法則,外在關心取向、外在判斷方式、外在目的主體以及外在效力來源。也就是說,法律關心行為所產生的結果而不是內心的意圖,只要行為符合法律條文就屬於合法行為,而法律領域中有相對權利義務的主體,並且有立法者與仲裁者。德國著名法學家古斯塔夫·拉德布魯赫認為,法律具有三大價值:公共利益、法明確性以及正義,然而因為人的不完美這三大價值並不總是和諧共存於每部法律之中。 從道德、習俗、法律、國家、科學、藝術和宗教這些文化內容中,依照不同的學派曾有三大體系來討論法律的目的,造就法治國家、文化國家、以及威權國家等不同型態的法律內容。首先是十八世紀末,康德、席勒、洪博特等人認為自由的倫理人格是最高的價值,而藝術和科學只是人格教養的工具,法律和國家只是它的保護機制,於是法律僅僅具有個人主義倫理的從屬價值,而沒有自身" +137,"事務的積極面外,還有國家保證不能觸犯個人自由的消極面。 警察國家不知道國家權力的界限,他們不僅是要保護人民免於他人的侵犯,還要免於自身無知的危害,因而犧牲人民的自由以保障其福祉。但當立憲國家默默地不再過問某些領域的事務時,便打破這種監護制度,這些領域是1789年的《人權宣言》後在憲法或其他法律中明白宣示不可侵犯的權利,如法律之前人人平等、個人自由、居住和通信隱私不受侵犯、信仰和文化自由、學術及理論自由、集會結社自由、出版自由、就業自由、遷徙自由等。形式上,憲法是國家根本大法,效力優於普通法律,其他一般法律均須根據憲法而制訂,不得與憲法抵觸,否則即失其效力。 ==經濟法與國家== 古典資本主義社會中,經濟法的本質是為了確保市民對經濟活動有自由放任的權利,特別堅持所有權自由、契約自由與人格平等之法律基本原理,法律主要內容是要求國家權利給予人民最大的經濟活動自由,並藉由市場自動調節機制,即所謂「看不見的手」,用以形成整體市場合理的經濟秩序,因此國家對經濟活動的介入實際上是採消極的態度。從一次世界大戰開始,立法者不再只將經濟關係視為雙方當事人之間的公平補償,而主要以經濟共同利益、生產及節約的觀" +138,"下的產物:個人性格和環境狀況。依據犯罪行為的主要動機可將犯罪行為人分成兩大類:機會犯人以及常習犯人。機會犯人的行為是由外在刺激或誘因所引起的,不具長期犯罪的傾向;常習犯人則想要犯罪,或基於習慣、天性而具有犯罪的本性。對於後者,經驗顯示很多都會辜負教育者的努力並再度犯罪,因此刑罰只能將他們除掉、永久或是長時間隔離,以確保社會安全。對於機會犯人則應表達明確訓斥,用以警告他們。因此,根據犯罪者分成三類,也可將刑罰目的分成三類:刑罰要警告時機犯罪者,要改善能改過的性格犯罪者,以及隔絕無可救藥的性格犯罪者。相對於一般教育,青少年罪犯應該要以教養為主。 == 法學與其他領域 == 現代的法學是指研究法律的科學,但是關於法學與科學的關係有不同的看法,這主要涉及價值論的研究是不是科學的問題。近代德國人把人文、社會科學提高到與自然科學對等的地位的,柏林洪堡大學以來的現代大學推動了社會科學的發展。法學被理解為科學與這一背景有關。法學以價值論為主要核心內容,如同丹麥法學家所說:「正義...不能作為法治的量尺,或法律的終極標準,主張某法不正義只不過是一種有害的情緒性字眼。」實質上,法學的核心僅僅是思維上的中心" +139,"象的學問,例如憲法學、民法學、刑法學、行政法學、商法學、國際法學等。也有以研究內容作為區分標準,分為法律哲學、法律史學與法律科學三種,法律哲學是研究法律的原理原則,探求法律的終極理想及其實用價值,各大學之法律科系,多設有法理學一科來探討法律哲學的範疇。法律史學,又稱為法律沿革學,主要研究法律的變遷過程及未來趨勢,現在法律科系多有法學史、法律學史或法制史等類學科,變屬於法律史學範疇。法律科學探討國家現行法律的共同原理原則,以及各種法律的概念、內容及適用範圍,其中又可分門別類研習,如憲法、行政法、民法、刑法與其他各種法律研究。 * 理論法學 ** 法理學 ** 法律哲學 ** 法律邏輯學 ** 法律社會學 ** 立法學 ** 法律心理學 ** 法律教育學 ** 法律史學 *** 法制史 *** 法律思想史 ** 法律解釋學 ** 比較法學 ** 外國法學 * 部門法學 ** 憲法學 ** 行政法學 ** 民事法學 *** 民法學 *** 經濟法學 *** 勞動法學 *** 環境法學 *** 婚姻法學 *** 智慧財產權法學 *** 金融法學 * 訴訟法學 ** 民事訴訟法學 ** 行政訴訟" +140,"訓兩個基本部分。 === 法學教育 === 法學教育之目的係在法學範疇上的知識培育,範圍包括各種法學理論及術科。有些人將法學教育分為法學本科教育和法學研究生教育,前者係指法學方面的第一學歷(法學士和法律博士),後者則為供法學本科畢業後繼續進修的法學教育,如法學碩士及法學博士。惟有些人則認為法律博士雖為法學方面的第一學歷,但卻是一個研究生學歷,因此認為分類應以基礎法學教育和進階法學教育為名。 === 法律職業培訓 === 法律職業培訓分為兩大類: * 資格前培訓,即向未取得法律業專業資格(如律師資格、檢察官資格等)的人,為取得法律頁專業資格而提供的教育,其目的是協助其取得資格及提供職業知識教育。例如香港的法律專業證書、英國的法律專業共同試和法律實踐課程等。 * 資格後培訓,即為已取得法律業專業資格者提供持續進修教育,其目的係為從業者不斷更新其專業知識。例如香港的執業律師便須要按持續專業進修的要求定期進修。 ==各地法律教育== ===中国大陆=== 清朝滅亡後,在民國初年,各式大學紛紛建立起來,當中包括不少法學院。其中,國立的法學院仍以前身為京師大學堂法律學門的北京大學法律系最為著名,而" +141,"國立成功大學法律學系暨研究所 * 國立中正大學法學院 * 國立中興大學法政學院 * 國立高雄大學法學院 * 東吳大學法學院 * 天主教輔仁大學法律學院 * 東海大學法律學院 *世新大學法律學院 僅提供研究所或大學部的法學教育: * 國立中央大學法律與政府研究所 * 國立清華大學科技法律研究所 * 國立東華大學法律學系 在臺灣,大學院校法律有關系所畢業者,具參加司法官(法官、檢察官)、律師考試的資格,因此法律學系成為三個類組中較熱門的科系之一,在各大學第一類組的科系裡(包括政治人文藝術設計、商管會計、其他社會科學等相關學科)錄取分數往往是最高的。近年前臺灣曾考慮修法比照國外,將參加司法官(法官、檢察官)考試的應試資格提高到必須具備法律專業研究所畢業才得以報考,若假以實施勢必影響高中升大學法律有關學系錄取的分數,至今仍未更動。 ===香港=== 在香港,於香港大學法律學院、香港中文大學法律學院、香港城市大學法律學院等三所大學均有開辦法律教育。 ===澳門=== 在澳門,於澳門大學法學院、澳門科技大學法學院二所大學有開辦法律教育。 ===美国=== 美国的法学院提供三种学位的法学教育:法律博" +142,"社會學()起源於19世紀末期,是一門研究社會的學科。社會學使用各種研究方法進行實證調查和批判分析,以發展及完善一套有關人類社會結構、社會行動或社會關係的知識體系,並會運用這些知識,以尋求或改善社會福利、深入社會與公眾進行對話與溝通、將人類從不平等及壓迫的物質條件下解放出來,等等為目標。 社會學的研究範圍廣泛,包括了由微觀社會學層級的機構或人際互動,至宏觀社會學層級的社會系統或結構,社會學的本體有社會中的個人、社會結構、社會變遷、社會問題和社會控制,因此社會學通常跟財政學、經濟學、法律學、政治學、人類學等學科並列於社會科學領域之下。社會學在研究題材上或研究法則上均有相當的廣泛性,其傳統研究對象包括了社會階層、社會階級、社會流動、社會宗教、社會法律、越軌行為等,而採取的模式則包括定性和定量的研究方法。由於人類活動的所有領域都是由社會結構、個體機構的影響下塑造而成,所以隨著社會發展,社會學進一步擴大其研究重點至其他相關科目,例如衛生、醫療、藥理、護理、軍事或民事、刑事制度、網際網路等,甚至是例如科學知識發展在社會活動中的作用一類的課題。另一方面,社會科學方法的範圍也越來越廣泛。在20世紀中葉" +143,"3。社會學就是針對社會改組的問題(現代性)而發展成為一門社會科學。涂爾幹學派的社会学家不但希望了解什么使得社会团体聚集起来,更希望了解社会瓦解的发展过程,從而作出“糾正”。糾正的觀念並不暗示社會學家預設一個「正確社會」的模板。涂爾幹本人都不認為有一個終極的、完美的社會可以參考。簡單來說,當一群人互動形成一個穩定的參考架構,運行良久,對他們來說就是「常態」。當社會制度問題積累下來,此一穩定狀態被打破,形成混亂,即上述所謂「瓦解」時,就形成病態。因此涂爾幹認為,社會學家職責在於找出一個社會的「日常常態」,如此才能確定出「病態」的判斷標準。社會學家需要不斷觀察與分析,不是找出模板,宣稱終極事實就結束。 相反地,法兰克福学派的社會學家並不探索對糾正社會的方法。因為他們認為對社會病疾提出的糾正方案,往往是以一個小群體的觀念強加到絕大多數人的身上,這不但解決不了問題,還會使問題加重。20世紀初,如霍克海默與阿多諾等人,受到上一代如韋伯與齊美爾學說影響,加之參考馬克思理論,針對現代社會與資本主義制度產生悲觀看法,其中尤其看到社會雖然高度分工,卻也產生生活的分裂化。尤其是在藝術文化領域,大量複製技術的" +144,"资料的方法,以及基于扎根理论、内容分析等定性资料的分析及歸納方法。从事定性分析的社会学家相信,这是一种更好的方法,因為这可以加強理解“离散”性的社会和独特性的人文。这种方法从不寻求有一致观点,但却可以互相欣赏各自所采取的独特方式并互相借鉴。主流的观点认为,定量和定性这两种研究方式是互补的,而不是矛盾的。涂爾幹與韋伯都強調兩者的重要性──亦即從主觀(質化)與客觀(量化)交錯研究,到後來的象徵互動派大師布魯默相當強調質化的影響力。 == 社會學的角度 == 社會學的角度就是讓我們通過社會學看事物的方法去認識自己及分析社會。其中兩種比較著名的社會學角度就是「社會學的想像力」(Sociological Imagination)及「破解謎思」(Debunking)。 「社會學的想像力」由賴特·米爾斯所創,其主要意思是指我們可以透過「社會學的想像力」去了解社會。 至於「破解謎思」是彼得·柏格的概念。他指出了「社會學的意識中是存在著一種揭穿真相、破解謎思的動機」。這句話指出了社會學家是關注所有的人類活動,但他們關注的層面並不是一般人所關注的事實之表面,而是以一個質疑的心態去思考及破解一件事情的謎思。" +145,"ontributions of Early Muslim Scholars and Challenges to Contemporary Muslim Psychologists , Journal of Religion and Health 43 (4): 357–377 [375].。到了十七世纪,以笛卡儿为代表的理性主义者即注意到科学拥有发现真理的能力,而开始尝试运用科学方法来研究社会现象。 社会学一词的英语“sociology”(或“sociologie”),是由拉丁语“socius”(同伴)、“-ology”(研究),和希腊语“λόγος”(即“lógos”,意指字词、知识)组合而成,这个名称最早由法国散文家埃马纽埃尔-约瑟夫·西哀士(1748年—1836年)在一份当时未公开的手稿中创造Des Manuscrits de Sieyès. 1773–1799, Volumes I and II, published by Christine Fauré, Jacques Guilhaumou, Jacques Vallier et Françoise Weil, Paris," +146,"治思想大革命,另一次是从英国开始、一直扩展到欧洲和美国的工业革命,这两件重大事件使西方社会发生了根本性转型。资本主义确立后,一方面生产力得到突飞猛进的发展,令社会的经济基础、思想观念和政治制度都发生重大变化;另一方面,社会运行也暴露出许多不协调因素,如周期性经济危机、失业、犯罪等。对于这些问题,传统方法和原有科学体系无法作出满意的解释和回答,于是社会学作为一门不同于其他社会科学的独立新学科便应运而生。 孔恩德所身处的年代适逢法国大革命之后的持续动荡,他认为社会上的各种弊病和问题可以透过社会学实证主义消除,这是在其著作《实证哲学教程》、《实证主义概论》(A General View of Positivism)中明确提出的一个认识论模式。他相信在人类知识发展“三阶段律”(Law of three stages)中,在继神学阶段和形而上学阶段(或称玄学阶段)之后,实证阶段将占据最终的位置,也是他最重视的部分。孔德認為過去神學主导及崇尚武力的社會会慢慢消失,以理性、科學、工業為主的年代正在抬頭,兩者之間的衝突正是社會動盪的源頭。在社會制度轉變的時期,這種過渡性的衝突及混亂是無可避免的。在舊制" +147,"马克思又否定了以孔德为首的社会学实证主义。在孔德提出社会学构想的同时,马克思在《1844年经济学哲学手稿》中已经形成了新的唯物主义历史观。马克思认为人与自然界的关系是以劳动为中介的,他从经济学上分析了在生产资料私有制度下,人的劳动本性异化的表现,提出了劳动的解放将标志着人性的复归和社会的人性化,从而使对资本主义的经济学批判转入到在社会学上关于资本主义对人限定的地位的批判。马克思唯物史观社会学的产生,使社会学在本质上变成为批判的、革命的科学,与孔德强调秩序、均衡,目的在于维护和改良现存制度的实证主义社会学形成鲜明的对立。这也是马克思本人一直拒绝把自己的社会理论称为“社会学”的基本原因,直至二十世纪初列宁才明确把马克思的社会理论称之为马克思主义社会学,使之为早期社会学的重要支柱之一。二十世纪著名哲学家以赛亚·伯林也认为,马克思可以被视为“真正的现代社会学之父”。 === 形成学术学科 === 十九世纪末期开始,社会学开始蓬勃发展,归功于法国社会学家爱米尔·涂尔干出版大量社会学研究专著,为社会学的规范化和科学化奠定坚实基础,并为确立社会学的学科地位作出重大贡献。他一生致力推动社会学发展,将源" +148,"尼斯和马克斯·韦伯共同成立,随后,韦伯又在1919年于德国慕尼黑大学设立德国首个社会学系,并首次提出了反实证主义社会学的理念。在1920年于波兰创立波兰首个社会学系。1923年,法兰克福大学设立了社会研究院(Institut für Sozialforschung)。 与此同时,美国在社会学方面也有长足的发展。美国社会学家威廉·格雷厄姆·萨姆纳于1875年在耶鲁大学开始教授名为“社会学”的课程。1890年2月,法兰克·威尔森·布莱克默(Frank Wilson Blackmar)在美国堪萨斯大学设立“社会学元素”(Elements of Sociology)课程,并延续至今。1892年,芝加哥学派的代表人物——阿尔比昂·伍德伯里·斯莫尔(Albion Woodbury Small),在芝加哥大学创立社会学系。社会心理学始创者之一的喬治·賀伯特·米德,于密歇根大学任教时遇到查理斯·霍顿·库利和约翰·杜威;米德和杜威两人在1894年转往芝加哥大学任教,这使得社会心理学、象徵互動論(symbolic interactionism,或称符號相互作用論)获得极大的发展。1895年,阿尔比昂·W·" +149,"7年设立的沪东公社则是中国第一个大学社会学实验基地,被视为中国社会工作服务的前身。 第一次关于社会学的国际合作发生于1893年。法国政府国会核数师创刊国际社会学杂志(Revue Internationale de Sociologie)并担当其主编,随后又成立“国际社会学图书馆”(Bibliothèque Sociologique Internationale)、“巴黎社会学学会”(Société de Sociologie de Paris)、“国际社会学研究所”(Institut International de Sociologie ),其中国际社会学研究所后来与创立于1949年的国际社会学协会合并。 总体而言,社会学形成学术规模的成长期大约处于19世纪末到20世纪30年代。社会学的发展历程可以被看作为对例如工业化、城市化、世俗化等现代性问题的积极回应,或是对社会理性化(rationalization)的理解和反思。在社会学创始初期这段时期期间,社会学领域在欧洲大陆占据主导地位,并与英国的人类学和统计学遵循着类似的发展轨迹、举头并进;但踏入二十世纪以后情况开始逆转,越来越多的社会" +150,",观点分歧 * 分科化,社会学与其他社会科学的渗透加强,分科社会学种类增多 * 方法科学化,计算机的广泛运用,大大加快了资料处理速度,使社会学的研究更准确、快速、规范 == 社会学理论 == 社会学理论经过一百多年的发展,尽管存在着缺乏完整的理论构架和很多彼此矛盾的方面,但已经成为成熟丰富且脉络清晰的理论体系,其中很多理论都有着鲜明的实践指向并在现实生活中发挥了有益的作用。也有很多理论虽然现在还很抽象概要,但也不乏启发性的地方。依照发展程度和时间顺序,社会学理论一般分作古典社会学理论、现代社会学理论和当代社会学理论。 古典社会学理论很大程度上是一些著名社会学家的理论,也就是说这一时期的社会学理论还缺乏完备的理论流派,单个社会学家的理论就能代表这一流派的特征。所以,人们在阐述这一阶段的理论的时候,多是介绍某几个社会学家的理论成果。不过这样的弊端也显而易见,单个社会学家理论成果对整个理论体系的代表在一些情况下过于勉强。所以,通过对其特征的归类和总结,将其归纳为以下四个理论框架。这样一来在对这一时期的理论有较为明晰的认识之后,又能与下一阶段的流派分类衔接。但要对各个理论有深入的了解,还是要回" +151,"作用。他将社会行为的理解置于对个人和主体间的意义和动机的理解。韦伯的理解社会学,齐美尔的形式社会学以及随后一系列学者构建的符号互动论,虽然看上去彼此差异很大,但质性方法论和对能动性的承认将其联系到了一起。 经过古典阶段的积淀,社会学理论摆脱了对哲学和其他社会科学的依赖,真正走上了特色鲜明的学科发展之路。现代社会学理论在取得重大发展的同时,其中的古典社会学理论影子依然清晰可见,这也说明二者的延续性和承袭性。事实上,现代与古典的阶段划分并非是界限分明的,二者除代表性人物和理论流派之外,都存在不少的交集。社会学理论在古典时期的四个理论框架,在此有不断的丰富和发展,随着特征差异增大,这些流派又不断分化,形成新的框架体系。 社會学理論當中常常用一些抽象和甚至複雜理論框架來解釋和分析社會樣式和宏觀社會結構。社會学理論總跟經典的學科有一個令人不安的關係,就是大部分的重要社會學家從未擔任大學職務。現今社會理論被認為社會學分支,涉足多個科學區域譬如人類學、經濟、神學、歷史等等。第一種社會理論幾乎跟社會學同時誕生。社會學之父——孔德創立第一社會理論——社會演化理論。在19世紀,三大社會理論分別是社會進化論" +152,"于整体的调配,社会是一个分工的和不平衡的统一体。圣贤(哲学王)则是居于社会最顶端掌控国家机器的最佳人选,因为他有足够多的经验和知识,能够超越具体部分的利益,实现整个社会的最大福祉。在柏拉图看来,私有财产和家庭这样分散且独立的因素应该为社会整体的发展发挥自己的作用。亚里士多德与此相反。在《政治学》中一系列对人类社会和国家的看法虽然还较为混沌,但其中的社会学思想却是显而易见的,他就认为社会各个部分的独立要素在构成社会整体的同时,也能保持各自的独立性。他指出,社会起源于人的社会本性,即人有集合为群体的本能,但复杂、且异常分化的社会并非由个人组成的,它是建立在一个个群体的基础上,这样有相应的功能和财产的共同体最终组成了社会。侯钧生:西方社会学理论教程.南开大学出版社.p3-7.2006 ==== 启蒙思想 ==== 启蒙思想对于西方文明的影响不言而喻,诸多社会科学在一次次的思想解放和突破中诞生,对社会转型产生的社会问题的关注,很大程度上也助推了整个启蒙思想的发展。 社会契约论是这一时期最重要的社会思想,诞生于与教权和王权思想斗争中,由此发展出来的一种契约义务和社会关系结构的世界观,成为社会学起" +153,"。历史社会学研究属于定性社会研究方法。同样的,社会学与人类学学科的交融催生越来越多社会学家运用民族誌的方法对现代社会进行研究。 不同的方法所面對的困難是它們都根據研究員各自採納的理論基礎來解釋及了解社會。作為功能主義者,艾彌爾·涂爾幹喜歡以社會大規模結構來解釋任何東西。如果他是符號互動者,他便專注人們如何理解別人。馬克思主義者或新馬克思主義者把什麼都化作階級鬥爭。現象學家只是思考大眾對現實如何建立自己的意義。當各方都面對現實社會問題時,常常爭論誰對誰錯,而實際上會把不同方法學結合。 一般觀點將社會學區分為宏觀或微觀兩類,認為分析單位如國家發展,就屬於前者;分析個人精神與家庭等,較屬於後者。這是相當僵化的解決看法。事實上,所謂微觀並非依照研究單位的規模看,微觀較屬於處理社會學基本原型的東西,更觀察在不同單位之間的互動,事實上,在企業之間、乃至於國家之間,也可以視為是一個個個體,研究他們之間的互動關係,以微觀方式處理之,諸如用象徵互動論、現象學方法等。 近年來,網際網路成為新的社會學的研究旨趣,如陳德維所言: #它是研究工具。例如網上問卷調查代替紙張問卷。 #它成為討論平臺。 #它本身是研" +154,"2012. Intersectionality and Global Gender Inequality Gender & Society 26(1):67-72. 今天,社會學研究人類組織、社會群體、社會互動等等都使用大量比較方法。社會學的發展對其它科學帶來額外的需求,這對研究現代工業社會是十分重要。近年,得到了人類學的啟示,加速了對多元文化及多元國民的研究。 另一方面,社會學朝著微社會結構發展,例如:種族、社會階級、性別及家庭。因為有很多罪案發生、家庭問題產生了很大的社會壓力,急需要社會學來尋找解決方法。 社會學家透過定性研究和定量研究來研究社會關係以預測社會變動。他們希望透過質性研究,如面談及小組討論,對社會運作有更深入的理解。有些社會學家正辯論着從中作出平衡填補兩者之間的空隙。例如:定量研究描述大型社會現象,而定性研究描述個人如何理解大型社會現象。在二十世紀早期,社會學家及心理學家曾對工業社會作出研究,對人類學作出了貢獻。要留意一點的是人類學家都曾對工業社會作出研究。今天社會學及人類學主要分別在於研究不同的理論和方法而不是對象。 社會生物學是綜合社會學及生物學的一門新科學。雖然" +155,"= * 历史社会学派人类社会历史发展的基本轮廓 * 社会统计学派 * 芝加哥学派 * 法蘭克福學派 == 社会学次領域 == * 社会学史 * 社會科學研究方法 ** 社会调查、社会统计学、紮根理論、俗民方法論、民族誌、田野工作、內容分析、歷史研究法 * 比较社会学 * 知识社会学 * 教育社会学 * 政治社會學 ** 社會運動 * 福利社會學 ** 社会保障、医学社会学、犯罪社会学、越轨社会学、家庭社會學 * 性別社會學 ** 女性主義、父權、酷兒理論、男性研究 * 经济社会学 ** 管理社会学、发展研究、工业社会学、消費社會學、环境社会学、都市社会学 * 文化社会学 ** 艺术社会学、传播社会学、体育社会学、音樂社會學 * 历史社会学 ** 種族社會學、宗教社会学、國家理論 * 社會階層 ** 社會流動、社會網絡、社會組織 * 人口学 ** 人口史、人口地理学、人口生态学 * 科技與社會 * 資訊社會學 == 注释 == == 参考文献 == === 引用 === === 来源 === ; 中文書籍 * * [德]韋伯. 《社會學的基本概念》, 廣西師範大學出版社, 2005年," +156,"Cambridge, 1998. (對古典社會學作很好的介紹) * Anthony Giddens, Sociology, Polity, Cambridge * Anthony Giddens, Human Societies: Introduction Reading in Sociology * Robert A. Nisbet, The Sociological Tradition, London, Heinemann Educational Books, 1967, ISBN 978-1-56000-667-1 * Evan Willis, The Sociological Quest: An introduction to the study of social life, 3rd edn, New Brunswick, NJ, Rutgers University Press, 1996, ISBN 978-0-8135-2367-5 * Andrey , Artemy Malkov, and Daria Khaltourina, Introduction to Soci" +157,"Britain: Science, Literature, and Society, Oxford University Press 2004 * Barbara Laslett (editor), (editor), Feminist Sociology: Life Histories of a Movement, Rutgers University Press 1997 * Levine, Donald N. Visions of the Sociological Tradition, University Of Chicago Press, 1995: ISBN 978-0-226-47547-9 * T.N. Madan, Pathways : approaches to the study of society in India. New Delhi: Oxford University Press, 1994 * Wiggershaus, Rolf, The Frankfurt School : its history, theories and political si" +158,"istorischen Identität einer Disziplin, 4 Bände, Frankfurt am Main (Suhrkamp) 1981, ISBN 978-3-518-07967-6 * Heinz Maus, Geschichte der Soziologie; in: Werner Ziegenfuß (Hg.), Handbuch der Soziologie. Stuttgart: Enke, 1956: 1-120 * ders., Bericht über die Soziologie in Deutschland 1933-1945; in: , 11 (1959) 1: 72-99 * ders., Zur Vorgeschichte der empirischen Sozialforschung; in: (Hg.), Handbuch der empirischen Sozialforschung Bd. I, Stuttgart: Enke, ²1967: 18-37 * Reinhard Müller, Marienthal. Das" +159,"雷蒙·阿隆, Les étapes de la pensée sociologique, Paris, Gallimard, 1976 * , La construction de la sociologie, Paris, PUF, 1991 * Pierre-Jean Simon, Histoire de la sociologie. Paris : PUF, pp. 7–24, 1991 * Charles-Henry Cuin, François Gresle, Histoire de la sociologie, tome 1 : avant 1918, tome 2 : depuis 1918, Nouvelle édition, Éditions La découverte, Collection Repères * Gilles Ferréol & alii, Histoire de la pensée sociologique, Colin Cursus, 1994 * Jean Étienne, Henri Mendras, Les grands auteurs e" +160,"ologia, Znak, 2002, ISBN 978-83-240-0218-4 == 研究書目 == * 李明堃、黃紹倫 編:《社會學新論》(香港:商務印書館,1992年). * 胡偉:《戰後法國社會學的發展》(香港:三聯書店【香港】有限公司,1988年). * 清水幾太郎 (1978年) 『オーギュスト・コント――社会学とは何か』(岩波書店[岩波新書], 1978年). * 富永健一 (2008年) 『思想としての社会学』(新曜社, 2008年). == 外部链接 == ; 台灣資料來源 * 台灣社會學會 ** 台灣社會學刊 * 台灣中央研究院社會學研究所 ; 中國大陆資料來源 * 社会学视野网 * 中國社會學網 * 社会学人类学中国网 * 天睿网(原社会学吧网) * 社会学博客网 * 《中国社会科学》 * 《社会学研究》 ; 自學課程 * 社會學入門的免費講義,加拿大特倫特大學 * 社會學課程入門的免費講義, 東卡羅萊納大學 ; 其他資料來源 * 美國宗教數據檔案 * 美國社會學協會 * 國際社會學協會 * 社會研究的方法 * SocioSite - 社會科學系訊系統 * 社" +161,"军事学与甚多範疇有關,主要與战争有关。此外,軍事學本身包含了各種學問。军事是政治的一部分,战争是政治的一种延续,是一国或者集团用暴力手段达到自己目标和目的的方式,而目标和目的往往与利益有关。战争是军事的集中体现,但不是唯一的体现。第二次世界大战(1939年-1945年)后的美国和苏联冷战,就是一种威慑基础上的回避战争方式的斗争。在人类可以看到的未来,军事始终是政治生活中重要的方面,并在科学技术上对人类生活予重大影响:人类很多科技成就往往先产生于军事领域然后普及到非军事领域的。 == 古代军事学 == 孙子兵法 == 現代軍事學體系 == 現代軍事包括軍事理論和軍事科技兩大部分。 *軍事理論:军队的建设目的是存在一支能击败任何敌人的综合力量。它明确军事思想集成·装备需求·作战训练的方向,从而根据时代要求展开各项工作来培养军队以满足作战需要。它武装官兵思想,增强作战信念和效能;它整合现有力量,增强针对性和攻击性;它明确军队建设方向,增强军队建设的迫切性和竞争性。 ** 軍事思想 **戰爭 ** 武裝力量 ** 軍隊 ** 軍事家 **戰略學 ** 軍事制度 ** 軍隊政治工作學 ** 軍事" +162,"信息科学(Information science),旧称情报学(漢語中的日語借詞),主要是指以信息为研究对象,包含資訊的分析、收集、分類、處理、儲存、檢索、傳播和保護。從發展脈絡來看,信息科學與計算機科學、心理學、科技和情報機構有相當的關聯,但是信息科學還包括了檔案學、圖書資訊學、博物館學、認知科學、商業、法律、語言學、管理學、數學、哲學、公共政策和社會科學等各個領域,是以扩展人类的信息功能为主要目标的一门综合性学科。 ==研究内容== *資訊存取 *資訊架構 *竞争情报 *知识管理 *資訊管理 *資訊檢索 *資訊尋求 *資訊社會 *知識表示 *知識本體論 *理论信息学 *資訊科技 *資訊系统和资源管理 *資訊哲學 == 理论基础 == 作为一门新型的综合性学科,信息学的理论主要是建立在数学中的离散数学之上的。因为信息学所研究的对象信息本身即是离散体。在某些特定的条件下,信息学与计算机科学是等价的。 == 参考文献 ==" +163,"物理學(源自,源自,转写:phýsis,直譯:大自然), , 是研究物質、能量的本質與性質的自然科學。由於物質與能量是所有科學研究的必須涉及的基本要素,所以物理學是自然科學中最基礎的學科之一。物理學是一種實驗科學,物理學者從觀測與分析大自然的各種基於物質與能量的現象來找出其中的模式。這些模式稱為「物理理論」,經得起實驗檢驗的常用物理理論稱為物理定律,直到有一天被證明是有錯誤為止(具可否證性)。物理學是由這些定律精緻地建構而成。物理學是自然科學中最基礎的學科之一。化學、生物學、考古學等等科學學術領域的理論都是建構於這些物理定律。 物理學是最古老的學術之一。物理學、化學、生物學等等原本都歸屬於自然哲學的範疇,直到十七世紀至十九世紀期間,才漸漸地從自然哲學中分別成長為獨立的學術領域。物理學與其它很多跨領域研究有相當的交集,如量子化學、生物物理學等等。物理學的疆界並不是固定不變的,物理學裡的創始突破時常可以用來解釋這些跨領域研究的基礎機制,有時還會開啟嶄新的跨領域研究。 通過創建新理論與發展新科技,物理學對於人類文明有極為顯著的貢獻。例如,由於電磁學的快速發展,電燈、電動機、家用電器等新產品纷" +164,"中国战国哲学家名家惠施、邓析和公孙龙,以及墨家,曾努力钻研宇宙间万物构成的原因。惠施有十个命题,主要是对自然界的分析,其中有些含有辩证的元素。他说:“至大无外,谓之大一;至小无内,谓之小一。”。“大一”是指整个空间大到无所不包,不再有外部;“小一”是指物质最小的单位,小到不可再分割,不再有内部。名家的思想合同异以惠施为代表,认为“天与地卑,山与泽平”,万物“毕异”本为“毕同”,并无区别。后期墨家认为物质世界是由微小的不可再分割的物质粒子所构成。 ===古希臘物理學=== 从古代以來,人们就尝试着了解大自然的奥妙:为什么物体会往地面掉落,为什么不同的物质会具有不同的性质?如此等等。從觀測與分析大自然的現象,早期人們找到其中的樣式,並針對這些樣式提出了各种理论,试图解释大自然的奥妙,然而他們所提出的大多數理論都不正确。以現代準則來看,早期的物理理论更像是一些哲学理论:现代的理论都需要经过嚴格的实验檢驗,而那些早期的理论并没有经过严格证实。像托勒密和亞里士多德提出的理論中,有些就与日常所能观察到的事实相悖。 儘管如此,仍有許多古學者貢獻出相當正確的理論。古希臘哲學家泰勒斯(前624年-前54" +165,"明顯地,在這裡,他所指的是針孔相機或暗箱。他還描述怎樣用暗室來觀測日蝕。 海什木的成就在阿拉伯世界並沒有得到應有的重視。十二世紀,他的著作被翻譯成拉丁文,書名為《透視》(Perspectiva)。直至十七世紀,這著作在歐洲是光學的標準參考書,強烈影響了後來约翰内斯·开普勒、威特羅、羅傑·培根等等科學家的研究。 ===經典物理學=== 經典物理學指的是不涉及到量子力學或相對論的物理學,例如,牛頓力學、熱力學、馬克士威電磁學等等。經典物理學的盛期開始於十六世紀的第一次科學革命,終止於十九世紀末。尼古拉·哥白尼打響了科學革命的第一槍,他於1543年提出了描述太陽系統的日心說,這理論推翻了托勒密的地心說。在1609年與1619年期間,約翰內斯·克卜勒發表了主導行星運動的定律,他用數學方程準確估算出從天文觀測獲得的行星繞著太陽的公轉數據,從而給予日心說強而有力的理論支持。伽利略·伽利萊做實驗研究物體運動,發現落體定律,並且展示出實驗方法對於科學研究的重要性。他倚賴使用實驗或觀測所獲得的證據,而不是倚靠純粹推理,來證實任何假說的正確性。他強調使用數學來描述物理現象,大自然的語言是數學,假若不懂數學" +166,"理論皆已通過很多不同實驗的多次檢驗,並且對於自然現象的預測被認為足夠準確,例如,經典力學的理論能夠準確地描述物體的運動,但必須滿足兩個前提,一是物體尺寸超大於原子、二是物體運動速度超小於光速。當今,這些核心理論仍舊是很熱門的研究領域。例如,二十世紀後半期,即在牛頓(1642年–1727年)表述經典力學整整三個世紀之後,學者發現與創建了混沌理論,其揭示了力學系統的決定論可預測性是一個錯誤的觀念。 這些核心理論大致包括於經典力學、量子力學、熱力學、統計力學、電磁學、狹義相對論等等基礎物理學領域,是進階研究專門論題的重要工具。 === 經典物理學 === 經典物理學包括那些在二十世紀初已成熟的傳統學術分支領域:經典力學、聲學、光學、熱力學、電磁學等等。 經典力學研究受力物體的運動狀況。牛頓定律是經典力學的基礎定律。經典力學分為靜力學、運動學和動力學。靜力學論述處於靜力平衡的物體所感受到力與力矩。運動學描述物體的運動,完全不考慮力或質量等等影響運動的因素。動力學研究改變物體運動的因素與物體運動如何因此改變。按照表述方式的不同,經典力學又可分為向量力學與分析力學。向量力學著重於論述位移、速度、加" +167,"性質給出微觀層級的詮釋。在大尺度的實驗中可以測量到這些宏觀性質。 電磁學描述帶電粒子與電場、磁場的交互作用。電磁學的分支有靜電學、靜磁學、電動力學等等。靜電學研究靜止帶電粒子彼此之間的交互作用。靜磁學研究所有涉及常定磁場的現象。電動力學研究所有涉及加速度帶電粒子、電磁輻射、時變電場與時變磁場的現象。經典電磁學的基礎理論是馬克士威方程式與勞侖茲力方程式。光波是电磁波的一種,可由帶電粒子的加速度運動產生。 === 現代物理學 === 經典物理學通常用以闡述日常可觀察尺寸的系統現象,而現代物理學通常用以闡述極端或非常大尺寸、非常小尺寸的系統現象。例如,化學元素可以被辨識的最小尺寸是原子物理學或核子物理學探索物質所操作的尺寸。而粒子物理學操作的尺寸則更為微小,它論述的是基本粒子或由基本粒子組成的粒子。由於使用大型粒子加速器來產生基本粒子需要非常巨大的能量,所以通常粒子物理學又稱為高能量物理學。對於粒子物理學所研究的物理系統,那些關於空間、時間、物質、能量的普通常識不再適用,必須加以修改。 現代物理學的兩種核心理論給出關於空間、時間、物質、能量的崭新繪景。量子力學論述發生於原子層級與亞原子層級各" +168,"合併為電弱交互作用。物理學者期望在不久的未來,電磁交互作用、強交互作用與弱交互作用能夠被收斂在大一統理論的論述內。廣義相對論將時空延伸為動態的彎曲時空,能夠描述大質量系統和宇宙的大尺寸結構。但是,廣義相對論與其它種基礎相互作用表述尚未能被統一為單一理論;科學家仍舊在發展幾種可能的量子引力理論。 == 與其它學術領域之間的關係== === 物理與數學相輔相成 === 數學是研讀物理必備的工具之一,這包括幾何、代數、微積分等等。應用這些數學工具,物理學者可以從物理定律推導與演算出很多有意思的結果。例如,1912年,圖利奧·勒維奇維塔獲知阿爾伯特·愛因斯坦在探索重力的相對性理論中,遇到一些挫折,他便力勸愛因斯坦學習張量微積分。愛因斯坦採納了勒維奇維塔的建議,勤學張量微積分,并於1915年成功創立了廣義相對論。如同大多數英國的理論物理學者,羅傑·潘洛斯讀大學時專修數學,因此有深厚的數學造詣,能夠將拓撲學方法引入相對論研究,證明在每一個黑洞的中心存在著一個奇點,這就是在宇宙學裏著名的奇性定理。 數學在物理學裏的主要角色並不是推導與演算的優良工具,它還扮演了一個更關鍵的角色:作为一種抽象語言,担当" +169,"內含的數學概念。例如,根據量子力學的數學表述,在量子力學裏,有兩個基礎概念:物理系統的量子態是以希爾伯特空間的單位向量來代表,從觀察物理系統得到的可觀察量是以作用於這些向量的厄米算符來代表。一旦找到了這兩個基礎物理概念的對應數學概念,整個線性代數的理論都可以立刻應用於量子力學。這凸顯出數學的重要性與適應性。 在數學理論裏瀰漫著數學語言,其伴隨的數學概念往往會指出前進的道路,有時甚至會衍生出經驗預測。這並不只是巧合,而恰恰反映出在數學與物理之間無比深奧的關係。例如,1915年,广义相对论最初创立之时,尚没有甚麼牢靠的经验性观测基础,它在当时所能解释的最著名现象就是牛顿力学无法解释的水星近日点的反常进动。1919年天体物理学者亚瑟·爱丁顿爵士观测到了广义相对论预言的光线在太阳引力场中的偏折(这一实验直到1959年才开始被精确地定量测量),这在当时是对广义相对论最有力的支持。時至今日,广义相对论的理論預測已由實驗測量結果证实。 === 應用與影響 === 物理學是一门基礎科學,不是應用科學American Association for the Advancement of Science," +170,"間的相互關係,或物質和能量之間的關聯。傳統的化學常常都是關於兩種物質接觸、變化,即化學反應,又或者是一種物質變成另一種物質的過程。這些變化有時會需要使用電磁波,當中電磁波負責激發化學作用。不過有時化學都不一定要關於物質之間的反應。光譜學研究物質與光之間的關係,而這些關係並不涉及化學反應。准确的说,化学的研究范围是包括分子、电子、离子、原子、原子团在内的核-电子体系。 隨著量子物理的發展,化學也吸收了量子物理的理論建立了更完備的理論基礎。 物理學使用的一些探本溯源、格物致知的方法也可用於跨學科領域。物理學或多或少地影響了很多重要學術領域,例如,經濟物理學應用大量物理學裏的理論與方法來解析經濟學問題,這些問題時常會涉及不確定性或混沌。 == 學術研究 == === 科學方法 === 科學方法是一種用來解答問題的系統性程序,通過這種程序,可以發展出對於大自然現象的合理解釋。從觀察自然現象、閱讀書籍或討論中,時常會找到有意義的問題。假若問題過於複雜,則必須剝除其無關緊要部分,找到核心思想,將問題簡化,進而發展出能夠解釋這問題並且被實驗檢試的假說。經過實驗檢試後,或許需要對於假說加以改善或駁回。" +171,"演進方式已大幅度改變,在某些物理學分支領域,理論預測驅使了實驗物理的方向。通常而言,當實驗者發現一個新現象,而已知理論無法解釋這新現象時,或者當根据新理論所作出的預測,可以通過設計精緻實驗來檢驗時,持著大膽假設,小心求證的研究態度,物理學往往會有所進展。例如,在大型強子對撞機進行的各項研究完美地展示出理論物理學與實驗物理學的互助互補。由於先前理論物理學者預測希格斯玻色子存在,實驗物理學者才會堅持不斷地做實驗探索其蹤跡,在經過幾十年努力後,終於證實了希格斯玻色子確切存在。當今,理論物理學者正等待更多實踐數據來指示未來的理論研究方向。 實驗物理學者設計與完成實驗來檢試理論的預測與探索新的物理現象。實驗物理學者探索大自然奧秘的方式有兩種,一種是消極方式,如同天文觀測者的作法,因為無法操控宇宙星體的物理行為,另一種是積極方式,如同粒子實驗者的作法,操控粒子來展示其行為與性質。 實驗物理學擴展了工程學與科技,也被工程學與科技所擴展。涉及基礎研究的實驗者,在做實驗時,時常會接觸到像粒子加速器或激光一類的先進器材;而那些涉及應用研究的實驗者,時常會在工業就職,开發像正電子發射計算機斷層掃描、電晶體一" +172,"廣泛的自然現象,從微乎其微的基本粒子(像:夸克、微中子、電子)到龐大無比的超星系團都是研究對象。很多千變萬化的現象,都可用更基礎的現象來做合理的描述與解釋。物理學是一門基礎科學,物理學者致力於追根究底,發掘這些現象的根本原因,並試圖尋覓其中任何連結關係。物理學者努力研究所得到的結果大致可歸納為一些明確的基礎定律。其它許多學術領域,像生物學、化學、地質學、工程學等等,所涉及的物質系統都遵守這些基礎定律。但是,這些基礎定律仍不完全。物理學對於自然現象所給出的描述與解釋,只是近似事實,而不是絕對事實。 舉例而言,古希臘人知道像琥珀一類的物質,當與毛皮磨擦時,會出現吸引力,使得這兩種磨擦物互相吸引。這性質後來稱為電性。在十七世紀,學者開始慎密地研查這性質。另外,在亞洲大陸的那一端,古中國人觀測到某些石頭(磁石),會通過某種看不見的作用力互相吸引。見《鬼谷子》反應第二:「其察言也,不失若磁石之取鍼,舌之取燔骨。」用白話解釋,「察析這人的言詞話語,就好像用磁石吸取鐵針,又好像用舌尖探取炙肉中的骨頭,絕對不能有所差失。」這性質後來稱為磁性。也是在十七世紀,學者開始嚴格地窮究其起因。經過燃膏繼晷、廢寢" +173,"稱為高能物理學。 標準模型可以正確地描述基本粒子之間的交互作用。這模型能夠說明12種已知粒子(夸克和輕子),這些粒子彼此之間以強力、弱力、電磁力或引力進行交互作用。這些粒子會互相交換規範玻色子(分別為膠子、光子、W及Z玻色子)。標準模型還預測了希格斯玻色子的存在。2012年,歐洲核子研究組織宣布,探測到希格斯玻色子。 ==== 原子分子与光物理学 ==== 原子分子与光物理学專注於研究原子、分子與光,以及研究光与物质之間、物質与物质之間的相互作用。闡明物理的基礎定律、了解物質是怎樣在原子與分子層次組構而成、明白光與物質之間的相互作用、發展出新技術與新器件,這些是原子分子与光物理学的中心目標。原子分子与光物理学發展出的實驗與理論技術,時常會被應用於其它科學領域,例如,化學、天文物理學、生物學、醫藥學等等。對於很多其它科學領域,通過發展關於控制與操縱原子、分子與光的方法,或通過精確測量與分析它們的物理性質,或通過發展出新方法來製成具有某種特定性質的光,原子分子与光物理学扮演著賦能的角色。 原子物理學研究原子的結構與物理性質。原子物理學的研究主要分為三種趨勢。第一是研究自然基礎定律,這通常會" +174,"提下,凝聚態物理學對於分析與描述多粒子系統給出工作框架。凝聚態物理學專注於多粒子系統,凝聚態指的是由大量粒子組成,並且粒子與粒子之間存在很強的交互作用的系統。常見的凝聚態有固態和液態,由於原子與原子之間因電磁力而形成的化學鍵,才會出現這些物態。比較罕见的凝聚態包括發生於非常低溫系統裏的超流體和玻色-愛因斯坦凝聚態、在某些物質裏的傳導電子展現的超導態、 在某些磁性物質內部因為定域於原子晶格的自旋而出現的鐵磁態和反鐵磁態。 守恆定律與對稱性破缺主導了物質的宏觀性質,這是凝聚態物理學的一個重要概念。在由大量粒子組成的孤立系統裡,粒子數、能量、動量、電荷量等等都是守恆量。在足夠高溫度狀況下,這系統具有較高的對稱性,例如在自由空間裡的旋轉對稱性與平移對稱性。假設降低溫度,則會凝聚出新的熱力學穩定態,其具有越來越多的對稱性破缺,因此,對稱性會變得越來越低。 凝聚態物理學起源於十九世紀固體物理學和低溫物理學的發展,當今是物理學的最大分支,與化學、材料科學、纳米科技有相當程度的重疊。 ==== 天文物理學 ==== 天文物理學主要研究的是宇宙星體的性質與結構。這包括恆星、行星、星系、星雲、暗星、黑洞等" +175,"球的路途中,會轉換為實驗無法偵測的別種類微中子的现象。帶質量微中子的物理研究是很熱門的理論與實驗題目。辨明微中子震盪與反微中子震盪的不同之處也是個重要題目,其可以對於為什麼宇宙裡到處都是物質,而不是反物質這個宇宙學難題給出解答。很多實驗都在尋找惰性中微子的蛛絲馬跡。 高能量大強子對撞器已開始偵測14TeV能量域,精緻的後標準模型超對稱理論所預測的粒子,或是約占宇宙物質85%的暗物質,這些都是大強子對撞器的探索目標。歐洲核子研究組織(CERN)宣布,大強子對撞機已發現希格斯玻色子,但數量有限,無法詳細觀察其性質,未來通過蒐集更多數據,例如希格斯玻色子的各種衰變管道的頻率,預期將能夠發現任何不符合標準模型之物理行為,以及找到任何不同種類的希格斯玻色子。 在理论物理学領域,理論物理學者嘗試將量子力學和廣義相對論統一成為量子引力理論。這研究已延續了大半個世紀,但至今仍未得到滿意的答案。現今幾個比較成功的理論為M理論、超弦理論、圈量子引力论。 在天文物理学領域,許多天文和宇宙現象仍舊沒有找到合意的解答,如超高能量宇宙射線、重子不對稱性、星系自轉問題等等。.以下列出一些重要論題: *研究宇宙的初始" +176,"物理學的比較 == 註釋 == == 參考文獻 == == 外部連結 == * Emilio Segrè Visual Archives -這檔案館網頁收集了超過30000張科學家本人和相關成就的相片。 * * The Mechanical Universe...and Beyond-加州理工學院物理系52集影音課程。 * 怎樣成為傑出的理論物理學者? -1999年諾貝爾物理學獎得主傑拉德·特·胡夫特的金石良言。 * 諾貝爾物理學獎100年 -東吳大學物理系網頁。 Category:自然科学分支" +177,"天文學是一門研究天體和發生在宇宙中各種現象的自然科學。它使用數學、物理和化學來解釋它們整體的起源和演化。天文學研究的對象包括行星、衛星、恆星、星雲、星團、星系、流星體、小行星和彗星等等。相關現象包括超新星爆炸、伽瑪射線暴、類星體、耀變體、脈衝星和宇宙微波背景輻射。更通俗地說,天文學研究起源於地球大氣層之外的一切事物。宇宙學是天文學中研究整個宇宙的一個分支。 天文學是最古老的自然科學之一。在早期文明有記載的歷史中,有對夜空進行了有條不紊的觀測。這些包括巴比倫、希臘、印度、中國、瑪雅和許多古代的美洲原住民,以及。在過去,天文學包括各種各樣的學科,如天體測量學、天文航海、觀測天文學和曆法的製作。 專業天文學分為觀測和理論兩個分支。觀測天文學的重點是從對天體的觀測中獲取數據,然後利用物理學的基本原理對這些數據進行分析。理論天文學的方向是發展電腦或分析模型來描述天體和天文現象。這兩個領域相輔相成:理論天文學試圖解釋觀測結果,觀測被用來證實理論的結果。 天文學是為數不多,讓業餘愛好者能積極參與的科學之一。對於瞬變事件的發現和觀測尤其如此。業餘天文學家的參與幫助了許多重要的發現,例如發現新的彗星。" +178,"理學的版本。然而,由於大多數現代天文學的研究都涉及與物理學相關的學科,因此現代天文學實際上可以被稱為天體物理學。有些領域,如天體測量學,純粹是天文學,而不是天體物理學。科學家對這一主題進行研究的各個部門可能會使用「天文學」和「天體物理學」,部分取決於該部門在歷史上是否隸屬於物理部門,而且許多專業的天文學家擁有物理學學位,而不是天文學學位。 該領域領先的科學期刊的一些標題包括《天文期刊》、《天文物理期刊》和《天文與天體物理學報》。 ==歷史== === 古代 === 在早期的歷史時期,天文學只包含觀測和預測肉眼可見天體的運動。在一些地方,早期文化留存了大量的文物,而除了儀式用途外,這些文物可能具有一些天文目的。通常,這些文物還可以用來確定季節,這是瞭解何時種植作物和瞭解一年長度的重要因素。 在望遠鏡等工具發明之前,對恆星的早期研究是用肉眼進行的。隨著文明的發展,最顯著的是在埃及、美索不達米亞、希臘、波斯、印度、中國和中美洲,天文觀測台被組裝起來,關於宇宙性質的思想開始發展。大多數早期天文學都是繪製恆星和行星的位置圖,這門科學現在被稱為天體測量學。通過這些觀測,形成了關於行星運動的早期想法" +179,"92-1336)對天文學和鐘錶學做出了重大貢獻,包括發明了第一個天文鐘,可以測量測量行星和其他天體之間角度的,以及可用於天文計算,如月球、太陽和行星的經度,並且可以預測日食,被稱為「Albion」的行星定位儀。尼科爾·奧雷姆(1320–1382)和尚·布里丹(1300–1361)首先討論了地球自轉的證據,此外,布里丹還發展了動力理論(現代科學理論慣性的前身),該理論能够證明行星能够在沒有天使干預的情况下運動Hannam, James. God's philosophers: how the medieval world laid the foundations of modern science. Icon Books Ltd, 2009, 180。(1423–1461)和約翰內斯·米勒(1436–1476)幫助取得了天文學的進步,對幾十年後哥白尼發展日心模型起了重要的作用。 天文學在伊斯蘭世界和世界其它地區蓬勃發展,這導致9世紀初穆斯林世界出現了第一個天文台。在964年,本星系群中最大的星系仙女座星系,在波斯穆斯林天文學家阿卜杜勒-拉赫曼·蘇菲的《恒星之書》中已經有相關的描述。埃及的" +180,"n, The Sun in the Church: Cathedrals as Solar Observatories (1999), p. 3。 ===科學革命=== 在文藝復興期間,尼古拉斯·哥白尼提出了太陽系的日心模型。他的工作得到了伽利略和克卜勒的擴展,在哥白尼的基礎上進一步完善日心說。克卜勒是第一個設計出正確描述行星繞太陽運動細節系統的人,然而,他並沒有成功地寫下建立起這個理論背後的定律。最終解釋行星運動的是牛頓,他發明了天體力學和引力定律。牛頓還開發了反射望遠鏡。 望遠鏡的尺寸和質量的改進導致了進一步的發現。英國天文學家約翰·佛蘭斯蒂德編輯的星表收錄了超過3,000顆恆星Chambers, Robert (1864) Chambers Book of Days;法國天文學家尼古拉-路易·德·拉卡伊製作了更巨大,收錄了10,000顆南天恆星的星表。天文學家威廉·赫雪爾製作了星雲和星團的詳細目錄,並於1781年發現了行星天王星,這是第一顆新發現的行星。 在18-19世紀,尤拉、克萊羅和達朗貝爾對三體問題的研究,導致能更準確預測月球和行星的運動。這項工作由拉格朗日和拉普拉斯進一步" +181,"時代。 ==觀測天文學== 可見光,乃至整個電磁輻射光譜,是人類對天象的主要觀測途徑。觀測天文學的不同領域可依電磁波譜的區域所分,其中有的波長可從地球表面觀測,稱之大氣窗口,有的則須要在高海拔甚至在地球大氣層以外才能有效觀測。 ===射電天文學=== 電波天文學使用波長超出可見光範圍,大於約一毫米的電磁輻射。因為觀測到的無線電波可以被視為波,而不是離散的光子,電波天文學與大多數其它形式的觀測天文學不同。因此,測量無線電波的振幅和相位相對容易,而在較短的波長下則不是那麼容易。 雖然有些無線電波是由天體直接發射的,是熱發射的產物,但觀察到的大部分無線電發射是同步輻射的結果,這是在電子繞磁場運行時產生的。此外,星際氣體產生的許多譜線,特別是21釐米處的氫光譜線,可以在無線電波長下觀察到。 在無線電波長下可以觀察到各種各樣的其它物體,包括超新星、星際氣體、脈衝星和活躍星系核。 ===紅外天文學=== 紅外天文學建立在對紅外輻射的檢測和分析之上,此類輻射的波長比紅光更長,超出了人類肉眼的可見的光波範圍。紅外光譜可用於研究太冷而無法輻射可見光的天體,例如行星、星周盤以及光線被塵埃阻擋的星雲等。紅外" +182,"== 紫外線天文學 === 紫外線天文學使用的紫外線波長介於100至3200 Å(10至320 nm)。這些波長的光會被地球大氣層吸收,因此需要從高層大氣或太空進行觀測。紫外線天文學最適合研究熱藍色恆星(OB星)的熱輻射和光譜發射線,它們在這個波段非常明亮。在紫外線下常見的其它天體包括其它星系中的藍色恆星,行星狀星雲、超新星遺跡和活動星系核。然而,由於紫外線很容易被星際塵埃吸收,所以取得的數據必須再利用其它方法加以校準。 ===X射線天文學=== X射線天文學在X射線範圍觀測天體。宇宙中的X射線來自於同步輻射、溫度高於1千萬開爾文的稀薄氣體發出的熱輻射(見軔致輻射)以及溫度高於1千萬開爾文的稠密氣體發出的熱輻射(見黑體輻射)。發出X射線的天體有:X射線聯星、脈衝星、超新星遺跡、橢圓星系、星系群及活動星系核等。由於X射線會被地球大氣層吸收,所以X射線觀測必須用高空氣球、火箭或X射線天文衛星進行。 ===伽馬射線天文學=== 伽馬射線天文學所觀測的是電磁波譜中波長最短的輻射。伽馬射線可通過康普頓伽瑪射線天文台等衛星或來觀測。切倫科夫望遠鏡不直接探測伽馬射線,而是觀測大氣吸收伽馬射線時所產生" +183,"射、中微子和引力波等不同方法研究同一個天體,這種做法稱為。 Extract of page 313 ===天體測量學與天體力學=== 天文學乃至所有科學中最古老的一個領域,是對各天體位置的測量。在歷史上,準確測量日、月、行星、恆星的位置,有天文航海和制訂曆法等作用。 18世紀開始,天文學家以精確測定的行星位置作為基礎,發展出完善的引力攝動理論,可以極精確地推算過去和未來的行星位置。這門學科稱為天體力學。今天,科學家對近地天體進行大規模追蹤,目的是預測這些天體何時會近距離略過地球以及評估與地球相撞的風險。 太陽系周邊恆星的視差是宇宙距離尺度的起始點。在用視差測量附近恆星的距離後,可以通過比對,推測遙遠恆星的各種屬性。通過測量恆星的徑向速度和自行,天文學家可以繪出銀河系內恆星的運行軌跡,從而算出銀河系暗物質的分佈。 1990年代,天文學家開始利用多普勒光譜學觀察太陽系周邊恆星的擺動。這種方法可以用來發現一些較大的系外行星(詳見系外行星偵測法)。 ==理論天文學== 理論天文學家的研究手段包括數學模型及用電腦做數值模擬,即天體物理學。數學模型一般能揭示天文現象背後更深層次的原理,數值模擬則可" +184,"有一部分會被困在環繞地球的范艾倫輻射帶中。當太陽風粒子沿著磁場線進入地球兩極的大氣層時,就會產生極光。 ===行星科學=== 行星科學的研究對象,除了有太陽系內的行星、衛星、矮行星、彗星、小行星等等,還包括太陽系外行星。科學家最先通過望遠鏡觀察太陽系內的天體,再通過航天器,如今已對太陽系自身的形成和演化有了較好的認識。 從內向外,太陽系可分為內行星、小行星帶和外行星。內行星包括水星、金星、地球和火星,均為類地行星;外行星包括木星、土星、天王星和海王星,均為氣態巨行星。在八大行星以外,還有柯伊伯帶和可能延續1光年的奧爾特雲。 太陽系行星是在46億年前的原行星盤中誕生的。經過相互吸引、碰撞和吸積,原行星盤中逐漸積累起大塊物質,這些物質慢慢演變為原行星。太陽風「吹走」大部分的離散物質,只有質量足夠大的行星才得以保留其大氣層。在接著的後期重轟炸期期間,行星繼續受到太陽系剩餘物質的劇烈碰撞。這些碰撞的歷史遺跡在月球上的諸多撞擊坑中就有跡可循。其中一些原行星也互相碰撞,科學家相信,月球就很有可能是在此類碰撞中形成的。 當行星達到一定的質量後,其內部的物質會根據不同的密度而分離,這段過程稱為行星分化" +185,"階段。恆星核需要有更高的溫度才能使氦聚變。核心溫度足夠高的恆星會一邊使外層膨脹,一邊增加核心密度,形成紅巨星。紅巨星會迅速用盡氦燃料,因此壽命不長。質量更大的恆星會逐步以更重的元素進行聚變,再經過一連串的演化階段。 恆星的質量決定了它最終的歸宿:8個太陽質量以上的恆星會坍縮,成為超新星,而8個太陽質量以下的恆星則會噴出外層的物質,形成行星狀星雲。超新星爆炸後的殘骸是一顆密度極高的中子星;如果恆星質量超過3個太陽質量,則超新星殘骸將會是一個黑洞。相互公轉的聯星會有更加複雜的演化過程,例如,白矮星會從其伴星不斷吸取物質,最終可引發超新星爆炸。行星狀星雲和超新星都有助於把恆星內部經聚變產生的「金屬元素」(在天文學中泛指氫、氦以外的一切元素)分散到星際介質當中。全靠這兩者,包括太陽系在內的行星系統才會由氫和氦以外的多種元素所組成。 ===銀河系天文學=== 太陽系所處的銀河系屬於棒旋星系,是本星系群中的一員。銀河系由氣體、塵埃、恆星等各種天體所組成,這些天體繞銀河系的中心公轉,並通過相互引力束縛在一起。太陽系位於銀河系一個螺旋臂的外端,因此銀河系有很大部分受塵埃的阻擋,觀測不易。 銀河系中心是" +186,"名思義,橢圓星系的截面呈橢圓形。星系中的恆星沿著隨機軌道,而不是一個特別的方向運行。在橢圓星系中,星際塵埃幾乎不存在或完全不存在,恆星誕生的區域少,恆星普遍較老。橢圓星系常見於星系團的中心,很可能是大星系相撞的產物。 螺旋星系呈扁盤形,沿一個方向旋轉,中心有一個凸起的球狀物或棒狀物,從中伸出若干條螺旋臂,向外放射。螺旋臂發亮,充滿塵埃,是恆星誕生的區域,其中的年輕大質量恆星呈藍色。螺旋星系外圍一般是由老恆星組成的暈。銀河系和鄰近的仙女座星系都屬於螺旋星系。 不規則星系是外表混亂,無法歸為螺旋或橢圓星系的星系。宇宙中有四分之一的星系都屬於此類。混亂的形狀很可能是引力擾動的結果。 活動星系會發出巨大的能量,但這些能量並不來自它的恆星、塵埃或氣體,而是來自它的緻密核心。科學家相信,星系中心的超大質量黑洞在吸入物質後發出大量輻射,形成活動星系核。電波星系會發出大量的無線電波,並散發出羽狀或葉狀的巨大氣體結構。其他的活動星系則會發出波長較短的高能輻射,如西佛星系、類星體和耀變體。類星體是可觀測宇宙中持續亮度最高的天體。 宇宙在大尺度上的結構由星系群和星系團組成。最大的星系集體稱為超星系團。宇宙中" +187,"和巨大的空洞。氣體和塵埃逐漸聚集,形成早期星系。這些星系不斷納入更多的物質,並互相形成星系群和星系團,再組成超星系團。 暗物質和暗能量的存在對宇宙的結構有著決定性作用。兩者合起來,共佔全宇宙質量的96%之多。因此,科學家正在極力試圖探究其背後的物理原理。 ==跨學科研究== 天文學和天體物理學與其他科學領域有著密切的跨學科關係。考古天文學利用考古學和人類學證據,研究遠古或傳統天文學在文化中的角色和地位。Sinclair 2006:13天體生物學研究生物系統在宇宙中的起源、演化和分佈,並特別關注地外生物能否存在,人類又如何能探測這些生命這些問題。將統計學方法應用在分析不可勝計的天文觀測數據上。 天體化學研究宇宙中化學物質的形成和反應。宇宙化學專門研究太陽系內化學物質的分佈、來源以及同位素比率的變化。 利用天文學的知識,解答法律、歷史上的疑問,例如驗證拍攝照片的日期或確認有關天文藝術作品的創作時間。 ==業餘天文學== 天文學是眾多科學領域中,業餘者能夠最大程度參與的領域。 業餘天文學家可以對各種天體和天文現象進行觀測,有的甚至會自己搭建觀測器材。最普遍的觀測對象包括:日、月、各大行星、小" +188,"初始條件如何,天文學家都會觀測到相同的初始質量函數?有待對恆星和行星的誕生過程有更深入的了解。 *宇宙中是否存在其他生命體?是否存在其他智慧生物?如果存在,應如何解決費米悖論?證實地球以外生物的存在,對許多科學和哲學問題有至關重要的意義。太陽系是平平凡凡,還是絕無僅有? *暗物質和暗能量的本質是甚麼?這兩者在宇宙的發展和未來有著決定性作用,然而人類對它們的本質尚且一無所知。宇宙的終極命運是甚麼? *第一批星系是如何形成的?超大質量黑洞是如何形成的? *超高能宇宙射線從何而來? *鋰元素在宇宙中的豐度為甚麼比大爆炸模型所預測的低四倍? == 注释 == == 参考文献 == === 引用 === === 来源 === * Available at Project Gutenberg , Google books * * == 外部連結 == * 美国国家航空航天局 * 《天空和望远镜》杂志 * 天文及天体物理学百科全书 * 2009國際天文年官方網站 * 中国國家天文台 * 北京天文馆 * 香港太空館 * 美國太空總署每日一天文圖正體中文站 * 台北市立天文科學教育館 * 網路天文館 (" +189,"力学()是物理学的一个分支,主要研究能量和力以及它们与物体的平衡、变形或运动的关系。 == 发展历史 == 人们在日常劳动中使用杠杆、打水器具等等,逐渐认识物体受力,及平衡的情况。古希腊時代阿基米德曾对杠杆平衡、物体重心位置、物体在水中受到的浮力等,作了系统研究,确定它们的基本规律,初步奠定了静力学,即平衡理论的基础,古希臘科學家亞里斯多德也提出作用力造成運動的主張,即物體不受力,必將停止。 自文藝復興之后,科學革命興起,伽利略的自由落体运动规律,以及牛顿的三大运动定律皆奠定了动力学的基础。力学从此开始成为一门科学。此后弹性力学和流体力学基本方程的建立,使得力学逐渐脱离物理学而成独立学科。到20世纪初,在流体力学和固体力学中,实际应用跟数学理论的互相结合,使力学勃起创立了许多新理论,同时也解决了工程技术中大量关键性问题。 == 古典力学及量子力學 == 力學主要可分為古典力学及量子力學。 若以發現的時間來看,古典力学較早被發現,啟源於牛顿的三大运动定律,量子力學則是20世紀初才由許多科學家所創立。 古典力学主要研究低速或静止的宏观物体。开普勒、伽利略,尤其是牛顿是古典力学的奠基人。 量" +190,"空間中的方向。 物體也有可能是可允許形變的半剛體,如彈性體,或者根本沒有固定的形狀,如流體。這些物體可利用古典力學的方式研究,也可以利用量子力學來分析。 例如一顆棒球的運動常使用古典力學來分析,而原子核內質子及中子的行為則通常會用量子力學來描述。 == 主要分支學科 == 在物理學的研究中,也有用「場」來描述物質的行為,稱為場論。其描述方式和力學使用的方式有些不同,可分為古典場論及量子場論。不過在實務上,場論及力學要探討的內容常常有密切的關係。例如作用在物體上的力常常是因為電磁場或重力場而產生,而當物體對其他物體產生作用力時,也常常會產生場。事實上,若以量子力學的觀點,物體也是場,可以用波函數來描述。 * 經典力學(基礎力學) ** 質點及剛體力學 ***應用力學或稱工程力學 ****靜力學 **** 運動學 **** 動力學 ***分析力學 ****拉格朗日力學 ****哈密頓力學 ** 連續介質力學 ***固體力學 ****材料力學 ****彈性力學 ****塑性力學 ****損傷力學 ****接觸力學 ****斷裂力學 ****結構力學 ****土力學 *** 流體力學 ****" +191,"sources for teaching Mechanics * The Archimedes Project * Engineering Fundamental Solid & Fluid Mechanics Category:物理学分支" +192,"化學是在原子、分子層次上研究物質的組成、結構、性質以及變化規律的科學。化學研究的對象涉及物質之間的相互關係,或物質和能量之間的關聯。傳統的化學常常都是關於兩種或以上的物質之間的接觸和其後的變化,即化學反應,又或者是一種物質變成另一種物質的過程。這些變化有時會需要使用電磁波,當中電磁波負責激發化學作用。不過有時化學並不一定要關於物質之間的反應。光譜學研究物質與光之間的關係,而這些關係並不涉及化學反應。准确的说,化学的研究范围是包括分子、电子、离子、原子、原子团在内的核-电子体系。 「化學」一詞,若單從字面解釋就是「變化的學問」之意。化学主要研究的是化学物质互相作用的科学。化學如同更廣義的物理皆為自然科學之基礎科學。很多人稱化學為「中心科學」,因為化學為部分科學學門的核心,連接物理概念及其他科學,如材料科學、纳米技术、生物化學等。研究化學的學者稱為化學家。在化學家的概念中一切物質都是由原子或比原子更細小的物質組成,如電子、中子和質子。但化学反应都是以原子或原子团为最小结构进行的。若干原子通过某种方式结合起来可构成更复杂的结构,例如分子、離子或者晶體。 == 詞源 == 英語中的「化學」()" +193,"成黃金,因此累積了金屬的提取和處理有關的觀察和技術。有些煉金術士主要的工作是製造藥物。2000年前,人類已廣泛使用金、銀、汞、銅、鐵和青銅。 === 早期化学 === 早期化學家收集了很多不同物質的資料。在17世紀以前,化學成就並不大(燃素說、煉金術),其中較有成就者如羅伯特·波義耳。到了1750年,化學仍帶有神秘色彩,並為不正確的理論支配著。直到1773年,安托万-洛朗·德·拉瓦锡提出了質量守恆定律,並以氧化還原反應解釋燃燒現象,推翻了盛行於中世紀的燃素說,才開啟了現代化學之路;他因此被尊崇為「化學之父」。接著道尔顿整合當時的化學知識,並以自身的實驗所得提出了劃時代的原子說。此後,一些化學家相繼發現了各種化學元素,後來門得列夫建立了元素週期表令化學視界更臻完備。1901年,化學家諾貝爾以其遺產成立了諾貝爾化學獎,以表揚在科學領域及其他重要領域對人類有較大貢獻者。 === 现代化学 === 现代化學始於20世紀初期蓬勃發展的量子力學。萊納斯·鮑林引進量子力學解釋化學鍵的本質,得以用波函數的線性疊加來描述。質子、中子和電子的發現,使化學真正由原子尺度來理解化學反應。量子力學和電子學的發展" +194,"元素組成的化合物的化學學門。有機化學主要研究有機化合物的合成途徑和方法、機構和物理性質。由於有機化學高度的應用性和悠久的發展歷史,通常被普羅大眾視為當代化學的代名詞。有機合成和新反應途徑的開發,對於藥物,天然物,生物和材料高分子的開發,都是極為重要的一環,對於化學工業有極大的影響。 * 無機化學有機化合物以外元素的化學領域,研究化合物的合成途徑和方法,機構和物理性質,最常見的分子體系為金屬化合物。有機和無機化學領域常有交疊,甚至有密不可分的趨勢。有機金屬化學就是一門結合有機和無機領域的化學。 其他延展和應用的學門: thumb|200px|移液器,一種生化常用的實驗儀器 * 理論化學從物理的理論去解釋各種化學現象的學門。 * 計算化學由於分子體系的複雜性,分子的反應,動態,結構,經常是無法完全以量子力學做計算的。因此計算化學提供各種簡約的計算方法,來預測並輔助實驗結果的推斷。實用性上已有諾貝爾獎的肯定,如1998年獲諾貝爾化學獎的密度泛函方法。 * 生物化學是研究生物體內發生的化學反應和相互作用的學科,被應用於研究細胞中各組分(例如:蛋白質,碳水化合物,脂類,核酸以及其他生物分子)的結" +195,"的物質有什麼不同。 * 大氣化學是一種對地球大氣層及其他星球的大氣層的研究。大氣化學都會研究環境變化途中發生過什麼化學反應,是大氣科學的一個重要分支學科。 * 環境化學研究化學物質進入不同自然環境,經物理、化學、生物反應後,形成的產物是否對人體及生態造成危害。 * 綠色化學研究怎樣從化學角度減低污染。 * 資訊化學用電腦去解決化學上的問題。 * 地球化學研究地殼中各種物質的化學特性,解釋它們的構造。 * 石油化學從化學角度研究石油及天然氣的特性及煉油技術。 * 高分子化學研究比較大的分子,即是高分子,例如發泡膠怎樣造出來和有些什麼特性。高分子化學亦會研究怎樣令很多分子結合為一粒高分子。 * 超分子化學研究共價鍵以外各種化學鍵,例如:氫鍵、范德華力、疏水效應的運作。 == 基本概念 == === 原子 === 一粒原子是由原子核及外圍帶負電荷的電子(称为核外电子)組成的粒子,一般而言是化學研究的最小尺度範疇。原子核通常是由質子和中子組成。与通常的物理概念不同的是,单一的质子在化学领域被认为是1H(氕)原子核,也就是说原子核内必然含有质子,但可能不含中子。 電子帶負電荷,質子帶正電荷,個數" +196,"化学药品或者说化学制品。大多数我们日常生活碰到的化学品都是混合物,比如空气、合金、生物制品。 ==== 命名法 ==== 物质的命名法在化学语言当中是最严格的一环。早在很久以前,化合物的命名是由其发现者自行决定的,这样则导致了命名的困难和混乱。而现在我们最常用的还是国际纯粹与应用化学联合会 () ()命名方法。它用一个命名系统让所有的化合物都有一个独有的名称和代码。有机化合物通过有机命名系统命名;而无机化合物通过无机命名系统命名。而通过化学索引服务(),我们可以轻松的通过CAS号()来找到每一个化合物的性质、特性、命名和结构。 === 分子 === 一個分子是化合物的最基本结构,不用化學方法是拆不開的。大部分分子都是由兩個或以上原子組成,但是都有些特例,例如氦氣分子,只有一個原子。這些原子,如果多於一個,是通过化學鍵結合。 === 離子和盐 === 離子是带电荷的物质,可以由原子或分子失去或得到电子形成。正離子(例如鈉離子Na+)和負離子(例如氯離子Cl−)結合可以成為電荷中性的鹽(例如食鹽NaCl)。有些離子是由幾個原子組成,而它們進行化學作用的時候又不會分离,例如磷酸根離子(PO4" +197,"向。同样的我们可以用pOH代替pH, Kb代替Ka来说明碱性强度。 === 氧化还原 === 氧化还原的概念和一个物质的原子获取或者给予电子的能力有关。物质拥有氧化其他物质的能力就被成为氧化性,而此物质被成为氧化剂(),或者成为氧化物。一个氧化剂能够将电子从其他的物质上移走。相应的,具有还原其他物质的物质被称作有还原性而成为还原剂()或者成为还原物。一个还原试剂能够传递给其他物质电子并且氧化自身。而正因为其“给予”了其他物质电子,它还被称为供电子物。氧化还原的性质与氧化数()有关--其实真正的给予或者获取完成的电子并不存在。所以,氧化过程被定义为增加了氧化数,而还原则是降低的氧化数。 簡單來說,氧化反應指還原劑失去電子,化合價上升;而還原反應是指氧化劑得到電子,但化合價下降。氧化和還原反應必須同時進行。 === 化學品 === 化學品泛指一切有確實化學構造及化學成份的物質,所以又稱化學物質。它們可以是元素、化合物或混合物。日常生活中,我們會遇到的東西多數都是混合物,例如合金。 === 化合物 === 化合物是一些以不同元素用固定比例結合而成的物質。成份的比例決定了它的化學特性。例如水是" +198,"。 === 分子间力 === 分子间力是不同分子之间的作用力,主要有氢键,范德华力,亲水作用/疏水作用等,这种作用力比化学键弱,容易打开或重新组合,但是是形成分子空间排列和架构的重要作用力,是现代化学的重要研究方向之一。 === 物理特性 === thumb|水湧上沙灘造成浪,就是水與沙的物理特性 物質有時會是液體,有時會是固體,有時會是氣體,這些叫作物質的相態。一件物質是否軟、透不透光、透光的話它的折射率是多少,這些都是一件物質的物理特性。總而言之,物理特性即是一種物質不靠化學作用都可以斷定到的特性。 === 化學反應 === 化學反應,亦称化学变化是一種物質轉變為另一種物質的過程,涉及分子中原子的交換和化學鍵的轉移、形成或消失。化學反應形成的改變既可令很多獨立的分子結合,也可將一個較大型的分子拆開成為很多獨立的小分子,甚至是同一分子內有原子移動,即使原子的數量沒有改變,但仍會構成化學反應。 === 平衡 === 雖然平衡概念在科學各領域都得到廣泛的應用,但在化學中,化學平衡是指化學成分中出現多種不同狀態的可能性,例如在可以彼此反應的幾種化合物的混合物中,或當物質可以以多於一種相態存" +199,"商在2004年共销售了5870亿美元的业绩,其中利润占据了8.1%,其中研发成本占据了2.1% == 学科分类 == * 无机化学是研究无机化合物的化学。 ** 元素化学 ** 无机合成化学主要是經由各式各樣的无機反應來建構無機分子。 * 有机化学是研究有机化合物的结构、性质、制备的学科,又称为碳化合物的化学。 ** 有机金属化学是有机化学和無機化學交疊的一門分支課程,研究含有金屬(包括類金屬)和碳原子鍵結的有机金属化合物,其化學反應、合成等各種問題。 ** 有机合成化学 ** 天然有机化学 * 分析化学是开发分析物质成分、结构与量的方法,使化学物质成分得以定性或定量,化学物质结构得以确定。 ** 按分析目标分,可分为定性分析与定量分析 *** 定性分析主要任务是确定物质的组分 *** 定量分析需要测定物质中各组分的含量的分析方法 ** 按分析手段分,可分为化学分析与仪器分析 *** 化学分析 *** 仪器分析是用儀器的物理學方法,測量物質的物理和化學性質的参數,並實驗其變化,以此判斷其化學成份,元素含量,甚至化學結構等。 * 生物化学是研究生物体中的化学进程的一门学科,常常被简称为生" +200,"化学等 == 参見 == * 诺贝尔化学奖 * 元素列表 * 化学工业 * 化学物质列表 * 化学术语列表 * 元素周期表 * 化学家 * 同位素列表 * 化學基本主題列表 * 化學與物理學的比較 == 註釋及参考資料 == === 参考文獻 === * * * * * * * * * * * * * * == 外部連結 == * 英国皇家化学学会 * 美国化学协会" +201,"地理學()是探索地球及其特徵、居民和現象的學問,研究地球表層各圈層相互作用關係,及其空間差異與變化過程的學科體系。 == 词源 == 英語geography一詞源自古希臘語(羅馬化:geografía),由geo-(意為“大地”)和(意為“寫”)組成,字面意思為“對大地的描述”。最早使用geography的是埃拉托斯特尼(Eratosthenes),他用此詞表示研究地表景物的學問。 中國古代地理學稱「輿地」或「輿地學」,「地理」則是指山川方域的地勢、外貌。最早的地理书籍有《禹贡》、《山海经》等。中文的「地理」一詞最早見於《易经·繫辭》:「仰以觀於天文,俯以察於地理」。日本在明治維新後,將英語geography一詞譯為「地理」,之後該譯法傳入中國。1902年,光緒皇帝接受吏部尚書張百熙建議,頒佈《欽定學堂章程》,故一般小學課目有史學、輿地二項。1903年,張百熙派吳汝綸赴日本考察教育後,負責教育改革的張百熙、張之洞、榮慶向皇帝建議重訂學堂章程《清史稿》〈志·選舉二·學校二〉:「博考外國各項學堂課程門目,參酌變通,擇其宜者用之,其於中國不相宜者缺之,科目名稱不可解者改之,過涉繁重者減之。」" +202,"生態、經濟,故地理學是一門跨學科的學問。 地理學可略分為二:自然地理學及人文地理學。自然地理學著眼自然環境,如地形的形成過程、有關氣候、土壤、水、生態、植被等的各種現象及其相互關係。人文地理學則著眼人造環境,例如人類如何開拓、管理和看待空間。自然與人的關係難解難分,故此立環境地理學,旨在基於自然地理學與人文地理學的研究成果,分析自然與人的相互關係,並提出人類適應、改造自然,以切合自身永續發展。 == 分支 == === 自然地理学 === 自然地理學屬自然科學,專門探索大自然(岩石圈、大氣圈、水文圈、生物圈)的變化模式,可分類為以下範疇: Línea de Wallace.jpg|生物地理學及景观生态学 Cyclone Catarina from the ISS on March 26 2004.JPG|氣候學及 90 mile beach.jpg|海洋學及 Gavin Plant.JPG|环境地理学及環境管理 Meridian convergence and spehrical excess.png|大地測量學 Delicate Arch LaSalle.jpg|地形學 Pangea" +203,"g|歷史地理學及時間地理學 Pyramide Comores.PNG|人口地理學及人口學 Dairy cattle, Synergy Farm, New York.jpg|工業地理學及农業地理學 ReligionSymbol.svg|宗教地理學 RERParisVision2025.png|交通地理學 Bangladeshi villagers and foreigner.jpg|旅遊地理學 New-York-Jan2005.jpg|聚落地理學及都市地理學 Egyptian T-55 tank disembarking LCU-1644 during Exercise Bright Star 1985 DF-ST-86-08080.jpg|軍事地理學 UN General Assembly.jpg|政治地理學及地緣政治學 Queue.svg|人種地理學 人類學問與日俱增,時有他學之新猷用於人文地理学研究,如: * 行為地理學 * 地理知識學(Geosophy) * * 音乐地理学 * 食品地理学 * 战略地理学 === 環境地理學 === 環境地理學從空間層面描述人類與大自然的關係,連" +204,"的注意力亦有放在區域化(regionalization)上,包括運用適合的方法把空間分界成為區域。 區域地理學亦被認為是研究地理科學的必然方式(類似計量革命或批判地理學),詳細參見地理學歷史。 === 相關範疇 === * 城市規劃,區域規劃(regional planning)及空間規劃(spatial planning):利用地理科學幫助決定如何發展(或是不發展)土地去滿足特定條件,例如安全、美觀、經濟機會、保護建築或自然遺產等等。城市、城鎮及區域規劃可以被視為應用地理學。 * 区域科学:區域學在1950年代由沃爾特·艾薩德(Walter Isard)帶領而冒起,它提供一個更為以數據及分析作基礎的方法去面對地理學問題,而不是傳統地理學上以描述的趨向去面對。區域學包含的知識中空間向度(spatial dimension)扮演重要的角色,例如區域經濟學、資源管理(resource management)、區位理論、城市規劃、區域規劃、交通運輸、通訊、人文地理學、人口分布(population distribution)、景觀生態學及環境質素。 * 行星學:雖然地理學通常關注地球,但亦有" +205,"Cartographers)必須學習認知心理學及人因工程學去理解那種符號表達地球的資訊最為有效,而學習行為心理學則可以引導地圖讀者對資訊作出行動。她們必須學習大地測量學及頗為高階的數學去理解地球的形狀如何影響地圖符號的變形,從而反映在平面上作觀察。地圖學可以毫無爭議地被視為地理學中的大型學科的種子。大部分地理學家在幼年時對地圖充滿幻想視為一個她們將會窮一生精力去追尋地理學的早期表徵。 === 地理資訊系統 === 地理資訊系統(,GIS)處理並儲存高準確度的地球資訊在電腦內。除了其他地理學分支外,地理信息系統專家必須理解電腦科學及資料庫系統。地理資訊系統引起地圖學革命性的改變;幾乎所有的地圖製作現在都以地理資訊系統軟件幫助製成。GIS同時亦代表地理信息科学(),即利用地理資訊系統的軟件和技術去代表、分析及預測空間關係的科學。 === 遙測 === 遥感()可以被定義為一門取得從遠處量度出來的資訊的技術和科學。遙測的資訊由多方面而來,包括衛星地圖、航空攝影及手提感應器。地理學家不斷增加利用遙測去得出的地球表面、海洋及大氣資料。其原因為: # 遙測能夠提供多樣空間尺寸的資料(局部至全球)" +206,"地理學歷史 == === 地理學的雛型 === 米利都的阿那克西曼德的構想被後期的希臘作家認為是地理學的始創人,而現在只能夠以其承繼者的片段才可以得知其一二。阿那克西曼德的貢獻包括發明晷針(Gnomon),一個雖然簡單但是她提供一個有效的希臘工具作為量度緯度。泰勒斯與阿那克西曼德亦因為預測日蝕而獲讚揚。地理學的基礎可以追溯至古代文化,例如古代、中世紀及近現代的中國歷史。最初以藝術與科學層面探索地理學的古希臘,則利用地圖學(詳見地圖學歷史History of cartography)、古希臘哲學、古希臘文學或數學(詳見數學史)等方面去探究地理學。 === 圓形世界理論 === 第一個指出地球為球體的人有究竟是巴門尼德或是畢達哥拉斯的爭議。阿那克薩哥拉雖然能夠利用解釋日蝕論證出地球是圓形,但他和其他當代學者一樣認為地球是平面的。埃拉托斯特尼是其中一個最初估計地球半徑的人。 === 經緯線系統及其進化 === 第一個最初準確的經緯線系統被認為是喜帕恰斯。他利用巴比倫數學推演出一個六十進位法(sexagesimal)系統。經線及緯線被細分為360°,每度更被細分為60′(角分)。為了量度(不同" +207,"方的地理学觀念及方法大多都被中國所採用。 === 地理大發現 === 在16世紀至17世紀的地理大發現時期,探險家如克里斯托弗·哥倫布等發現及佔據新土地,需要令地理資料更為準確,與及一個更為堅固的理論基礎。由杰拉杜斯·麦卡托(Gerardus Mercator)和伯恩哈德·瓦倫紐斯(Bernhardus Varenius)的著作《地理學概論》(Geographia Generalis)正正是新出現的科學化地理學的一流例子。 === 眾多地理學協會的成立 === 地理學在18世紀及19世紀被認同為一個獨立學術科目,並成為歐洲(特別是巴黎及柏林)大學的一門經典課程。19世紀期間眾多地理學協會發展起來,包括1821年成立的巴黎地理學會(Société de Géographie)、1830年成立的皇家地理學會(Royal Geographical Society)、1845年成立的俄羅斯地理學會(Russian Geographical Society)、1851年成立的美國地理學會(American Geographical Society)與及1888年成立的國家地理學會。伊曼努爾·康德" +208,",出版《宇宙》及創立地理學的分支。 * 卡爾·李特爾(Carl Ritter,1779年-1859年):被視為現代地理學之父之一。穩佔柏林洪堡大學第一把交椅。 * (1807年-1884年):在冰川結構、冰川運動,特別是快速的高度認識享譽盛名。 * 威廉·莫里斯·戴維斯(William Morris Davis,1850年-1934年):美國地理學之父及侵蝕循環(cycle of erosion)的發展者。 * 维达尔·白兰士(Paul Vidal de la Blache,1845年-1918年):法國地質政治學學校創辦人及近代地理學奠基人。 * 哈尔福德·麦金德爵士(1861年-1947年):倫敦政治經濟學院的共同創辦人,之後幫助創立以及雷丁大學,並成為英國地理協會會長,《》及心臟地帶(Heartland)理論作者。 * 瓦爾特·克里斯塔勒(Walter Christaller,1893年-1969年):人文地理學家及中心地理論發明者。 * 段義孚(Yi-Fu Tuan,1930年-):美籍華人學者,開展人本主义地理学(Humanistic Geography)作為一個學科。 *" +209,"地质学(法语、德语:Geologie;;拉丁语、西班牙语:Geologia;源于希腊语 γῆ 和 λoγία)是对地球的起源、历史與结构进行研究的学科。主要研究地球的物质组成、内部构造、外部特征、各圈层间的相互作用和演变历史。在现阶段,由于观察、研究条件的限制,主要以岩石圈为研究对象,并涉及水圈、大气圈、生物圈和岩石圈下更深的部分,以及涉及其他行星和衛星的太空地质学。 ==地球歷史學== -{zh-hant:;zh-hans:}- 地质时间尺度涵盖了整個地球历史International Commission on Stratigraphy . stratigraphy.org。其起點開始於最早的太陽系材料生成時,約在4.567Ga,地球的出現約在4.54 Ga,一開始稱為冥古宙,最年輕的地質年代則是全新世。 ===重要的地質事件=== * 4.567 Ga(十億年前):太阳系形成 * 4.54 Ga:地球吸积 * 約 4 Ga:後期重轟炸期的結束,出現第一個生命 * 約 3.5 Ga:開始光合作用 * 約 2.3 Ga:有含氧的大氣,第一起雪球地球事件 * 730–635 Ma(百萬" +210,"质学的起源者,还是路灯、窗玻璃、焰火、弦乐器、栽培水果、香水、香料等等的发明者。 (Fielding H. Garrison, An introduction to the history of medicine, W.B. Saunders, 1921, p. 116)比魯尼就是最早的之一, 他的著作有最早的叙述的文章,提出了印度次大陆曾经是海洋的假设。伊斯兰学者伊本·西那对山脉的形成,地震的原因,以及其他一些现代地理学的论题给出了详细的解释,这些内容为日后地质学的发展提供了基础。Toulmin, S. and Goodfield, J. (1965), ’The Ancestry of science: The Discovery of Time’, Hutchinson & Co., London, p. 64在中国,博学家沈括(1031–1095)提出陆地形成的假说。他在一个离海洋几百公里远的山中的看到,在一个地质地层里有贝壳类生物化石。由此他推论,陆地是由山脉的侵蚀和淤泥的沉积所形成的。 很早以前,地质学的知识比较零星分散。关于这方面的知识,如从地中开采金属、黏土、煤和盐的一" +211,"持渐变论观点的学者认为,物种演化的动力来自于微弱的地质作用在地球演变过程中的长期积累,不依靠大型的灾难也能够发生。 第三场争论是固定论与活动论之争。固定论学说认为地壳的位置是永远不变的,其运动方式以垂直运动为主。传统的地槽、地台学说就是一种固定论学说。活动论学说认为地壳的运动是以水平运动为主的,垂直运动虽然存在,但是是水平运动过程中派生出来的(比如两地块水平挤压处地壳向上隆起)。活动论随着板块构造学说的发展,逐渐被学者所认同。 == 其他分支学科 == * 基础学科 ** 矿物学 ** 岩石学 ** 矿床学 ** 地球化学 * ** 古生物学 ** 地层学 ** 历史地质学 ** 古地理学 ** 地质年代学 ** ** 天文地质学 ** 地球深部地质学 * 应用地质学 ** 水文地质学 ** 工程地质学 ** 军事工程地质学 ** 环境地质学 ** 灾害地质学 ** 金属矿产地质学 ** 非金属矿产地质学 ** 石油地质学 ** 煤地质学 ** 找矿勘探地质学 ** * 其他 ** 地球物理勘探 ** 地球化学勘查 ** 矿业工程 ** 数学地质学 ** 第四纪地质学 ** 冰川地质学" +212,"气象学()是把大气当作研究的客体,从定性和定量两方面来说明大气特征的学科,集中研究大气的天气情况和变化规律和对天气的预报。气象学是大气科学的一个分支。 天气学(synoptic meteorology)又稱綜觀氣象學,是大气科学中研究各种天气现象发生发展的规律,以及应用这些规律来制作天气预报的学科;其為專門對天氣圖資料之研究與分析的一種氣象學,內容包含地面氣象觀測、高空圖、熱力圖及衛星雲圖分析,為每日天氣預報的主要依據。 == 发展歷史 == 第一位建立氣象學的人是古希臘哲學家亞里士多德。在他的專書《氣象匯論》中,他最先敘述和粗淺地解釋了風、雲、雨、雪、雷、雹等天氣現象,而這書是世界上最早的氣象書籍。直到18-19世紀,由於物理學和化學的發展以及氣壓、溫度、濕度和風等測量儀器的陸續發明德國人布德蘭繪製了第一張地面天氣圖,開創了近代天氣分析和預報方法。1835年,法國人科利奧里提出風偏轉的概念;而1857年荷蘭人白貝羅提出風和氣壓的關係,他們的概念都成為大氣動力學和天氣分析的基礎。1854年11月14日克里米亞戰爭期間,一場暴雨使亨利四世的軍艦及商船毀壞殆盡,造成400人死亡,當時的國防" +213,": === 觀測研究 === 觀測研究是藉觀測去了解不同的大氣現象,可以說是氣象學理論的其中一塊基石,亦是一般氣象愛好者所關注的。觀測方法亦有很多種,氣象站、高空氣球、衛星雲圖、雷達回波圖等。觀測研究不只是觀測,也有一定程度的歸納和分析,例如一句「明天轉冷」,便是一種分析。此外,繪製天氣圖、整理熱帶氣旋路徑、氣候區域分類等,亦是觀測研究所要做的。 === 理論研究 === 理論研究有三大部份,除觀測外,物理和數學對理論研究亦很重要。理論可以從兩方面產生,一方面是從觀測數據中直接建立出來的,例如分析熱帶氣旋強度的德沃扎克分析法,另一方面是從物理理論或其他氣象理論演化出來的,例如地轉方程、氣壓梯度方程等。物理理論很多時需要數學的幫助,反過來說,數學語言有時更能使我們明白物理和氣象理論。 === 數值模式研究 === 現代的天氣預報即是建立在數值模式上,數值模式透過非線性方程式以及超級電腦的插點格運算,反演出一地區未來數天天氣。數值模式研究是較少人所認識的,它們都需要相當的理論知識、電腦程序技巧和實驗技巧。數值模式研究會把不同的物理和氣象方程,以電腦程序的方式放進電腦裡,再計算出未來溫度、濕" +214,"面(冷锋、暖锋、准静止锋、锢囚锋)、高空急流、西風急流 ** 气旋和反气旋 *** 飓风或台风 *** 溫帶氣旋 *** 副熱帶高氣壓 *** 冷涡 *** 热带辐合带 *** 阻塞高压和切断低压 ** 中尺度系统 *** 颮线 *** 对流云 * 大气光学 ** 晕、彩虹(霓)、华、峨眉宝光、假日 * 雷电 ** 闪电、雷、高层大气闪电 * 气候、气象灾害 ** 厄尔尼诺现象、拉尼娜、季风、信风 ** 台风、洪水、干旱、沙尘暴、寒潮、热浪、冰暴 * 天气现象 ** 降水、雾、雪、阵雨、霰、冰雹、冰珠、冻雨、雾凇、冰针、露、霜、雨夹雪(霙) ** 沙尘暴、霾 ** 暴风雪、暴雨、强对流天气、龙卷风、下击暴流 ** 极光、地磁暴 * 气象观测手段 ** 陆基(自动气象站)、海基(测量船、浮标)、空基(航测)、天基(卫星遥感) * 人工影响天气 ** 人工降雨、人工除雹 * 气象测量仪表和设备 ** 风速计、气压计、湿度计、温度计、雨量计、风向标、雷达、地球观测卫星、多普勒雷达 == 气象学的研究尺度 == === 行星尺度(气候尺度) === 水平尺度达10000公里,大气超长波,超大型" +215,"宜时应当推迟或取消。 == 另见 == * 极端价值学说 * 微氣象學 * 中尺度氣象學 * 大气科学 == 参考文献 == == 外部链接 == * Glossary of Meteorology - From the American Meteorological Society, an excellent reference of nomenclature, equations, and concepts for the more advanced reader. Category:大气科学" +216,"生物學(;;德語:;法語:;)或稱生物科學()、生命科學(),是自然科學的一大門類,由經驗主義出發,廣泛研究生命的所有方面,包括生命起源、演化、分佈、構造、發育、功能、行为、與環境的互動關系,以及生物分類學等。現代生物學是一個龐大而兼收並蓄的領域,由許多分支和分支學科組成。然而,儘管生物學的範圍很廣,在它裡面有某些一般和統一概念支配一切的學習和研究,把它整合成單一的、連貫的領域。在總體上,生物以細胞作為生命的基本單位,基因作為遺傳的基本單元,和進化是推動新物種的合成和創建的引擎。今天人們還了解,所有生物體的生存以消耗和轉換能量,調節體內環境以維持穩定的和重要的生命條件。生物學分支學科被研究生物體的規模所定義,和研究它們使用的方法所定義:生物化學考察生命的基本化學;分子生物學研究生物分子之間錯綜複雜的關系;植物學研究植物的生物學;細胞生物學檢查所有生命的基本組成單位,細胞;生理學檢查組織,器官,和生物體的器官系統的物理和化學的功能;進化生物學考察了生命的多樣性的產生過程;和生態學考察生物在其環境如何相互作用。最終能夠達到治療診斷遺傳病、提高農作物產量、改善人類生活、保護環境等目的。 ==" +217,"群和學術團體,都認為演化論是唯一能完全滿足在生物學、古生物學、分子生物學、遺傳學、人類學及其他各領域中所觀察到的現象的理論。The National Science Teachers Association's position statement on the teaching of evolution.IAP Statement on the Teaching of Evolution Joint statement issued by the national science academies of 67 countries, including the United Kingdom's Royal Society (PDF file)From the American Association for the Advancement of Science, the world's largest general scientific society: 2006 Statement on the Teaching of Evolution (PDF file), AAAS Den" +218,"生物,都有體内平衡的機制Kelvin Rodolfo, Explanation of Homeostasis on scientificamerican.com . Retrieved Oct. 16, 2009。 :一系統若要維持動態平衡,並且有效的進行調整,需要有能力偵測擾動,並且針對擾動進行回應。生物系統在偵測到擾動後,一般會利用負回授的方式回應。也就是藉由調整系統的條件,設法降低擾動的影響。就像若動物體內血糖濃度過低時,會釋放胰高血糖素增加血糖一様。 === 能量 === 一个活的生物體的生存依赖于能量的连续输入。生物體是靠化學反應來從食物中提取能量,才能維持身體機能,並建立新的細胞。在上述反應中,組成食物化學物质的分子扮演两个重要角色。第一,這些分子中有些可以藉由生物體內的化學反應產生能量。第二,有些則可以組成生物分子中的新的分子结构。 负责引进能量到生态系统的生物被称为生产者或自养生物。几乎所有的这些生物體最初都从太阳吸取能量。 == 研究概況 == 生物學家從很多面向研究生物,因此產生很多研究領域。例如: * 面向原子和分子:分子生物學、生物化學、結構生物學。 * 面向细" +219,"分類與命名 === * 分類:生物學對物種的分類,由上而下有 8 個層級。任一物種同時在這 8 個層級有其位置和名稱。 它們是域(regio)、界(regnum)、門(divisio 或 phylum)、綱(classis)、目(ordo)、科(familia)、屬(genus)、種(species)Vienna Code, 2006. ,Article 3,此處補注階層的拉丁名,括弧內前者為拉丁文,後為英文,相同者不另列出:界 (regnum, kingdom), 門(divisio or phylum, division or phylum), 綱 (classis, class), 目 (ordo, order), 科 (familia, family), 屬 (genus), 種 (species)。參閱國際植物命名法規。。 * 命名:所有的物種都有其獨特的一個學名,全球認可共通(除了學術上的分類爭議以外),不因地區或國家而不同。 雙名法是學名的命名方法,英文為 Binomial Nomenclature。它給每個物種訂立兩個文字,前字是該物種的屬(genus)名,後字是它的種" +220,"为地干预、控制所研究的对象,并通过这种干预和控制所造成的效应来研究对象的某种属性。17世纪前后生物學中出现了最早的一批生物學实验,如英国生理學家威廉·哈维关于血液循环的实验,扬·巴普蒂斯塔·范·海尔蒙特关于柳树生长的实验等。到了19世纪,物理學、化學比较成熟了,生物學实验就有了坚实的基础,因而首先是生理學,然后是细菌學和生物化學相继成为明确的实验性的學科。19世纪80年代,实验方法进一步被应用到了胚胎學,细胞學和遺傳學等學科。 * 系统的方法:系统科學源自对还原论、机械论反省提出的有机體、综合哲學,从克洛德·贝尔纳与揭示生物的稳态现象、诺伯特·维纳与威廉·罗斯·艾什比的控制论到卡尔·路德维希·冯·贝塔郎非的一般系统论,最早建立的是系统心理學,系统生态學、系统生理學等先后建立与发展,20世纪70-80年代系统论与生物學、系统生物學等概念发表。从克劳德·香农的信息论到伊利亚·普里高津的耗散结构理论,将生命看作自组织化系统。细胞生物學、生化与分子生物學发展,曼弗雷德·艾根提出细胞、分子水平探讨的理论。 == 尚未解决的生物學基本问题 == 尽管我们近几十年来对于生命的基本过程的认识取得了的深" +221,"另一个基本未解决的问题是老化的生物學基础。目前,没有任何衰老的根本原因共识。各种竞争的理论列在老化#老化理論。 == 生物學主要分支 == 這些是生物學的主要分支: * 動物學領域:動物生理學、解剖學、胚胎學、神經生物學、發育生物學、昆蟲學、動物行為學、組織學。 * 植物學領域:簡單的學門分類可概分為[Bold, H. C. et al. 1987.],p. 2. 此處或有與其他學門重複,且Bold強調,這些學門很難完全地分開,研究者屬於純科學或應用科學,可能會因時間不同而有不同程度的轉變。: ** 應用植物學:農學(或農藝學)、園藝學、花卉學、植物育種學、林學、植物病理學等等。 這些是生物學的主要分支 ** 純科學(之植物學):植物分類學、植物生理學、植物形態學、植物解剖學、植物地理學、遺傳學、生態學、藻類學等等。 * 微生物、醫學和免疫學領域:微生物學、免疫學、血液學、細菌學、病毒學、感染性疾病、毒理學、放射生物學、癌生物學。 * 生物化學領域:生物化學、蛋白質力學、醣類生化學、脂質生化學、代謝生化學。 * 演化和生態學領域:古生物學、演化論、演化生物學、社會生物學、分類學、系統分" +222,"al Code of Botanical Nomenclature (Vienna Code). Regnum Vegetabile 146. A.R.G. Gantner Verlag KG. ISBN 0080-0694 * Bold, H. C. et al. 1987. Morphology of plants and fungi, 5th ed. NY: HarperCollins Publisher. 912 pp. ISBN 978-0-06-040839-8 == 延伸閱讀 == * 吴相钰 (编者)、 陈守良 (编者)、 葛明德 (编者). 2014. 十二五 普通高等教育本科国家级规划教材:陈阅增普通生物學, 第4版. 高等教育出版社. 538页. ISBN 978-7-04-039631-7 * Boujard, Daniel et al. 2012. Biologie cellulaire et moléculaire. Paris: Wiley, 512 pp. ISBN 978-2-10-056425-5 * Lynn Margulis: Five King" +223,"ee/phylogeny.html * 生物杂志(The Journal of Biology) :一個免费的小型研究杂志。 * https://bootinfos12.com.ng/ NCBI 書架 :可以查詢許多生物學書籍。 * DIAM:一个微生物信息的网站。 == 參見 == * 生物学家列表 * 诺贝尔生理学或医学奖、诺贝尔化学奖 * 医学 * 人类学 * 心理学 * 农学 * 進階先修課程 * 植物学 Category:生物學術語 Category:自然史" +224,"心理学是一门研究人类和动物的心理现象、意识和行为的科学。它既是一门理论学科,也是一门应用学科,包括理论心理学与应用心理学两大领域Fernald LD (2008). Psychology: Six perspectives (pp.12–15). Thousand Oaks, CA: Sage Publications.。 心理學研究涉及意识、感覺、知覺、認知、情绪、人格、行為和人際關係等眾多領域,影響其他學科的發展,例如:教育学、管理學、传播学、社会学、经济学、精神病学、统计学、以及文學等等。心理學一方面嘗試用大腦運作來解釋個体基本的行為與心理機能,同時,心理學也嘗試解釋個體心理機能在社會行為與社會動力中的角色。心理學家從事基礎研究的目的是描述、解釋、預測和控制行為。應用心理學家還有第五個目的——提高人類生活的品質。這些目標構成了心理學事業的基礎。 有些領域的心理學家為了研究人類的行為,會對動物進行實驗,以推測環境、生理(如:大腦損傷、運動是否能增加多巴胺等)以及一些特定因素所產生的影響。心理學家也會使用核磁共振、斷層掃描、及發展較成熟的問卷調查等方式來研究人類的心理。有時會有腦部部" +225,"很多闡述和應用。柏拉圖提出過二元並存的理念,有人认为亚里斯多德《论灵魂》是西方最早的一部论述心理学思想的著作。經由長久的演變,慢慢地產生各式各樣不同的學科,包括了現在人所了解的心理學。 哲學心理學主要是在探討心身關係、天性與教養、自由意志與決定論、知識來源等四大議題。其早期的理論有一元論、二元論、環境決定論、精神決定論等。近代的哲學心理學則是有三大思想流派,包括了理性主義、經驗主義與浪漫主義。正如德国心理学家艾宾浩斯所说:“心理学有一个漫長的過去,卻只有一个短暫的歷史”。 在中世纪的伊斯兰医学与心理学中,已经开始进行临床的精神科治疗Ibrahim B. Syed PhD, Islamic Medicine: 1000 years ahead of its times ,Journal of the Islamic Medical Association, 2002 (2), p. 2-9.和实验研究Ibrahim B. Syed PhD, Islamic Medicine: 1000 years ahead of its times , Journal of the Islamic M" +226,"锡大学建立世界第一个专门的心理实验室,吸引了來自世界各地的學生。10餘年後,也就是1890年代,美國各大學風起雲湧地仿傚此舉,建立數十座心理學實驗室,尤其1892這一年就成立了20座,這一年也是美國心理學會的成立年。通常将1874年作为科学心理学诞生的时间,这一时间比许多自然科学脱离哲学而形成独立学科的时间为晚。冯特也是第一个把自己称为心理学家的人。其他早期而重要的心理學家包括艾賓浩斯等。19世纪末、20世纪初,西格蒙德·弗洛伊德的精神分析学派兴起。 === 构造主义 === 德国心理学家威廉·冯特被认为是将实验引入心理学研究的人,被誉为“实验心理学之父”。1879年,他在莱比锡大学建立了第一个心理学实验室。Stanford Encyclopedia of Philosophy. (2006). Wilhelm Maximilian Wundt .冯特着重于将心理分解成为基本的元素,这是由于当时化学的进步,后者在分析物质元素与结构上取得突破。虽然冯特自己不是构造主义者,他的学生愛德華·B·鐵欽納成为了构造主义的思想家,与机能主义者对立。鐵欽納是美国早期心理学家的重要人物。 === 机能" +227,"”,并将其应用与人类。自二十世纪50年代起,冯特、詹姆士、艾宾浩斯与其他实验心理学家们所发展的实验技术方向更加趋向研究認知——关注信息以及信息处理——最终演化为认知科学中的一部分。在早些时候,这被视为“革命性”的发展Mandler, G. (2007). A history of modern experimental psychology: From James and Wundt to cognitive science. Cambridge, MA: MIT Press. === 精神分析 Psychoanalysis === 奥地利医生西格蒙德·弗洛伊德自十九世纪90年代开始研究精神分析学,直到自己1939年去世为止。精神分析学是一种研究意识、解释经历的方法;是一种关于人类行为的理论系统;是一种针对心理、情绪的治疗方法,特别是对潜意识中的冲突进行心理治疗。Moore, B.E.; Fine, B.D. (1968), A Glossary of Psychoanalytic Terms and Concepts, Amer Psychoanalytic Assn, p. 78," +228,"科学哲学家,认为精神分析作为科学是错误的,Karl Popper, Conjectures and Refutations, London: Routledge and Keagan Paul, 1963, pp. 33–39; from Theodore Schick, ed., Readings in the Philosophy of Science, Mountain View, CA: Mayfield Publishing Company, 2000, pp. 9–13. Faculty.washington.edu ,而艾森克则称精神分析原理与实验数据不符。到二十世纪末,美国高等院校心理学院变得更加倾向于实证主义,将弗洛伊德理论边缘化,并蔑称其为“干瘪老死”的历史古董。 June 2008 study by the , as reported in the New York Times, Freud Is Widely Taught at Universities, Except in the Psychology Department by Patricia Cohen" +229,"Eric Kandel; Joseph E. LeDoux (LeDoux, J.E. (1998). The emotional brain: The mysterious underpinnings of emotional life (Touchstone ed.). Simon & Schuster. Original work published 1996. ISBN 978-0-684-83659-1); (Panksepp, J. (1998). Affective neuroscience: The foundations of human and animal emotions. New York and Oxford: Oxford University Press); Oliver Sacks (Sacks, O. (1984). A leg to stand on. New York: Summit Books/Simon and Schuster); (Kaplan-Solms, K., & Solms, M. (2000). Clinical studies" +230,"为之前出现、而且为一个机体产生反应或抑制反应提供活动场所的条件。因此,他们把行为反应看做是机体理解、预测和控制的行为,并因此做出相应的结果。基于对实验和变量的严格控制和强调,行为主义学对后来的心理学研究有着重要的影响。其代表人物為史金納,然而其對於內在的認知歷程是存而不論,近期流行的認知主義則是重視之前忽視的內在認知歷程,代表人物有皮亞杰、米勒、西蒙等。 当今世界主要的心理学学派有五大观点,每一个观点都强调行为及心理过程的不同方面。其主要包括神经心理学、精神分析学、行为主义心理学、人本主义心理学、认知心理学。在《心理学与生活》(Psychology and Life)中,当代心理学的主导观点还包括生物学观点,进化论观点和文化观点。(p9,p11) === 人本主义 Humanism === 二十世纪50年代,在行为主义与精神分析的影响下,人本心理学应育而生。应用现象学、交互主体性、第一人称视角,人本主义试图捕捉整个人——而不是人格或认知的一部分。Rowan, John. (2001). Ordinary Ecstasy: The Dialectics of Humanistic Psy" +231,"义心理学不同于其它人本学派,即人性中立视角、对焦虑的积极观点等。存在主义心理学注重于人文主题,如死亡、自由意志、意义,并认为意义可以被神话、或是讲述方式改变。对于死亡及未来的真实性,通常会鼓励人们接受自由意志,尽管令人焦虑。 奥地利存在主义精神病医生、大屠杀幸存者维克多·弗兰克从集中营中吸取教训,总结出不同版本的存在主义治疗法,这种方法着重于存在主义,不同于阿德勒的尼采主义的权力意志或弗洛伊德的快乐原则。Seidner, Stanley S. (June 10, 2009) A Trojan Horse: Logotherapeutic Transcendence and its Secular Implications for Theology . Mater Dei Institute. p 2. 瑞士精神分析学家路德维希·宾斯万格,美国心理学家喬治·亞歷山大·凱利和梅(May)、弗兰克尔(Frankl)一道,也被认为是存在主义学派学者。 === 认知主义 === 认知主义心理学派研究心理活动,包括解决问题、知觉、記憶、学习等。作为认知科学的一部分,这个心理学分支与其它学科有密切联" +232,"特·艾利斯和精神病学家亚伦·T·贝克创立而成。认知心理学与其它学科,如精神哲学、计算机科学、神经科学,一道被归入的上层学科认知科学。 == 分支 == === 生物 === 生物心理学或行为神经科学从生物角度研究行为和心理过程。生物心理学中有不同的专业分支。例如,生理心理学运用动物模型,特别是老鼠,来研究神经、基因、细胞机能在学习、记忆、恐惧中产生的作用。神经心理学家利用神经成像工具研究神经与人类心理活动之间的关系,認知神经科学家使用心理测评来进行科学研究,如大脑损伤带来认知缺失的程度与表现形式。 === 临床 === 临床心理学的研究与应用包括理解、预防、缓解心理痛苦与紊乱,促进心理健康和个人成长。虽然临床心理学家也会参与研究、教学、咨询、出庭作证、程序编订与管理,但该分支的中心是心理测评与治疗。Brain, Christine. (2002). Advanced psychology: applications, issues and perspectives. Cheltenham: Nelson Thornes. ISBN 978-0-17-490058-0一些临床心理学家会着" +233,"of confluence and the Charybdis of confusion. In Handbook of Counselling Psychology (R. Woolfe & W.L. Dryden, eds.). London: Sage, pp. 258–83. ISBN 978-0-8039-8991-7 === 认知 === 绿 红 蓝 紫 蓝 紫 ---- 蓝 紫 红 绿 紫 绿 ---- 斯特鲁普效应指出读出文字的颜色,第一组的要比第二组的简单很多。 認知心理學研究心理活動中的認知。知覺、注意、理智、思維、解題、記憶、學習、語言、情緒都在它的研究領域之中。經典認知心理學與認知主義學派有相互聯繫,根據機能主義與實驗心理學,認知主義支持心理信息處理模型。 在更廣域的層面上,認知科學是一種跨學科範疇,包括認知心理學、認知神經科學、人工智能、語言學、人機互動、計算神經科學、數理邏輯與人類學。計算機常被用於模擬這類實驗現象。 計算機模擬為研究思維的功能組織提供了工具,神經系統科學則為大腦活動提供了度量衡。 === 比较 === 比较心理学指对非人类动物的科学研究,特" +234,"的方式呈现,既好玩,又有科学意义;研究人员甚至设计出非常灵巧的方式以研究婴儿的心理活动。在研究儿童心理之余,发展心理学家也会研究人类毕生的老化过程,特别是在某些时段的快速转变(例如青少年和老年)。发展心理学家设计了一整套心理学理论来支持他们的研究。 === 教育 === 教育心理学是研究人类在教育过程中的学习、效率、授课心理以及学校作为一个组织的社会心理。儿童心理学家,如利維·維谷斯基、讓·皮亞傑、杰罗姆·布鲁纳、伯纳德·罗斯金(Bernard Luskins)等人对教授方式与教学实践都有重要影响。在许多国家里,教育心理学都是教师的必修课。 結合了教育心理学和临床心理学,试图去理解並应付有学习障碍的学生;培育资优生;促进青春期行为;或是提倡安全、互助、高效的教育环境。学校心理学家在许多领域,如教育行为评估、干预、预防、咨询等都有了解、有的在研究领域颇有造诣。 === 演化 === 演化心理学从现代演化观点来研究心理的特质理论——例如记忆、知觉、语言等。它试图去探究是何种人类心理特征在适应进化,即,作为自然选择和性选择的功能产品。演化心理学家认为在人类先祖生活的环境下,心理适应的演化解决" +235,"论则尝试使用离散统计数据来进行研究,所提出的特质种类也大有千秋。早期的汉斯·艾森克模型提出人格由三种基本特质组成:外向性与内向性、神经质、。雷蒙德·卡特尔则提出了16条人格特征。如今,人格维度模型受到越来越多的重视,例如。目前集大成的為社會認知學派,其實最初稱為社會學習論,是從行為學派作為延伸:人格的形塑就是人與人、環境的互動所學習到的行為,而後美國心理學家班杜拉從中把行為學派的理論更加擴大,加入了人的內在活動─認知成分,形成了社會認知理論。 === 社会 === 社会心理学研究人类彼此之间的看法以及它们是如何产生联系的,社会心理学家研究课题包括他人对个体行为的影响(例如:从众、說服)、信仰的建立、态度、或更大層面來說,不只人與人之間的互動,也有人與一個群體的互動等,就像刻板印象的建立與強化,會引起之後的歧視行為,就是一個經典的例子。社会认知将认知心理学与社会学的元素联系在一起,试图理解人们如何处理、记忆、扭曲社会信息。群体动力学研究揭示领导能力、交流及其他的本质及其优化潜质。近些年来,许多社会心理学家对內隱態度、、个人与社会价值互动所致行为倍感兴趣。因此,对人类社会的研究可以发现导致" +236,"缺失者有更好的效果。 === 情感 === 情感心理学(Psychology Of Emotion)是心理學中研究情緒的分支,主要研究如何消除紧张和压力的情绪,恢复健康健全的人格。 == 研究方法 == 心理学研究倾向于折衷主義,即从其他领域汲取知识以帮助理解、解释心理现象。另外,心理学家大量使用了查尔斯·桑德斯·皮尔士的三段论:演绎、归纳、溯因。由于常常使用,心理学家也会依靠归纳逻辑来对问题进行解释。例如,演化心理學家尝试利用适者生存来解释心理特质——如记忆、知觉、语言等,即这些功能是自然选择或性选择的产物。 心理学家会展开基础研究来进一步理解心理学的某一领域,或是进行应用研究来解决临床、工作场所等其它问题。硕士级别的临床专业会同时教授学生研究方式与循证实践。心理学专业协会也会协助设立规范标准,如道德、培训、研究方式、专业操作等。另外,根据不同的国家和地区,心理服务或“心理学家”这一职称受到相应法律的规范,从事对外服务的心理学家也常常被要求取得从业执照。 === 实验法 === 根據理論或觀察作出假設,在控制實驗情境中各種情況下操縱自變量觀察因變量的結果。對結果進行統計分析,控制無關" +237,"物研究 === 动物学习实验对于研究人类心理学有很重要的辅助作用,例如知觉、感情、学习、记忆等等。十九世纪90年代,俄罗斯心理学家伊万·彼得罗维奇·巴甫洛夫利用狗的实验来展示经典条件反射。非人类动物如灵长类、猫、狗、鸽子、鼠以及其它啮齿类动物常常被应用于心理实验中,特別是行為心理學的研究。理想状态中,实验控制组一次只给出一个自变量,以显明它对因变量的作用。这些在实验室里可以得到最为近似的结果。与之相反,人类的生存环境与基因环境差异太大,变量太多,以至于在人群中难以控制重要的变量数据。当然,把动物实验的结果往人类身上生搬硬套是明显不可取的。动物实验会受相关約束,需要遵守心理學會所定的道德原則。 === 定性与定量研究 === 心理学绝大多数的领域遵循标准的科学方法进行研究。心理研究员尝试使用最新理论和假设来对数据、质与量进行研究。 心理質性研究方式包括訪談法、觀察法等。克雷斯維爾(Creswell,2003)定義了五項主要的質性研究,包括敘述、現象學、民族誌、案例分析和基礎理論。質性研究員Glaser, B. & Strauss, A. (1967). The discovery of" +238,"须保证遵循严格的道德准则,不能做出有悖伦理的尝试。 === 调查问卷 === 心理学上,统计调查可被用来度量态度、特质,监视情绪的改变,检查试验的效度,以及其它相关研究项目等等。心理统计员可以使用传统纸张、电话、电子邮件、或是目前流行的网络等,围绕主题制定好问卷,按一定比例进行人群调查、随机调查、重点调查、抽样调查。类似的调查方式也被用于其它应用场所,如临床评估、人事评估等。 === 发展心理学的研究設計 === 主要分為三種 *橫向設計(cross-sectional design):在同一時間下調查不同年齡層的受試者在某個特質或是行為上的表現。 :優點:省時省力,較縱貫研究省時且能較快速取得研究結果,沒有樣本流失的問題。 :缺點:存在同辈效应(cohort effect),也即年龄差异不完全由年齡变化所导致,有可能也受到了外在環境,例如不同年代群体的不同生活經驗的影響。 *纵向设计(longitudinal design):在不同時間追蹤同一群被试在某特質或行為上的表現。 :優點:較能避免同辈效應,也即不會受到不同年代群体间存在的差異的影響,可以从中了解到个体发展的数据。 :缺點:" +239,"学家观察心理事件的假设值,这在平时活动中是不易观察到的。 应用过程中有许多不同的模型可供选择。联结主义利用神经网络来模拟大脑。另一种方式是形象模拟,利用变量和规则模拟不同的心理活动。其它种类的模型包括动力系统和随机过程等。 档案研究法:是指依据一定目的收集大量现有资料,通过分析找出某些现象与心理之间关系的一种度.档案研究法有广义和狭义之分,广义的档案研究法除了传统的参考资料还包括视听影像资料,狭义的档案资料包括报纸、杂志、会议记录、政府公文、个人书信等文字材料。档案研究法的使用能弥补其他一些研究方法的不足,但有其缺点,需要视情况而定。 == 批评 == === 理论 === 对研究的批评常常包括指责心理学是“软”科学。科学哲学家托马斯·库恩的1962批判T.S. Kuhn, The Structure of Scientific Revolutions, 1st. ed., Chicago: Univ. of Chicago Pr., 1962.中暗示心理学仍处在前范式形态,缺乏像成熟的化学、物理学那样的支撑性理论。 由于心理学的一些领域依赖于问卷调查之类的研究方式,批评家们认为心理学" +240,"著性差异与实践之间的关系,而前者在实践上常常无关紧要。這種辩论時長发生在心理学界内部,例如实验室研究人员与从事实践的临床医生之间的争执等。 === 实践 === 有的观察者发现了某些理论与实践上的冲突——例如,一些临床实践没有理论支持。批评家们认为越来越多的心理培训课程没有足够的科学依据。Beyerstein, B.L. (2001). Fringe psychotherapies: The public at risk. The Scientific Re-view of Alternative Medicine, 5, 70–79一些质疑者认为,像“婴儿孤独症協助性溝通”;被用于记忆恢复的塑身锻炼;再生疗法等,虽然十分风行,但却令人怀疑,而且可能有害健康。 === 道德 === 在今天的道德准则框架内,一些研究实验不允许进行。这些实验违反了美国心理学会道德准则(Ethics Code of the American Psychological Association)、加拿大涉及人类实验道德准则(the Canadian Code of Conduct for Research I" +241,"代,哈里·哈洛在威斯康星大学麦迪逊分校利用猕猴进行了“绝望”实验,遭到谴责。Blum 1994, p. 95, Blum 2002, pp. 218–19. Blum 1994, p. 95: ... the most controversial experiment to come out of the Wisconsin laboratory, a device that Harlow insisted on calling the 'pit of despair.实验利用动物来模拟抑郁症。哈洛设计出了一种“强暴架”,利用对母猕猴行为的干扰进行研究。Blum, Deborah. Love at Goon Park: Harry Harlow and the Science of Affection. Perseus Publishing, 2002. ISBN 978-0-7382-0278-51974年,美国文学评论家写道:“哈里·哈洛跟他同事几十年折磨着非人类灵长实验品,证明了我们都早已知道的——社交生物会因社交纽带的损害而被损害。”他写道哈洛对他研究的伦理道德没有任何评论。B" +242,"员会,以保障研究对象的权益与尊重。心理研究员在进行实验之前必须获得准许,以保护人类参与对象或实验动物的权益。The American Psychological Society: Responsible Conduct of Research === WEIRD偏差 === 1959年,统计学家西奥多·斯特林(Theodore Sterling)计算發现97%的心理实验支持了它们最初的假设,暗示了偏差存在的可能。与之类似,贾内尔(Fanelli, 2010)发现91.5%的精神/心理研究得到了证明,所得出的问题(阳性结果)是空间或地球科学的5倍大。贾内尔认为较“软”科学的研究人员就他们的意识与潜意识中的偏差缺乏束缚。 2010年,一组研究人员公布了心理研究中的系统性实验对象偏差WEIRD (「西方地区、受过良好教育、工业化的、富有、民主的」; Western, Educated, Industrialized, Rich and Democratic )。The WEIRDest people in the world? Henrich, J., Heine, S., & Norenz" +243,"从20世纪里重点关注快乐和满足的人本主义心理学中找到根源。 * 比较心理学:是研究动物行为进化的基本理论,和不同进化水平的动物的各种行为特点的心理学分支。它对哲学认识论具有重要意义,比较心理学的研究还能为生产实践和生物医学研究提供动物行为进化方面的基础科学知识。 * 发展心理学:主要研究人類由出生至死亡時的人格發展過程,艾瑞克森把人格發展分為8個階段,並認為每個階段的人都會面對不同的人格發展的障礙,如處理得好便能順利發展,如處理不好便會形成焦慮及各樣問題。 * 人格心理学:研究行为、思维和情绪的模式,通常称为个体的人格。 * 定量心理学:在心理学研究中应用数学模型和统计学模型,以及将统计学方法用于分析、解释行为数据。 * 心理统计学:是心理学中对知识、能力、态度和人格特征等进行心理测量的理论和技术,主要与教育、人格和临床心理学有关。数学心理学则与实验和认知、生理心理学关系密切。 * 社会心理学:探討社會與認知歷程對於個體知覺他人,影響他人及聯繫他人的方式如何產生作用的科學性研究,以心理学、社会学相关理论为基础的社会科学。 * 臨床心理学:是讨论人類非正常行為,通过临床观察探討這些行為的" +244,"要關注音樂與人的各種心理現象的相互關係,解釋人由原始(初生)到高級的音樂經驗和音樂行為的心理學。 == 参见 == * 心理学家 * 精神病学 * 病理学 * 社會心理學 * 中国心理学会 (CPS) * 美国心理学会 (APA) == 參考文献 == ==研究書目== *李紹崑:《精神學研究》(台北:台灣商務印書館,1998)。 *李紹崑:《哲學.心理.教育》(台北:台灣商務印書館,1993)。 *李紹崑:《美國的心理學界》(台北:台灣商務印書館,1991)。 *李紹崑:《歐洲的心理學界》(北京:商務印書館,2007)。 *張春興:《現代心理學——現代人研究自身問題的科學》(台北:東華書局,1991)。 *朱光潛:《文藝心理學》(北京:中國人民大學出版社,1998)。 == 外部链接 == * 心理學研究 * 心理学百科全书 * 美国心理学史档案 * 心理学史上的名著 * 中国心理学会 * 美国心理学会 * 台灣心理學會 *香港心理學會 P P P P" +245,"中醫學,全稱漢族醫药學,俗称“中医”中医学 [M/OL]//陈至立. 辞海. 7版. 上海:上海辞书出版社,2020[2024].,是一種起源於中國漢族且至今已有千年歷史的傳統醫學。與現代醫學相比較时,其常被歸入傳統醫學的範疇。因此,現今世界上的中醫學的发展目标,多是追求自身的科學化和現代化。 目前中醫以中華文化圈傳統醫療為主流,按照中華人民共和国全国科学技术名词审定委员会审定的名词,中医学是「以中医药理论与实践经验为主体,研究人类生命活动中健康与疾病转化规律及其预防、诊断、治疗、康复和保健的综合性科学」。 == 定義 == 220px|缩略图|中醫穴道 *廣義:是指所有的中華民族醫學、宗教醫學、中國化西洋醫學,如漢醫、藏医、蒙医、苗医、彝医、维医、哈萨克医、佛医等。 *狹義:全世界的漢族醫學,簡稱漢醫學。 ===華人圈共通的中醫=== 中醫學以阴阳五行作为理论基础,将人体看成是气、形、神的统一体,通过望、闻、问、切,四诊合參的方法,探求病因、病性、病位、分析病機及人体内五臟六腑、经络關節、氣血津液的变化、判斷邪正消長,進而得出病名,歸納出證型,以辨證論治原則,制定“汗、吐、下、和、溫" +246,"局(2013年卫生部撤销后转隶卫计委。2018年衛計委撤銷後再轉隸衛健委)。 为继承和弘扬中医药,保障和促进中医药事业发展,保护人民健康,经十二届全国人大常委第二十五次会议通过了《中华人民共和国中医药法》,于2017年7月1日起实施。 ===香港=== 中医在港英政府时期无合法行医地位,但能以“Herbalist(草药师)”的名义注册并进行活动。中医医生还可使用“中医”、“中医师”、“中医生”、“国医”、“唐医”等名称,不能使用“医生”、“医师”、“医务所”、“医疗所”等属于行医范围的名称,因此中医师无权签署死亡证,也不能在处方上开任何西药,更不能使用注射;否则被认为是违法行为。 1999年,香港政府根據《中醫藥條例》,成立香港中醫藥管理委員會,用作規管中藥買賣和中醫師的專業水平,以保障公眾健康和消費者權益。根據《中醫藥條例》,所有在香港執業的中醫師,必須向中醫藥管理委員會註冊。在中醫師之前註冊制度生效之前,除了提供全科診治及開方,以「中醫」掛名應診的從業員之外,還有後來被納入中醫註冊制度,當時俗稱為「跌打師傅」、「跌打佬」的從業員。其起源沒有一個統一說法,有一說法是與上世紀香港盛行的" +247,"。中醫師能夠獨立進行診斷、開立處方簽、調劑中藥等。另外並訂有《中醫藥發展法》、《臺灣中藥典》等,同時設置衛生福利部國家中醫藥研究所進行中醫藥研究。 ===美國=== 美國目前有大約三萬多名執照針灸師(部份州份稱為針灸醫師或東方醫學醫師),五千多名西醫師另外獲得應用針灸執照。美國有嚴格的針灸執照發放制度,但由於美國實施聯邦制,各個州份均各自有當地的法律,監督針灸師執照的發放。因此,各個州份的執照發放的要求都不一樣,但一般都要求申請人在認可的針灸或中醫學校學習針灸的相關課程,由1,700到4,000學時不等,以及一般要求擁有950小時的臨床實習經驗,方可獲允許參加針灸執照考試。另外,各個州份一般都要求針灸執照的持有者,於每年或每兩年為其針灸執照續期,否則其針灸執照將會被取消。針灸執照續期的手續,除了包括更新資料外,還要求針灸師提供持續進修的證據,一般都要每年數十個小時的專業持續進修課程,方可獲得續期。 在美國大部份州份,沒有持有有效執照,而又宣稱為專業針灸師,又或者提供中醫治療,是刑事罪行。違例者會面對監禁和罰款。 ;針灸師與中醫師 直到2012年,美國有六個州份沒有法律監管中醫,一般稱為" +248,"ight|200px|中醫穴道圖表 === 古代中医史 === 中医学目前是否为最早的传统医学还无法确定。中国的中医学自称其起源于三皇五帝时期,相传伏羲发明了针灸并尝试草药,神农炎帝更是尝尽百草,并且用茶来解毒。中医研究者自称:相传在公元前3000多年,中国的轩辕黄帝写下了人类第一部医学著作——《黄帝祝由科》(然而公元前1600~前1100年的甲骨文才被认为是中国已发现的时代最早的文字),“在公元前3000多年可以使用文字并组织成大篇幅的医学著作”在目前与无凭无据的中医典籍相比,这种说法是与事实不符的。公元前3000左右的美索不达米亚医学已用楔形文字记载了肝和脏是血液的中心,疾病分为热病、中风、精神症状、眼病、耳病、黄疸病等。在中国, 后世人在《黄帝祝由科》的基础上不断增补删改,逐渐形成了后世的《黄帝内经》和《黄帝外经》,并從祝由科裡将纯粹的医药分离了出来,形成了后来的中医学。而其中的《黄帝内经》则在世界上第一个提出了“不治已病治未病”这一防病养生保健康的预防医学观点。 在周代(公元前1046年-公元前771年)中国就建立了世界上第一个医院和医疗制度,周代的医疗机构设有医师、上士、下士" +249,"不相信医道者不治。后世则尊称他为神医扁鹊。春秋战国时流行的主要医学著作有《黄帝内经》、《黄帝外经》、《扁鹊内经》、《扁鹊外经》、《白氏内经》、《白氏外经》和《旁篇》这七本,合成“七经”。 在秦朝出现了世界上最早的专门法医—— 令史 。秦律规定,死因不明的案件原则上都要进行尸体检验,司法官如果违法不进行检验,将受到处罚。秦代的《封诊式》对法医鉴定的方法、程序等有较为详细的记载。在人命案件中,鉴定检验的主要内容有尸体的位置、创伤的部位、数量、方向以及大小等。令史检验完成之后,必须提交书面报告,称为“爰书”,是世界上现存最早的法医鉴定和现场勘察报告。秦代还在世界上第一个建立传染病医院——“疠迁所”,并制定了最早的治疗传染病的隔离制度。据1975年湖北省云梦睡虎地出土秦简中记载:当时规定,凡经医生在给病人检查后发现有鼻梁塌陷、手上无汗毛、声音沙哑、刺激鼻腔不打喷嚏等症状者,一律送至疠迁所隔离治疗。这说明中国古代对传染性疾病的治疗措施,很早就已经是得力有效的。 到了西汉时期,中医的阴阳五行理论已经非常完备,名医则有太仓公淳于意和公乘阳庆。东汉出现了著名医学家张仲景和华佗。张仲景完善了中医的辨证理" +250,"太医令秦承祖创建了世界上第一个医学院。到了公元6世纪,隋朝完善了这一医学教育机构,并命名为“太医署”,署内分医、药两部,太医令是最高官职,丞为之助理,下有主药、医师、药园师、医博士、助教、按摩博士、祝禁博士,在校师生最多时达580人之多。 北宋年間,隨着中國與阿拉伯之間貿易的高度發展,阿拉伯香藥也開始大量輸進中國,一次輸入的阿拉伯乳香竟以萬斤計,伊斯蘭醫藥學也隨之對中國產生明顯影響。傳統中醫自古不分科,但在北宋年間,太醫院在培訓醫生時也如古希臘和阿拉伯醫學那樣,分成了內、外、婦、兒、口腔等十三科,這可能是受到阿拉伯-伊斯蘭醫學的橫向影響。傳統中醫以湯藥為主,但北宋的《太平聖惠方》、《聖濟總錄》卻大量增加了丸、散、膏、酊的處方,如《乳香圓》、《阿魏圓》等。究其原因,乃是使用從阿拉伯-伊斯蘭國家進口的香藥大量增加,而香藥含有揮發性物質,若用煎湯法,其有效成分就會失掉,故只能製成丸、散、膏、酊。可見這類處方的出現,是阿拉伯-伊斯蘭藥理學對中醫藥影響的結果。 在唐朝,孙思邈总结前人的理论并总结经验,收集药方多达5000多个,出版了《大医精诚》、《千金要方》和《千金翼方》三本医学著作,后世尊称他" +251,"珍的医学巨著《本草纲目》成书,这本书不仅是药物学专著,还包括植物学、动物学、矿物学、化学等方面的知识。《本草纲目》刊行后很快传入日本、朝鲜及越南等亚洲地区,在公元17、18世纪先后被翻译成多种欧洲语言。另一方面,李时珍是世界上第一个提出大脑负责精神感觉、发现胆结石病、利用冰敷替高热病人降温以及发明消毒技术的医学家。此外还有王叔和的《脉经》、皇甫谧的《针灸甲乙经》、陶弘景的《本草经集注》、葛洪的《肘后备急方》、巢元方的《诸病源候论》、苏敬的《新修本草》、王焘的《外台秘要》、元丹贡布的《四部医典》、《太平圣惠方》、王惟一的《铜人腧穴针灸图经》等大量医学典籍问世。自明朝中医发展已经达到了顶峰,出现了诸多的医学流派。同时在朝鲜研究中医的所谓東醫學也得到了很大的发展,例如許浚撰寫了《東醫寶鑑》。 清初至鼎盛時期,醫藥學進步所表現在很多方面,基本上是明朝醫藥盛況的延續。如對經典著作的研究、本草學、方劑學、診斷治療學、醫案整理等,均較明朝更成熟。各家學派的紛爭也逐漸緩和,大多醫家能採各家之長折衷於臨床。但也不乏固守《內經》、《難經》、《傷寒論》,而批評金元以後一切新說的醫學,這與當時考據學盛行不無關" +252,"Efficacy, and Practice ACADEMIA AND CLINIC COMPLEMENTARY AND ALTERNATIVE MEDICINE SERIES 5 March 2002 Volume 136 Issue 5 Pages 374-383。世界卫生组织(簡稱:WHO)认为,只有部份的傳統藥學包含针灸和草药在臨床實驗裡得到比較有力的結果支持其功效,其他傳統療程與草藥仍需進一步的研究,不能忽视传统疗法存在的安全性及危险性的風險,乃至於偷獵保育類藥材,而且生物體內的麻醉劑、毒素積累近年來被逐漸發現,被證明很有可能弄巧成拙,以至於致人於死的案例多次上演,也因為本身就是地下經濟,存在冒充假藥獲取暴利等问题。WHO在2002年5月26日發表「2002-2005年傳統醫藥研究全球策略」,邀請全球180餘國將替代醫學納入該國的醫療政策。 WHO世界衞生大會將於2019年推出第11版全球醫學綱要,並首次將中醫傳統醫學納入其中。此舉意味着中醫在全球或躋身主流療法,而非僅僅作為手術、放射治療等準備療法之外的補充療法。 全球醫學綱要又稱為「國際疾病與相關健康問題統計分類」(ICD" +253,"位小前,故祭先脾;秋位小郤,故祭先肝。腎也,脾也,俱在鬲下;肺也,心也,肝也,俱在鬲上。祭者必三,故有先後焉,不得同五行之氣。今醫病之法,以肝為木,心為火,脾為土,肺為金,腎為水,則有瘳也。若反其術,不死為劇。(《禮記》十三《月令》正義)”古文尚書由伏生所傳,而今文尚書由歐陽和伯所傳。臨床實驗證明,湯液經法家理論與人體的體液味道是一致的。 這證明了現今流行的中醫理論基礎實際上是晚出的今文經學的著作,而五行相剋說是戰國時期出現於齊國,而五行相生說是劉向時期出現,所以完善的黃帝內經完成時間絕不可能早於漢宣帝時期。後來採用湯液經法的稱為神仙家,採用今文尚書的稱為醫經家或稱為針炙家,利用今文尚書與湯液經法雜煣的稱為經方家。 以下是三個不同時期中醫理論的對比: *湯液經家:脾(酸)金、肺(辛)木、肝(苦)水、腎(鹹)火 *今文經家:肝(酸)木、心(苦)火、脾(甘)土、肺(辛)金、腎(鹹)水 *古文經家:脾(酸)木、肺(苦)火、心(甘)土、肝(辛)金、腎(鹹)水 在《湯液經法》原始中醫理論之中,是採用殷商時期的《龜書》八卦與《龍圖》八卦一共是十六卦的學說。其中和陰陽的地方是相反的。在《湯液經法》或" +254,"”。人的生命活动规律以及疾病的发生等都与自然界的各种变化(如季节气候、地区方域、昼夜晨昏等)息息相关,人们所处的自然环境不同及人对自然环境的适应程度不同,其体质特征和发病规律亦有所区别。因此在诊断、治疗同一种疾病时,多注重因时、因地、因人制宜,并非千篇一律。 # 认为人体各个组织、器官共处于一个统一体中,不论在生理上还是在病理上都是互相联系、互相影响的。因而从不孤立地看待某一生理或病理现象,头痛医头,脚痛医脚,而多从整体的角度来对待疾病的治疗与预防,特別强调“整体观”。 ==== 精炁(氣)學說 ==== * 炁(氣)是構成天地萬物的原始物質。 * 氣的運動稱為“氣機”,有“升降出入”四種形式。由運動而產生的各種變化,稱為“氣化”,如動物的“生長壯老已”,植物的“生長化收藏”。 * 氣是天地萬物之間的中介,使之得以交感相應。如:“人與天地相參,與日月相應”。 * 天地之精氣化生為人。 ==== 陰陽學說 ==== * 陰陽是宇宙中相互關聯的事物或現象對立雙方屬性的概括。最初是指日光的向背,向日光為陽,背日光為陰。 * 陰為“體”,陽為“用”;陰為有形之“體”,陽為無形之“用”。 * 陰" +255,"化,以測知內在“臟”的生理病理狀態,稱為“從象測臟”。即“視其外應,以知其內臟”。 * “臟腑”不單是解剖型態的概念,而是包括解剖、生理、病理在內的綜合概念。 * 五臟:指肝、心、脾、肺、腎,一般笼统功能為“化生和儲藏精氣”。 * 六腑:指膽、胃、大腸、小腸、膀胱、三焦,一般笼统功能為“腐熟水榖、分清泌濁、傳化糟粕”。 * 奇恒之府:指“腦、髓、骨、脈、膽、女子胞”。 ==== 氣血津液 ==== * 氣、血、津液是構成和維持人體生命活動的基本物質 * 氣的生成源自先天與後天。稟受於父母的精氣,稱為“先天之氣”。肺吸入自然的清氣,與脾胃運化水榖產生的水榖之氣,合稱為“後天之氣”。 * 氣有推動、溫煦、防禦、固攝、氣化、營養等作用。 * 人體的氣可分為元氣、宗氣、營氣、衛氣、臟腑之氣、經絡之氣。 * 氣的“升降出入”運動失常,稱為“氣機不調”。其表現形式有氣滯、氣鬱、氣逆、氣陷、氣脫、氣閉等。 ==== 經絡學說 ==== * 經絡是人體運行氣血、聯絡臟腑形體官竅、溝通上下內外的通道。 * 經絡系統包括十二經脈、十二經別、奇經八脈、十五別絡、浮絡、孫絡、十二經筋、十二皮部等。 * 經絡在" +256,"===== * 病機是疾病發生、發展與傳變的機理,又稱“病理”。 * 基本病機包括:邪正盛衰、陰陽失調、氣血失常、津液代謝失常 * 內生五邪,包括:風氣內動、寒從中生、濕濁內生、津傷化燥、火熱內生 * 病位傳變,包括:表裡出入、六經傳變、三焦傳變、衛氣營血傳變、臟腑傳變等 * 病性轉化,包括:寒熱轉化、虛實轉化等 === 《湯液經法》學派基礎理論 === 《湯液經法》學派是自殷商以來傳承的中醫學派,经方派代表。這一套理論與儒家派生的今文經學派有相當大的差別。例如《湯液經法》學派之中沒有五行理論,採用南島民族祖宗的「連山」「歸藏」十六卦學說,而且當中陰陽是與今文學派顛倒的。以下的理論還原是出自香港歷史學家陳振銘「由《輔行訣臟腑用藥法要》到香港當代新經學」。陳振銘,《由《輔行訣臟腑用藥法要》到香港當代新經學》,亞馬遜, ====陰陽學說==== 《湯液經法》學派以「立竿測影」之法,定日影之長為陽,日影之短為陰。 推導出: 立春為少陰之陰巽,春分為少陰之陽坎。 立夏為太陰之陽艮,夏至為太陰之陰坤。 立秋為少陽之陽震,秋分為少陽之陰離。 立冬為太陽之陰兌,冬至為太陽之陽乾。 ====寒熱學說=" +257,"得出结论,并在此基础上确定治疗原则与具体治法。这种方法既不同于简单的局部对症治疗,又有别于不分主次、不分阶段、不分性质、一方一药治一病的辨病治疗方法。 == 治療方法 == 治疗手段和方法丰富多采,既有一般内服药物的内治法,又有药物外敷、热熨、熏洗等外治法,以及独特的针灸、拔罐、推拿、按摩、气功、布气等非药物疗法,还有以药膳为代表的日常食品疗法。 一般而言,完整的中醫處方,至少包括四項以上:情治(情緒控制)方面的指導;忌口(飲食應注意的事項);運動的建議(游泳、跑步、導引、氣功等);中藥的使用,或針刺、艾灸(前二者统称针灸)、推拿等。故中醫在治療疾病時,是以身心合一的角度全盤考量。 近年来还涌现出了一批传统药物注射制剂,被用于肌肉和静脉注射。此外,绝大多数药物均为天然动、植、矿物,非人工合成之化学品,加之多为复方制剂,配方时可采用相应的拮抗措施。 相較於傳統中醫,現代中醫的革新發明例如:小針刀、传统药物注射制剂、科學中藥等。 == 学科分类 == 自周朝起有食醫、疾醫、瘍醫、獸醫之分。隋唐的太醫署分醫學及藥學,醫學設四科,即醫科、針科、按摩科、咒禁科;醫科又分體療、少小、瘡瘍、耳目口齒" +258,"医耳鼻咽喉科、中西医结合医学、气功科、中医护理等。 == 中醫學典籍 == :參見:中醫典籍 中醫典籍可分為醫經、醫論、本草、醫方、醫案、醫話等,例如《黄帝内经》、《難经》、《伤寒论》、《金匱要略》、《神农本草经》、《備急千金要方》、《本草纲目》、《名醫類案》。 == 流派及医家 == 《漢書·藝文志》中,將醫家分為“醫經”、“經方”、“神仙”、“房中”四大流派。發展至今,可分為八大流派:傷寒學派、河間學派、易水學派、攻邪學派、丹溪學派、溫補學派、溫病學派、中西匯通學派。 从古至今,知名的和传说中的中医学家有:(參见:中医学家列表) 岐伯、扁鹊、张仲景、华佗、王叔和、陶弘景、孙思邈、宋慈、刘完素、张从正、李杲、朱震亨、張景岳、傅山、葉桂、陈修园、曹颖甫、余无言、唐容川、秦伯未、章次公、赵绍琴、张简斋、施今墨、程门雪、蒲辅周、左季云、郑寿全、談允賢等。 == 中医与近現代醫學的異同 == 自从近現代醫學(又稱西洋医学)隨著傳教士由歐洲传入中国之后,两种医学体系的競爭與融合就一直在进行。 === 醫學理論體系 === 世界上的醫療體系大致上可分成: “擬人論醫學體系”(神學):認為疾病的發" +259,"运行是客观且有规律可循的,人们通过经验、逻辑、实证的方式,可以将生物体的现象进行归因,从而掌握规律,获得处理某些问题的能力。 === 診斷疾病的思維方式 === 中醫診斷疾病是依望聞問切的結果來辨病及辨症,但也有-{只}-辨病不辨症(例如急症或有專病專方時),或只辨症不辨病(例如遇到不知名的疾病、疑難雜症、或調理體質等)的情況。與現代醫學在診斷上注重「病」的方式不同的是,中醫常會強調「辨(症)」。 以感冒為例,現代醫學醫師會分辨究竟是病毒性或是細菌性的感染,會鑑別是上呼吸道感染或是合併肺炎;但這些診斷對中醫師而言,卻不是最重要的。以中醫的觀點,需要分辨的是,這是屬於風寒或風熱;若屬風寒則又辨屬於太陽病、陽明病或少陽病;屬風熱則辨別病邪在衛氣營血的哪個階段等。 === 治療的思維及方法 === 中医主要通过中药、针灸等治療方法,幫助人體驅除病邪,恢復正氣。其思維可簡約為攻補二類,依病情不同,施行攻或補、或攻補兼施。傳統上,臨床進行攻補有八個方法,即「汗、吐、下、和、溫、清、補、消」,稱為八法。 中醫學的價值之一在於方劑,藥方是醫者的經驗結晶。中醫學本身發展的過程即是實驗的醫學,尤其是經過" +260,"6年9月24日舉辦的「二〇〇六中醫藥研究暨臨床病例學術研討會」上,長庚醫院北區中醫部主任楊賢鴻發表「辛夷散治療過敏性鼻炎隨機雙盲臨床療效評估」,在為期兩年的雙盲實驗中,共收集108位病患,完成整體實驗共有60位,其中包括實驗組40位、對照組20位,並針對臨床症狀、鼻腔阻力、鼻腔截面積、塵螨特異性免疫球蛋白、T淋巴球細胞激素之分泌等各項指標進行統計分析。結果顯示,辛夷散對過敏性鼻炎患者有臨床療效,此療效之機轉包括T細胞的免疫調節及嗜中性白血球活化的影響。這是中醫藥科學與其醫療價值驗證的眾多科研舉證之一。而其它具代表性的中醫科研驗證結果也在中華民國行政院衛生署中醫藥委員會網站有公開而詳實的記錄,提供有興趣者一窺中醫與臨床的成效。 === 知识传播方式 === 中医的典籍大多成书于古代,如成书于战国时期的《黄帝内经》、东汉的《伤寒论》、明朝的《本草纲目》,使用文言或古人的口語、歌訣記載,今人不易了解,而增加傳播的難度。古代中國官方亦不斷蒐集修訂各類中醫典籍,成為官修醫書,並設有考試制度,而在民間則多以師帶徒或家傳的方式傳承。近現代則成立中醫藥大學或學院等,對中醫學進行的系統整理、研究、教學," +261,"== 中醫存廢之爭 == 在中國,对于中醫學是否屬於科学的问题,始終一直争论不休。学术界的争论可追溯到19世纪末,现代医学传到中国之後,一些主張医学现代化的人士,主张废除中医,中醫學連帶受到巨大的挑戰。直到今天,中医学是否歸屬於科学、及中醫藥是否有效等,仍倍受主流科学界的質疑。 1879年,俞樾发表《废医论》最早提出了废除中医中药的主张。在1915年江苏的袁桂生将“废五行说”作为一项提案交神州医药总会讨论,题为《拟废五行生克之提议》文章发表于 《神州医药学报》第二十六期 。据 医学捌号楼 称可能是最早的提出废除五行理论的文章。 1926年,魯迅曾撰《父親的病》一文,批判了中醫存在的一些問題。 1929年,国民政府通过了余雲岫提出的《废止旧医以扫除医事卫生事业之障碍案》。但在全國輿論壓力下,國民政府隨後收回成命其中細節請參考董崇敏,針灸列入世界襲產同時,還原一段廢除中醫的往事。。 2005年,中南大学张功耀教授在《医学与哲学》杂志上发表了《告别中医中药》。 中国官方的说法认为中医学是一门科学。针对中医不科学的说法,荊志伟認為“2005年11月19日国家将中国中医研究院更名为‘中国中医科学" +262,"些中醫信奉的典籍有較多的荒謬錯誤,以及很多中醫藥方並非來自臨床經驗,而是來自取像比類的巫術思維。方舟子《人参的历史与现实》,《吃什么变什么?》 当前,许多科普媒体,如果壳网、丁香医生等等,以及许多科普作者和部分网民,也对中医持否定立场。如否定中医的理论意义和疗效,或主张废除中医的理论并利用现代科学方法检验与解释中医的某些治疗手法或药物的效果(简称“废医验药”)。 2019年,香港歷史學家陳振銘在“由《輔行訣臟腑用藥法要》到香港當代新經學”指出了陰陽五行的儒家政治哲學來源,還原了《輔行訣臟腑用藥法要》中關於《湯液經法》的內容,而且否定了《黃帝內經》與陰陽五行理論作為中醫基礎理論的地位。 == 節日、文化遺產 == 1929年在一場廢除中醫的嘗試因受到抵制而失敗之後,上海中醫藥界人士為了慶祝這一勝利,把每年3月17日定為「中國國醫節」。這是和中醫學相關的節日。 == 參見 == * 中藥列表 * 中藥 * 性味 * 五運六氣 * 藥用植物 * 韓醫學 * 日本漢方醫學 * 中國醫學史 * 中醫的科學性問題 * 中醫診斷學 ==注释== == 参考文献 == == 外部連結 == * 中華民" +263,"藥學院 * 《香港中藥材標準》網上檢索系統 Hong Kong Chinese Materia Medica Standards (HKCMMS) 中國香港特別行區政府.衛生署.中醫藥規管辦公室 T T 替代醫學 Category:中醫診斷學 Category: 中醫疾病" +264,"海洋学()是研究海洋的自然现象、性质及其变化规律,以及开发利用海洋的知识体系。它是研究海洋的地理学的分支。它涵盖了广泛的主题,包括生态系统动力学、洋流、波浪、。板块构造、海底地质,以及各种化学物质和物理性质在海洋内及其边界的通量。这些不同的主题反映了海洋学家融合多个学科对世界洋的进一步认识和对天文学、生物学、化学、气候学、地理学、地质学、水文科学、气象学、物理学中的过程的理解。古海洋学研究了地质历史中海洋历史。 ==發展歷史== 人類在史前時代就有了關於海浪和洋流的知識。科學形式海洋研究的雛型可以追溯到古希臘,亞里士多德和斯特拉波記錄下了對潮汐的觀測。但早期海洋探索主要目的在於製圖,儘管也用了鉛線做深度探測,人類對海洋的了解相當長時間都局限在淺層區域。19世紀中葉,英國皇家海軍嘗試繪製全世界的海岸線,發現大部分海洋似乎都相當深。 開啟現代科學海洋學的是1872-76年的挑戰者號遠征。英國政府1871年接受皇家學會的建議,宣布探索世界大洋並進行科學調查。由威維爾·湯姆森和約翰·默里發起,改裝了皇家海軍的挑戰者號並配備獨立的自然史和化學實驗室。在湯姆森的科學監督下,近7萬海里(13萬公里)" +265,"的北大西洋考察團,是迄今最雄心勃勃的海洋學和海洋動物學計畫。第一次聲學測量海深在1914年。1925年至1927年間,“流星”考察隊使用回聲測深儀收集了70,000次海洋深度測量,探勘大西洋中洋脊Alexander Hellemans and Brian Bunch, 1989, Timeline of Science, Sidgwick and Jackson, London 。 50年代,瑞士物理學家奧居斯特·皮卡爾發明,並利用來研究海洋的深度。1953年,和發現了沿著大西洋中洋脊延伸的全球洋脊系統Edgar W. Spencer, 1977, Introduction to the Structure of the Earth, 2nd edition, McGraw-Hill, Tokyo; 1954年,蘇聯的北極研究所發現了北冰洋的海底山脈。 1960年哈里·哈蒙德·赫斯發展了海底擴張學說。1958年美國核潛艇鸚鵡螺號第一次在北極進行了冰封航程,並抵達北極點。1962年,聖地牙哥加利福尼亞大學建立了能夠在所有海洋中運作,長108公尺的浮式儀器平台(FLIP)。 70年代開始," +266,"學。 ===相關學科=== * 水文學 * 水文地理學 * * 生物地理學 * 冰川學 * 氣象學 * 氣候學 * 地球物理學 * 海洋科學 * 環境科學 * 生物地球化學 ===應用與交叉學科=== * 航海海洋学 * 渔场海洋学 * 军事海洋学 * 区域海洋学 * * 海洋环境学 * * 海洋工程 * 海洋石油和天然气开采工程 * 深海采矿工程 * 海洋能开发工程 * 海洋水下工程 * 海洋空间开发工程 * 海岸工程 * 海港工程 * 围海工程 * 海水养殖 * 海水淡化工程 * 海水综合利用工程 == 参见 == * 世界海洋列表 * 海洋能 * 海洋灾害 * 缺氧事件 * 海平面上升 * Argo计划 * 中国海洋大学 == 参考文献 == Category:自然地理学 + Category:地球科学" +267,"水文学()属于地理學,研究的是关于地球表面、土壤中、岩石下和大气中水的各種行為,包含水循環、含量、分布、物理化學特性、對物質影响以及与所有生物之间关系的科学。 == 水文的基本概念 == === 水循環 === 水分子藉由吸收太陽的輻射能,使其從海水或是陸地蒸發,可因為地面溫度過低而直接在蒸發後不久凝結於地表,甚至凝固稱做霜,大多數情況蒸發的水分子,或稱做水蒸氣,上升至高空,因寒冷而凝結成雲,再以雨水、雪、冰雹等形式降至海洋、陸地,若降落在寒冷高地則可能形成冰川,或在較為平緩的地勢匯集形成湖泊或河流,或是滲入土壤成為地下水,隨後河水、湖水、地下水等液態水分子再流入海洋,這當中可能還伴隨著蒸發。由於這個過程可以不斷的循環發生,因此稱為水循環。在水循環的過程中,還必需顧及生物系統對於水資源的遲延作用,同時地表滯留洼蓄、植物截留與中間流的效應也是水循環的一部份。 === 水平衡 === 水平衡是指地表上某一區域,在一定時間之內,水量收入和支出的均衡狀態。全球海陸總降水量又與總蒸發量保持平衡,大約57萬立方公里。儘管整體來說全球保持水平衡,但不同地區,因為氣候的差異,使得水平衡會隨著時間、空間" +268,"游則會因為工業廢水、都市廢水等,使得水資源失去了被觀賞、遊玩、垂釣、生態棲息的功能。 == 特殊形態的水體 == === 河流 === 當降水量超過地面的滲入量,過剩的雨水會沿著地表的坡度向海拔較低處流動,經年累月會在地表上刻劃出小的紋路或水溝,長期下來會因為持續侵蝕溝道而形成具有固定流道的河流。 === 湖泊 === 水體在地表凹陷處長及積聚,終年不乾不涸,且水深在五公尺以上者稱為湖泊。分布於外流河流域的湖泊,由於湖水外流,鹽類不會堆積,因此多為淡水湖;分布遠離海岸的內流河流域之湖泊,鹽類積聚容易,多成為鹹水湖。 === 冰河 === 高山或兩極地區的積雪由本身壓力形成冰塊,又因重力作用而沿地表坡度滑動,這種移動的大冰塊稱為冰河,亦稱冰川。 === 地下水 === 廣義地下水指降水滲入地面後,儲存在地面以下的水體,包含不飽和帶土壤水,和飽和帶地下水,後者為狹義地下水。 === 海洋 === 地表上除陸地以外的廣大鹹水域。包括太平洋、大西洋、印度洋、地中海、北冰洋等,約占地表面積71%。 == 各種水體儲量表 == 水體種類 儲量(千立方公里) 佔總水體千分比 佔總淡水千分比 滯留時間" +269,"測繪學研究測定和推算地面幾何位置、地球形状及地球重力場,据此測量地球表面自然物体和人工设施的幾何分布,编制各种比例尺地图的理論和技术的学科。测绘学的研究对象是地球的形态、位置、重力分布等地理空间信息,因而测绘学可认为是地球科学的一个分支学科。近年来,测绘学的研究对象还从地球表面扩大到了地外空间及地球内部构造等领域。 == 測繪學的發展 == 测绘学的进步倚赖人类对地球形状认识的深化、地图制图技术的提高和测绘仪器的创新及变革。 在世界上古史时代,就有利用測繪學治理尼罗河泛滥后農田邊界的說法。公元前7世纪,管仲在其所著《管子》一書中已收集了早期的地圖27幅。公元前5世纪至3世纪,中国已有利用磁石制成最早的指南工具“司南”的記载。公元前2世纪,在《史记·夏本纪》中就记载有大禹治水时“左准绳,右规矩”,说明当时已经有简单的测绘活动。 === 地球形状的认知 === 测绘学研究的是地球,而人类对地球形状的认识也出于不断的变化中。最初人们认为地球乃“天圆地方”,直到公元前6世纪毕达哥拉斯和公元前4世纪亚里士多德提出了“地圆说”。公元前3世纪,埃拉托斯特尼在亚历山大进行观测日影,推算出了子午圈的长度" +270,"测绘学的发展离不开测绘工具的革新。早期的测绘使用的是简单的绳尺、矩尺等,17世纪望远镜发明,测绘工具开始变革。1617年,威理博·斯涅尔发明了“三角测量法”,开创了角度测量。1730年,西森研制出测角用经纬仪。地理大发现开始后,许多国家研究出了海上测定经纬度的仪器以定位船只。19世纪50年代,洛斯达首创摄影测量法。20世纪以后,随着飞机的发明,出现了航空摄影测绘地图的方法。人造卫星升空后,卫星定位技术(GPS)和遥感技术(RS)得以广泛应用,这与地理信息系统技术(GIS)合称“3S技术”。 == 學科分支 == * 大地測量學是一门量测和描绘地球表面的学科,也包括确定地球重力场和海底地形。 * 摄影測量學是研究利用被摄物体影像来重建物体空间位置和三维形状技术的学科。 * 地圖(制图)學是研究地图的理论、编制技术与应用方法的科学。 * 工程測量學是研究工程建设和自然资源开发中进行测量工作的理论。 * 海洋測繪学是以海洋和海底为对象进行测量和编制海图的理论。 * 测量数据处理是研究如何消除各种误差并判定精度,如何处理分析各种形式的测量数据进行合理预报等的数学手段。 == 参考文献 == *" +271,"农业属于第一級產業,包括作物种植、畜牧、渔业养殖、林业等活动,负责主副食和经济作物供应。农业的主要产品是食物、纤维、能源和原材料(例如橡胶),其中食物包括谷物、蔬菜、水果、食用油、肉类、奶制品、蛋和菌类。全球农业年产出约110亿吨食物,3200万吨自然纤维和40亿立方米木材。不过,其中有14%的食物在到达零售环节之前被浪费。自20世纪开始,基于单一作物种植的工业化农业开始成为世界农业产出的主要来源。 农业的出现是人类文明转向定居形式的里程碑,借由野生动植物的驯化、培育与繁殖,人们获得了充足的食物与资源,并促進早期城市的发展与成型。人类在10.5万年前开始从野外采集谷物,但直到1.15万年前才开始种植,并在大约1万年前驯化了绵羊、山羊、猪、牛等家畜。世界上至少有11个地区独立发展出了作物种植。 现代农业技术、植物育种、农业化学产品(例如杀虫剂和化肥)的发展显著增加了作物产量,但也引发了诸多生态与环境问题。选择育种和现代畜牧业技术发展增加了肉类制品产量,但也引发了动物福利和环境忧虑。上述环境问题包括气候变化、地下含水层枯竭、森林砍伐、抗生素耐药性和农业相关污染。农业既是环境退化的原因,也深" +272,"约7,700年,而后绿豆、大豆、红豆也在此区域驯化。美索不达米亚人于13,000-11,000年前驯化了绵羊。而家牛则是从10,500年前生活在现今土耳其和巴基斯坦地区的原牛驯化而来。家猪由野猪驯化而来,此过程在欧洲、东亚和东南亚独立进行,最早驯化时间距今约10,500年。在南美洲安第斯山脉,人们在10,000-7,000年前驯化了马铃薯,后续又驯化了豆子、古柯、羊驼、大羊驼和豚鼠。高粱于7,000年前在非洲萨赫勒被驯化。棉花于5,600年前在秘鲁被驯化,后也在欧亚大陆独立驯化。玉米源于野生玉蜀黍属,于6,000年前在中美洲驯化。Johannessen, S.; Hastorf, C. A. (eds.) Corn and Culture in the Prehistoric New World, Westview Press, Boulder, Colorado.公元前3,500年左右,马在欧亚大草原被驯化。关于农业的起源有大量研究,学者为此提出了众多假说。人类从狩猎采集向农业社会的转型伴随着集约化和的发展,相关过渡期案例有黎凡特地区的纳图夫文化和中国早期的新石器文化。随着生活方式的" +273,"以村庄形式定居,并依靠底格里斯河和幼发拉底河灌溉作物。其公元前3,000年的象形符号中出现了犁,公元前2,300年出现了播种犁。苏美尔农夫种植大麦、小麦、蔬菜(例如扁豆和洋葱)、水果(例如椰棗、葡萄和无花果)。古埃及农业始于旧石器时代末期的前王朝时期,时间大约在公元前10,000年,主要依靠尼罗河和当地季节性洪水灌溉作物。当地人种植的主要粮食作物为大麦和小麦,此外还有亚麻和纸莎草等经济作物。印度先民在公元前9,000年前驯化了小麦、大麦和枣,后续又驯化了绵羊和山羊。在公元前8,000-6,000年的梅赫尔格尔文化中,巴基斯坦先民驯化了牛、绵羊和山羊。Baber, Zaheer (1996). The Science of Empire: Scientific Knowledge, Civilization, and Colonial Rule in India. State University of New York Press. 19. .Harris, David R. and Gosden, C. (1996). The Origins and Spread of Agricu" +274,"er, Robert (2006) The Technology of Ancient China, Rosen Publishing Group. pp. 11–12. Wang Zhongshu, trans. by K. C. Chang and Collaborators, Han Civilization (New Haven and London: Yale University Press, 1982).,随后这些技术向西在欧亚大陆传播。借助基因分子钟估算,亚洲水稻的驯化时间大约在8,200-13,500年前,驯化地点位于中国西南部的珠江流域,原始物种为野生稻。古希腊和古罗马种植的主要谷物为小麦、二粒小麦和大麦,蔬菜包括豌豆、大豆和橄榄,绵羊和山羊养殖主要是为获取奶制品。Koester, Helmut (1995), History, Culture, and Religion of the Hellenistic Age, 2nd edition, Walter de Gruyter, pp. 76–77. White, K. D. (1970), Roman Farm" +275,"区域,借此创造有利于农业持续发展的。北美原住民还发展了一种名为“”的系统,这三种作物为、玉米和荷包豆。 普遍认为澳大利亚原住民是游牧的狩猎采集者,他们会有计划地在野外放火,可能是为了增加中野生作物的产量。学者指出,狩猎采集者需要让周围环境中野生作物产量维持在特定水平,只有这样才可在不耕作的情况下满足生存需求。新几内亚森林中可食用植物较为稀少,因此当地先民可能会实行“选择性放火”,以提高野生的果实产量。MLA Rowley-Conwy, Peter, and Robert Layton. Foraging and farming as niche construction: stable and unstable adaptations. Philosophical transactions of the Royal Society of London. Series B, Biological sciences vol. 366,1566 (2011): 849–62. 澳大利亚自5,000年开始饲养鳗鱼。澳洲中西部海岸和中东部海岸先民曾种植山药、本地小米、,种植地点或许是位于其永久定" +276,"布在撒哈拉干旱与半干旱地区、中亚,以及印度部份区域。 轮耕是作物种植的一种形式。农民首先从森林中开辟一块土地,在此种植作物,在数年后肥力过低时将其废弃。而后农民会选择另一块林地重复上述过程。这种种植方式主要流行于印度东北部、东南亚、亚马孙盆地的热带雨林地区,因为这些区域的土地可在废弃后快速再生为森林。 自给农业是指仅为满足家庭或本地需求的农业活动,很少有多余的产品运输至其他地区。自给农业在亚洲季风区和东南亚十分常见,截至2018年,世界上大约有25亿人从事自给农业,其耕地面积占总耕地面积约60%。 集约农业是指以最大化产量为目的的农业活动,尽可能的减少休耕期,并有大量资源投入(水、化肥、杀虫剂和自动化设备)。这类农业主要分布在发达国家。 == 当代农业 == === 现状 === 自20世纪以来,集约农业大量提高了粮食产量。如今,小型农场的食物产出约占世界总量的三分之一,但大型农场正变得十分普遍。占据前1%大小的农场平均面积超过50公顷,所管理的土地面积占所有耕地面积70%。将近40%的耕地属于面积超过1,000公顷的巨型农场。不过,六分之五的农场面积仍小于2公顷。化肥和杀虫剂等化学产" +277,"和国的农业产出居世界首位,其次是欧盟、印度和美国。经济学家估算如今美国农业的全要素生产率约为1948年的1.7倍。 尽管现代农业已大大進步,但截至2021年仍有7.02-8.28亿人受饥饿威胁。影响粮食安全的因素包括战乱冲突、气候变化与极端天气、经济波动,也可能源自国家结构特征(例如-{zh-hans:收入水平;zh-tw:所得水準}-)、自然资源丰富程度、政治经济制度等。国际农业发展基金认为,增加的比重或许是解决问题,并缓解粮食安全忧虑的可行方案,这种方法已在越南实行且成果甚佳。 === 劳动力 === 农业提供了全球大约四分之一的劳动岗位,撒哈拉以南非洲有超过一半的人口从事农业,而在部分低收入国家这一比例甚至超过60%。随着社会发展,许多农业人口会转向其它行业,而新技术的运用也允许农业在降低劳动力的情况下维持充足产量。随时间推移,从事农业的人口比例会逐步降低。 在16世纪的欧洲,大约有55-75%的人口从事农业,而到了19世纪,則下降至35-65%。如今,这些国家的农业人口低于10%。在20世纪初,全世界有约三分之一的人口从事农业,总人数大约10亿人。这占到全球儿童劳动力的70%," +278,"因。杀虫剂等化学产品可能会对接触者造成危害,长期暴露在这些产品中可能会引发疾病或增加新生儿致畸风险。工业化农场从业者常是整个家庭一同居住在农场,从而增加家庭成员的受伤和罹患疾病风险。家庭中6岁以下儿童尤其可能遭受农业伤害,常见的事故原因包括溺水、机械或引擎事故、农用车辆侧翻等。 国际劳工组织将农业认定为“所有经济行业中最具危险性的行业之一”。它认为每年与农业劳动相关的死亡至少有17万,死亡率是其它行业平均值的两倍。此外,农业相关伤病也十分常见,但通常不被记录。该组织于2001年制定了《》,其中覆盖了农业领域可能包含的安全风险,对这些风险的预防方法,以及个人和组织在农业活动中应当发挥的作用。 美国国家职业安全卫生研究所将农业置于的优先位置,为其中存在的安全与健康风险提供干预策略。也制定了准则以保护农业、畜牧业、园艺、林业从业者的健康与安全。美国农业安全与健康委员会()每年都会组织会议,邀请各界专家与政策制定者探讨该行业的安全问题。 == 农业生产 == 世界上农业总产值最高的国家如下: 根据国际货币基金组织和CIA《世界概况》的数据得出的2018年农业总产值(名义值)排名 根据国际货币基" +279,"。而年耕是一种强度更高的耕作方式,几乎不存在休耕期,这种方法需要向土地添加大量养分并维持害虫控制。 随工业化发展,农业发展出了单一耕作方式,即在大面积土地上种植同种作物。由于缺乏生物多样性,养分使用也相对单一,病虫害的问题更加严重,导致杀虫剂和化肥的需求增加。与此相对,或混养是指多种作物在一年中依顺序种植,同时保持其它作物的和伴生种植。 在亚热带和干旱环境,农业种植受降雨限制,可能无法在同一年内多次种植一年生作物,或是需要额外的灌溉。这些气候环境可支持多年生作物生长,例如咖啡、可可等,并可实践混农林业。在温带环境,生态系统多为草地和草原,在这些区域主要为高产量的年耕。 农业中重要的食用作物包括谷物、蔬菜、草料、水果和豆类。自然纤维来源包括棉花、羊毛、火麻、丝绸和亚麻。不同作物在全球不同区域种植。根据联合国粮农组织的估算,全世界农业产出如下表: 根据农业产品种类计算的产出(单位:百万吨)2004年数据 根据农业产品种类计算的产出(单位:百万吨)2004年数据 谷物 2,264 蔬菜和瓜类 866 根类和块茎类 715 乳类 619 水果 503 肉类 259 植物油 133 鱼类(200" +280,"有显著增长,1975-2007年的年均增长率为7.9%。 20世纪后半叶,畜牧业开始大量应用动物选择配种和杂交方法,这些方法显著提升了产量,但同时也造成了养殖动物遗传多样性降低,由此导致家养动物演化出的适应性与抗病性减弱。 草地畜牧业需要依靠灌木、草原或牧地放养草食动物,动物粪便可作为肥料排放回这些区域。部分区域因气候或土壤限制而难以施行农耕,因此草地畜牧业是这些地区主要的农业活动方式,全世界大约有3,000-4,000万牧民。混合畜牧业使用草地放养动物,但也辅以草料或粮食作为反刍和动物(主要是牛和鸡)的饲料。在这种畜牧系统中,动物粪便会被回收作为作物的肥料。 无土地畜牧业使用农场外的食物饲养动物,这种饲养方法在经济合作与发展组织成员国(主要为发达国家)中较为常见,其标志着作物与畜牧生产的分离。全球大部分猪肉和家禽都由无土地畜牧方式生产。据估计,2003-2030年约75%的家畜在室内封闭环境饲养,这种饲养方式也称为。部分商业养殖会采用生长激素促进动物生长,这种养殖方法存在许多争议。 === 方法与实践 === 耕作是使用工具(例如犁或耖)在作物种植前翻土,以将营养物质混入土壤或控制虫" +281,",将有害生物对农业造成的经济损失控制在较低水平,而杀虫剂则作为这些方法无效时的最后手段。 Pesticide Use in U.S. Crop Production , pp. 240–282 in Acquaah 是指对作物和牲畜的营养投入,以及牲畜粪肥管理。营养投入包括非生物化肥、粪肥、绿肥、堆肥和矿物质等。 Soil and Land , pp. 165–210 in Acquaah作物营养管理也包括轮作与休耕的安排。动物产出的粪肥可用于辅助草料生长,例如应用在集约化轮牧草场,或以液态或固态形式于农田或放牧场中。 Nutrition from the Soil , pp. 187–218 in Chrispeels thumb|系统 在气候多变或降水稀缺的地区,水资源管理显得尤为重要。一些农场在自然降雨之外也给作物补充额外水分,这种行为称为灌溉。在一些特定区域(例如北美大平原),农民会采取休耕一年的方式,让土壤获得足够未来数年耕种所需的水分。 Plants and Soil Water , pp. 211–239 in Acquaah现代精确农业可以更加便利地监控和调配水资源,从" +282,"因此,联合国粮农组织将农业自动化定义为在农业活动中使用机械设备提高排错、决策或执行效率,减轻农业操作的繁琐程度,提高及时性和潜在精度。 农业技术发展经历了从人力工具、动物牵引、机械化、电子化,最终到人工智能机器人的转变。机械化设备使用引擎驱动,可代替农业中的重复人力操作,如耕地和挤奶。随着数字自动化技术发展,这些机械也被赋予了自动排错和自主决策的能力。例如,谷物收割机器人可自主完成收割谷物并播种,而无人机可采集信息以提高自动化的准确性。这些自动化机械在现代精准农业中十分常见。如今,传统机械设备正被附带排错与决策功能的数字化设备取代,例如过去农业中常用的拖拉机现在正逐渐被新型农用车辆所替代,这些车辆可自动在农田中播种。 自动化设备也被广泛应用在畜牧管理中。根据市场数据,自动化产奶系统的销量在最近数年有显著增加,不过,该技术主要应用于北欧国家,在中低收入国家应用较少。市场上也存在针对奶牛和家禽的自动喂料系统,但关于这类系统的销量趋势数据较少。 关于农业自动化对就业影响的研究较为困难,因为这类研究需要收集农业上下游产业工人的大量跟踪数据。自动化引发某产业工作岗位减少的同时也会增加其它产业的工" +283,": Food security and food production systems (archived ), in 未来的气候变化可能导致低纬度地区粮食产量降低,而对北纬地区的影响可能混合有积极与消极影响。这些影响可能危及部分弱势群体(例如穷人)的粮食安全。Paragraph 4, in: Summary and Recommendations, in: == 生物技术 == === 植物配种 === 人类-{zh-hans:通过;zh-tw:經由}-配种来筛选植物理想性状的实践已有数千年历史。在作物后代选育的过程中,一些特定的性状被保留并得到了增强,例如更大的果实、抗旱抗虫性等。得益于19世纪奥地利生物学家孟德尔关于显隐性基因的研究,人类对植物遗传和配种有了更深入的理解,致使植物配种技术的進展頗為可觀。作物配种方法包括筛选理想性状、、异花授粉,以及在分子层面修改遗传基因。 在过去几个世纪,驯化作物的产量显著增加,抗旱与抗虫性提升,并更易于收割,营养价值也有提升。1950年代,在X射线和紫外线诱变技术的帮助下,诞生了首批现代商业作物,例如小麦、玉米和大麦的特殊物种。 20世纪中叶的" +284,"转基因食品的标签有不同规定,例如欧盟要求转基因食品必须有标注,而美国则没有这类要求。 抗除草剂作物是经基因改造,可暴露于除草剂(例如草甘膦)而正常生长的作物。农民在种植这类作物时可使用除草剂控制田间杂草,而不必担心危害作物的生长,由此提升作物产量。随着这类作物在全球范围的广泛种植,农民对草甘膦除草喷雾的使用也有增加。研究发现,在一些地区已出现具有草甘膦抗性的杂草,这促使农民选择其它种类的除草剂。还有一些研究指出,草甘膦的普遍使用可能导致了部分作物铁元素的缺乏,这引发了关于这类作物产量和营养质量的忧虑。 农业中使用的基因改造生物还包括抗虫作物,这类作物的抗虫基因来源于土壤细菌苏云金芽孢杆菌(Bt),其可产生对昆虫而言有毒的化合物,从而抵抗虫害。也有人认为,借助传统植物配种技术也可获得与转基因类似的抗虫性状,并可透过与野生物种的杂交或异化授粉获得对多种害虫的抗性。在部分情况下,野生物种是作物抗性的主要来源,例如一些番茄品种借助于野生番茄的杂交而获得了针对至少19种疾病的抗性。 == 環境影響 == === 影响与损失 === thumb|新西兰一处因农业活动造成的水污染 农业即是环境退化的" +285,"各类手段去除作物病原体、捕食者和竞争者(例如杂草)。这些措施的平均花费随农田面积扩增而降低,因此现代农业倾向于扩大农田面积,从而移除树篱、沟渠,以及其它生物的栖息地,并使用农药消灭昆虫、杂草和真菌。在集约农业中,大面积农田仅种植十分有限的作物,由此导致环境中的生物多样性处于极低水平。此外,农业产出还可能因为农产品收割、处理或加工时的操作而导致额外损失。 因为气候变化,过去某地罕见的有害生物与疾病可能会变得流行。例如在法国香槟地区,农民发现了当地小麦患上了秆锈病,这类疾病只在20-30年前的摩洛哥曾有出现。气候变化影响了冬季均温,因此在过去无法活过冬季的昆虫如今却可能可以存活甚至正常繁衍。 === 畜牧业环境问题 === 联合国高级官员亨宁·斯坦菲尔德曾表示,畜牧驯养是引发环境问题的最主要因素之一。畜牧业用地占农业总用地面积的70%,或占总陆地面积30%。其为最主要的温室气体排放源头,占全球总排放的18%,相比之下,所有交通工具的温室气体排放只占总排放的13.5%。畜牧业还产生了大量一氧化二氮气体,占总排放的65%,这类气体的暖化效应是二氧化碳的296倍,此外也产生了37%的甲烷、64%" +286,"约70%淡水资源取用与农业相关,41%的灌溉水资源影响到了环境水流需求()。很久以前人们就注意到在部份区域,例如中国北部、印度的恒河上游,以及美国西部的含水层正在枯竭,而2012年的研究指出,这种状况已在越来越多地区出現。农业需要生产更多粮食,以供应全球日益增长的人口,但由人工增长带来的工业与城市发展同时也占据了农业所需的部份水资源,因此节水是未来农业发展的一个重要方向。虽然工业取水量在过去数十年有所下降,城市取水量自2010年来仅略有增长,但农业取水量却正以前所未有的速度增长。农业用水也可造成许多环境问题,例如湿地破坏、水媒傳染病流行,以及因错误灌溉导致的土地盐碱化或内涝。 === 杀虫剂使用 === 自1950年代起,杀虫剂的使用日益增多,如今年度使用量已达到230万吨。然而,杀虫剂的广泛使用并未显著减少害虫对农业造成的损失。根据1992年世界卫生组织的估计,全球每年有300万人因杀虫剂中毒,由此致死的人数高达22万。Our planet, our health: Report of the WHO commission on health and environment. Gen" +287,"力),并将其引诱至方便移除的位置(拉力)。 === 气候变化 === 农业活动对气候变化的影响主要包括温室气体排放与森林砍伐。Section 4.2: Agriculture's current contribution to greenhouse gas emissions, in: 农业、林业和其它土地使用活动产生的温室气体占人类总排放的13-21%。.农业产生的温室气体中,超过一半为一氧化二氮和甲烷。畜牧业是农业温室气体的主要来源。 在所有农业温室气体中,57%与动物食物生产相关,29%与植物食物生产相关。作物种植领域温室气体排放最多的是水稻生产(占12%),而动物制品中温室气体排放最高的是牛肉生产(占25%)。南亚、东南亚和南美是生产型温室气体的最大排放区域。 === 可持续性 === thumb|美国爱荷华州的一个农场,其采用了梯田、保护耕作、保护缓冲区等方式降低土壤侵蚀与水污染 由于水资源短缺、土壤侵蚀和肥力降低等因素的影响,现行的农业实践已不可持续。因此,必须重新考虑农业生产与水、土地和生态系统之间的关系。其中一个解决方向是赋予生态系统实际价值,认识农业生产活动消耗的生态" +288,"虫剂产业的发展使全球农业产量有了大幅增长。1960年代至1980年代的绿色革命将农业技术推广到全球各地,使农业产量进一步增长(不同地区小麦产量增长60%至390%,水稻产量增长60%至150%),全球人口数量翻了一倍。但是,对化石燃料的过度依赖也引发了部分人对未来能源短缺的担忧,这可能会增加农业成本并影响农业产出。 现代农业中的能源消耗主要可以分为两个方面:直接消耗和间接消耗。直接能源消耗包括农用机械所需的燃料和日常维护所需的润滑剂。间接能源消耗则包括生产化肥、杀虫剂和农用机械所耗费的能源。其中,生产氮肥消耗的能源占农业总能耗一半以上。农业的能源消耗被包括在食品产业的能源消耗中,除了农业,食品产业的能源消耗还包括食品的处理、运输、包装和销售等环节。农业在食品产业能源消耗中的比例并不高,例如在美国,农业占食品产业能源消耗的比例约为五分之一。 === 塑料污染 === 塑料在农业中应用广泛,用途包括增加作物产量,优化水资源和化学产品利用率。常见的农业塑料产品包括:覆盖温室的塑料薄膜、覆盖土壤的地膜(用于抑制杂草生长、保水保温、增加肥料利用率)、遮光材料、杀虫剂包装、育苗盆、保护网和灌溉管道" +289,"= 经济合作与发展组织对动物农产品和饲料的直接补贴,2012年数据 产品 补贴 (单位:10亿美元) 牛肉 18.0 牛奶 15.3 猪肉 7.3 禽类 6.5 大豆 2.3 鸡蛋 1.5 绵羊 1.1 农业政策是指政府制定的与农业生产和国外农产品进口相关的政策。政府通常会以国内农业市场的特定预期目标来制定农业政策,这些目标的涉及范围十分广泛,例如:风险管理与调整(包括与气候变化、粮食安全、自然灾害相关的政策);(包括税收政策);自然资源和环境的可持续性(例如水资源相关政策);科学研究与发展;国内商品的市场准入(包括与国际组织的关系和与他国的协定)。农业政策也可能涉及,也即保证食物供应的质量和稳定性;粮食安全,保证食物供应可满足人口需求;以及自然保育。政策实施方式可能涉及财政项目,例如农业补贴。 根据2021年的一份研究报告,全球每年向农业生产者的支持与补助高达5,400亿美元,占农业总产出价值的15%。这些补助有明显的偏向性,可能促使效率低下、分配不均,或支持可能危害环境和人类健康的活动。 农业政策的制定受多方面影响,包括消费者、农业生产者、农企业、贸易游说团体等。其中-{zh-ha" +290,"导致经济剩余在生产者(农民)和消费者之间的再分配,但也可能对乡村社区产生负面影响。 政府施行的各种政策,如税收、补助、关税等,可对农产品市场产生显著影响。自20世纪60年代以来,贸易壁垒、汇率制度和农业补贴的综合作用对全球农民产生了深远影响。在20世纪80年代,由于农业补贴导致的人为降低农产品价格,为无补贴的发展中国家农民带来了不利影响。从1980年代中期到21世纪初,一些国际协议对农业关税、补贴和其他贸易限制作出了约束。 尽管如此,直到2009年,全球农产品价格仍受诸多政策因素影响而出现扭曲。受贸易限制影响最大的三种农产品分别时糖、牛奶和大米,限制主要来自税收政策。在油籽作物中,芝麻的税收最高,但总体而言畜牧产品的税收远高于饲料和油籽作物。自20世纪80年代以来,由政策导致的畜牧产品价格扭曲已大幅减少。然而,一些作物如棉花在部分发达国家仍受到补贴支持,导致价格人为压低,这为发展中国家没有补贴的农民带来了负面影响。未加工产品如玉米、大豆、牛肉等,它们可能会根据质量进行分级,从而影响销售价格。 === 农业科学 === 农业科学是一门跨学科的领域,涉及自然科学、经济学和社会科学等相关理论" +291,"寻和的自然天敌,将这两种害虫的寄生蜂(例如棲蜂)和自然天敌引入美国。Coulson, J. R.; Vail, P. V.; Dix M. E.; Nordlund, D. A.; Kauffman, W. C.; Eds. 2000. 110 years of biological control research and development in the United States Department of Agriculture: 1883–1993. U.S. Department of Agriculture, Agricultural Research Service. pages=3–11 == 参考文献 == === 注释 === === 脚注 === === 来源 === * * * == 外部連結 == * Food and Agriculture Organization * United States Department of Agriculture * 世界银行关于农业 的资料 Category:食品产业 Category:农学" +292,"统一资源定位符(,缩写:URL,或称统一資源定位器、定位地址、URL地址)俗称网页地址,简称网址,是因特网上标准的资源的地址(),如同在網路上的門牌。它最初是由蒂姆·伯纳斯-李发明用来作为万维网的地址,现在它已经被万维网联盟编制为因特网标准-{}-RFC 1738。 在網際網路的歷史上,統一資源定位符的發明是一個非常基礎的步驟。统一资源定位符的语法是一般的,可扩展的,它使用美国信息交换标准代码的一部分来表示因特网的地址。统一资源定位符的开始,一般會标志着一个计算机网络所使用的网络协议。 统一资源定位符的标准格式如下: 统一资源定位符的完整格式如下: 其中[访问凭证信息]、[端口号]、[查询]、[片段ID]都属于选填项。 == 语法 == 超文本传输协议的统一资源定位符将从因特网获取信息的五个基本元素包括在一个简单的地址中: # 传送协议 # 层级URL标记符号(为「//」,固定不变) # 访问资源需要的凭证信息(可省略) # 服务器(通常为域名,有时为IP地址) # 端口号(以數字方式表示,若為預設值可省略) # 路径(以「/」字元區別路徑中的每一個目錄名稱) # 查詢(GET模式的表" +293,"pedia.org/w/index.php?title=Special:-{zh-tw:隨機頁面;zh-cn:随机页面}-」)就可以了。 由于超文本传输协议允许服务器将浏览器-{zh-tw:重導向;zh-cn:重定向}-到另一个网页地址,因此许多服务器允许用户省略网页地址中的部分,比如「www」。从技术上来说这样省略后的网页地址实际上是一个不同的网页地址,浏览器本身无法决定这个新地址是否通,服务器必须完成-{zh-tw:重導向;zh-cn:重定向}-的任务。 ==相對Protocol URL== 相對Protocol links (PRL,又稱為相對protocol URLs (PRURL), 是没有指定协议的URL。例如,//example.com 将使用当前页面的协议,通常是 HTTP 或 HTTPS。 == 其它使用 == 统一资源定位符不但被用作网页地址,JDBC 客户端也使用统一资源定位符连接其数据库服务器。作为对比,ODBC 的连接字符串作用相同,但并不采用 URL 格式,而是分号和等号分隔的键值对。 以下是一个 Oracle 数据库的统一资源定位符: jdbc:datad" +294,"計算()是一種將「單一或多個的輸入值」轉換為「單一或多個的結果」的一種思考過程。 計算的定義有許多種使用方式,有相當精確的定義,例如使用各種算法進行的「算术」,也有較為抽象的定義,例如在一場競爭中「策略的計算」或是「計算」兩人之間關係的成功機率。 將7乘以8(7x8)就是一種簡單的算術。 利用布莱克-斯科尔斯模型來算出財務評估中的公平價格(fair price)就是一種複雜的算術。 從投票意向計算評估出的選舉結果(民意調查)也包含了某種算術,但是提供的結果是「各種可能性的範圍」而不是單一的正確答案。 ==字源== 計算的英文Calculation,字源來自,意為碎石,用來計算數目用的小石頭,它譯為,之後成為。在英文中,還有另一個字Computation,也被譯為計算。這兩個字在中古時代被分開。 == 參見 == * 算法 * 算盘 * 电子计算器 * 数学 ==外部链接== *揭开运算的神秘面纱 是从18世纪的手稿,在阿拉伯语中,Ibn al-Banna' al-Marrakushi 有关的计算过程 J" +295,"在计算机科学中,数据结构()是计算机中存储、组织数据的方式。 数据结构意味着介面或封装:一个数据结构可被视为两个函数之间的介面,或者是由数据类型联合组成的存储内容的访问方法封装。 大多数数据结构都由数列、记录、可辨识联合、引用等基本类型构成。举例而言,可為空的引用(nullable reference)是引用与可辨识联合的结合体,而最简单的链式结构链表则是由记录与可空引用构成。 数据结构可透过程式语言所提供的数据类型、引用及其他操作加以实现。一个设计良好的数据结构,应该在尽可能使用较少的时间与空间资源的前提下,支援各種程式執行。 不同种类的数据结构适合不同种类的应用,部分資料結構甚至是為了解決特定問題而設計出來的。例如B树即為加快樹狀結構存取速度而設計的資料結構,常被應用在資料庫和檔案系統上。 正確的数据结构選擇可以提高演算法的效率(請參考演算法效率)。在電腦程式设计的過程中,选择适当的数据结构是一項重要工作。许多大型系统的編寫经验顯示,程式設計的困难程度与最终成果的质量与表现,取决于是否选择了最適合的数据结构。 系統架構的关键因素是数据结构而非算法的見解,导致了多种形式化的设计方法与" +296,"中华人民共和国是一個位於东亚的社会主义国家,首都为北京市,国土面積約为960萬平方千米。全国共划分為23個省、5個自治區、4個直轄市和2個特別行政區共34個省級行政區,國土面積位居世界第三Countries of the world ordered by land area . List of countries of the world in alphabetical order. [2016年1月22日] .。 中国抗日战争结束后,毛泽东领导下的中共和人民解放军在第二次国共内战中逐步取得优势,并于1949年10月1日在北京宣告成立中华人民共和国中央人民政府。1950年代后,中华人民共和国实际控制中国大陆地区,与控制台湾地区的中华民国形成两岸分治的格局。 截至2023年末,中华人民共和国有约14.1亿人,约占世界人口的17.6%,为世界上人口第二多的国家。中华人民共和国民族众多,仅官方承认的民族就有56个。不过除汉族外,其他民族仅占总人口的8.89%。中华人民共和国的国家通用语言文字是普通话与规范汉字。中华人民共和国于1986年后实行九年义务教育制度,截至2020年已有2.4亿人接受" +297,"合作組織、二十國集團、金磚國家、東亞峰會、亞投行、RCEP等國際組織的創始國和主導國。隨著綜合國力的上升,中華人民共和國已被認為是潛在超級大國。 == 國名 == 在「中華人民共和國」國名中:「中華」代表中華民族,「人民」包涵工人、農民、小資產階級和民族資產階級,「共和國」代表國體。新政治協商會議籌備會最初曾擬定國名為「中華人民民主共和國」,簡稱「中華民國」。但1949年9月27日中國人民政治協商會議第一屆全體會議通過《中華人民共和國中央人民政府組織法》後,國名最終定為「中華人民共和國」朱月怡. 新中國國號誕生內幕揭秘 . 人民網. 2006年7月20日 [2016年1月22日] .。 中華人民共和國有時會稱作「新中國」,以同1949年前的「舊中國」區分。1949年至1971年,中華民國政府遷台後,其仍被國際社會广泛視為“唯一合法的中國政府”,並拥有聯合國的中國代表權席位洪健昭. Taiwan, Taipei — What’s in a name? . 《英文中國郵報》. 2009年10月15日 [2015年1月22日] .;当時的中華人民共和國則被稱為「紅色中國」或「共產中國」汪園斐" +298,"了「联俄、容共、扶助农工」的政策,第一次国共合作得以实现。1927年国民革命军北伐取得接连胜利之际,中国国民党内部左派和右派斗争加剧,蔣介石在南京另立中央,建立南京国民政府,实施清党。同年8月1日,中国共产党进行南昌起义,第一次国共内战爆发;10月,毛泽东率领经“三湾改编”后的秋收起义部队上井冈山,创立了第一个农村革命根据地。 1928年4月,朱德率领南昌起义余部与湘南起义农民军到达井冈山与毛泽东会师。1930年12月到1931年9月,毛泽东、朱德领导红一方面军连续取得第一、二、三次反“围剿”战争的胜利,形成了以瑞金为中心的拥有21座县城、5万平方公里面积、250万人口的全国最大的中央革命根据地。1931年11月,中华苏维埃共和国在江西瑞金成立。1933年,中国共产党挫败国民政府发动的第四次围剿,通过三年革命根据地的治理实践,积累了经验。1934年10月,中國國民黨第五次剿共戰爭迫使中华苏维埃共和国政府撤离中央苏区开始长征,并于1935年10月抵达陕甘苏区。1935年11月,中华苏维埃共和国中央政府西北办事处成立。同年12月,中共中央召开瓦窑堡会议决议将“工农共和国”改为“人民共和国”" +299,"。26日,新疆和平解放。 === 建国初期和社会主义建设(1949年-1978年) === ==== 稳固政权和初期建设 ==== left|thumb|1949年,毛澤東在開國大典上宣布中華人民共和國中央人民政府正式成立 1949年10月1日,中华人民共和国中央人民政府正式成立。1950年5月,解放军占领海南岛,海南特别行政区并入广东省。6月,国军和政府人员逐渐撤往臺灣與部分沿海島嶼,内地残余部隊逐渐被消灭。1950年,中华人民共和国政府發起鎮壓反革命大規模政治運動人民網. 1950年3月16日 全國展開大規模剿匪斗爭 . 中國共產黨新聞網. [2016年1月22日] .中國共產黨中央委員會. 中共中央關於鎮壓反革命活動的指示 . 中國共產黨新聞網. 1950年10月10日 [2016年1月22日] .李良玉. 建國初期的土地改革運動. 國學網. 2005年2月26日 [2016年1月22日] .。而透過批鬥約100萬至200萬名地主并向农民分发耕地的土地改革,中華人民共和國政府得以鞏固,並獲得底層民眾的歡迎Donald F. Busky. Communism in History" +300,"主义建设,意图快速进入发达国家行列。但此冒进之舉重現了苏联社會改造的災難,造成1958年至1961年至少上千萬人因飢荒死亡侯楊方. 《中國人口史:第六卷·1910~1953年》. 上海: 復旦大學出版社. 2001年1月1日: 第384頁至第415頁. .。 ==== 文革時期(1966年-1976年) ==== 1962年七千人大會後,毛澤東同劉少奇發生分歧。1966年後,以毛澤東為代表的中共左派為防備資本主義復辟,發動文化大革命,引發了全國範圍內的社會動蕩和政治攻訐。1976年秋毛澤東逝世,華國鋒接任其職務,並與葉劍英、汪東興、李先念等人合作,“一舉粉碎四人幫”,持續十年的文化大革命就此結束。 ==== 外交 ==== 外交方面,與印度、越南爆發邊境衝突,並與多國簽訂界約,解决了部分领土争端。由于中苏交恶,毛泽东提出“三个世界”理论,形成了以第三世界为盟友的国际统一战线策略。1971年10月中華人民共和國取代中华民国获得在聯合國中的中国席位聯合國大會.《聯合國大會2758號決議》:关于“恢復中華人民共和國在聯合國組織中的合法權利問題”的決議,并成為聯合國安全理事會常任理事國Mich" +301,"公社等集体农业制度,深圳、珠海、汕头和厦门被设为最早的四个经济特区以实行对外开放。1982年,中华人民共和国通過現行的《中華人民共和國憲法》。1983年,左派保守势力主导开展了清除精神污染运动。1987年,中共中央總書記胡耀邦因八六學潮而下臺,改由國務院總理趙紫陽接任《胡耀邦思想年谱,1975-1989》. 泰德时代出版社. 2006年. .;1989年4月,因胡耀邦之死,北京學生发起游行示威,后事态扩大化并波及全国,是为八九民运,引致军队清场以平息动乱。此举受到西方国家的譴責、經濟制裁及武器禁运,趙紫陽也因此下台。 ==== 江泽民时期(1989年-2002年) ==== 六四事件后,中共上海市委书记江澤民先後接替趙紫陽和鄧小平擔任中共中央委員會總書記、中央軍事委員會主席,表现強硬的李鵬继续擔任國務院總理。1992年邓小平进行南巡,使得六四事件后放缓的改革开放进程重新提速,经济得到快速发展。在江澤民及先後两任國務院總理李鵬和朱鎔基领导下,坚持「一個中心、兩個基本點」,中华人民共和国從計劃經濟轉型為社會主義市場經濟体制,經濟環境日益開放。其间估計有1.5億名農民脫離貧困,国内生产总值平" +302,"nts, Taiwan . Migration News. 2006年1月 [2016年1月22日] .。 ==== 胡锦涛时期(2002年-2012年) ==== 2002年11月15日,中共十六屆一中全會選出胡錦濤和溫家寶等組成第四代領導集體。同年冬SARS疫情爆发并持续至2003年夏。2003年,中华人民共和国完成了首次载人航天飞行任务。2005年,青藏铁路全线贯通。2008年,第29届奥林匹克运动会在北京举办,中国人民解放軍海军开赴亚丁湾海域执行护航任务。2009年,新疆乌鲁木齐发生骚乱造成至少197人死亡,1721人受伤。2010年,中华人民共和国举办了上海世界博览会和广州亚运会从北京奥运到上海世博:改变西方对中国陈腐印象 . 新浪. 2010年5月10日 [2016年3月27日] .。2010年後,中华人民共和国經濟增长因全球經濟大衰退而減緩,但國內生產總值仍于2011年超越日本,成為全球第二大經濟體。 ==== 習近平時期(2012年至今) ==== 2012年11月15日,習近平在中共十八屆一中全會上當選為中共中央总书记和中共中央軍委主席。2013年3月,習近平在十二屆" +303,"of government debt . 英國廣播公司新聞網. 2013年7月29日 [2016年1月22日] .。其也展开了大规模的反腐败运动。2014年,以购买力平价计算,中华人民共和国国内生产总值已经超过美国,成为世界第一大经济体。2014年召開的亚太经合组织峰會上,習近平表示中华人民共和国經濟將進入「新常態」顧錢江、張正富和王秀瓊. 習近平首次系統闡述「新常態」 . 人民網. 2014年11月9日 [2016年1月22日] .。在2015年,中华人民共和国主導成立亞洲基礎設施投資銀行、舉辦中國抗日戰爭70周年紀念大會、進行两岸领导人会面王靜宇. 俄媒體盤點2015年中國大事 成立亞投行與大閱兵上榜 . 新浪. 2015年12月29日 [2016年1月22日] .。2016年,中华人民共和国首次舉辦二十國集團峰會;在东海、南海主权争议问题上主动出击,采取一系列岛礁建设和军事演习等措施进行回击。2017年,130個國家代表團出席了在北京举办的一帶一路峰会。 2019年,香港爆发反对逃犯条例修订草案运动,至2020年《港版国安法》颁布后逐渐停息。2019年底,武汉市爆发新冠疫情,中国" +304,"之後的第四名。中华人民共和国總面積一般認為約有960万平方公里Geography . 中國網. [2016年1月22日] .Land area. 中華人民共和國政府. [2016年1月22日] .,不同地方也有不同數據資料,包括《大英百科全書》提到的9,572,900平方公里福赫伯. China . 《大英百科全書》. 2015年12月28日 [2016年1月22日] .、《聯合國年鑑》標記的9,596,961平方公里、美國中情局《世界概況》標記的9,596,961平方公里等中央情報局. CHINA . 《世界概況》. [2016年1月22日] .。 中华人民共和国法定疆域北至漠河縣以北的黑龍江河川航道中心線至北點,東至黑龍江和烏蘇里江匯合處的黑瞎子島中部,西至新疆維吾爾自治區烏恰縣以西的帕米爾高原,南至南海諸島最南端的曾母暗沙;東西距離約5,200公里(橫跨5個時區),南北距離約為5,500公里。中华人民共和国海岸線有18,000公里,瀕臨渤海、黃海、東海、南海,其中渤海為中华人民共和国內海,內海和邊海水域面積約470萬平方公里,沿海島嶼有7,600多個,面積500平方公尺以上的海島" +305,"的蒙古國和朝鮮,另外中华人民共和国還與韓國、日本、菲律宾、马来西亚、文莱和印尼為海上鄰國罗琪. 外交部官员:中国与8个周边邻国存在海上争议 . 中国网. 2012年4月10日. [2016年4月46日] .。 === 地形氣候 === 中华人民共和国地勢西高東低,分为三級階梯。第一級階梯是位於中华人民共和国西南部的青藏高原,平均海拔高度4,000米以上,被稱為「世界屋脊」地形 . 中華人民共和國政府. 2005年6月24日 [2016年1月22日] .;第二級階梯以高原和盆地為主。在大興安嶺、太行山、巫山山脉、武陵山脈、雪峰山脈一帶以東至黃海和東海沿岸,是沖積平原为主的第三階梯,人口多聚集在此處。各类地形面積依序為高原(33.30%)、山地(26.04%)、平原(18.75%)、盆地(11.98%)、丘陵(9.90%)。中华人民共和国地貌景觀有冰川、丹霞、黃土、沙漠、喀斯特等。西北地區有塔克拉瑪干沙漠和戈壁沙漠,南方则以丘陵和矮山為主,西側還有喜馬拉雅山脈在首的山地,後者海拔高度約6,000公尺;中國和尼泊爾的邊境有海拔高度8,848公尺的珠穆朗瑪峰,爲地球海拔最高的山峰Nepal a" +306,"西南部偏遠山區,草地分布在內陸高原和山區。草地面積4億公頃,是草地面積最大的國家之一,當中內蒙古、新疆、青海和西藏為四大牧區蘇向東. 中國自然資源概況(組圖) . 中國網. 2010年2月2日 [2016年1月22日] .。中华人民共和国水資源總量達25,500億立方公尺,排行世界首位;當中有大量的河流和湖泊,重要的河流有黃河、長江、西江、湄公河、雅鲁藏布江、珠江、淮河、松花江和黑龍江等。 === 生物多样性 === 中华人民共和国是超級生物多樣性國家同盟成員,亦是《生物多樣性公約》的締約國,在2010年加入生物多樣性行動計畫List of Parties . 生物多樣性行動計畫. [2016年1月22日] .;是生物多樣性第三豐富的國家,僅次於巴西和哥倫比亞。中华人民共和国位於古北界和東洋界兩個生物地理分布區間,以喜馬拉雅山脈、橫斷山脈北部、秦嶺山脈、伏牛山、淮河與長江間為界,以北地區主要是溫帶和寒溫帶動物群,以南地區則是熱帶性動物自然資源 . 中華人民共和國政府. 2005年7月27日 [2016年1月22日] .。中华人民共和国陸棲脊椎動物約有2,070種,占全世界9.8%。境內" +307,"[2016年1月22日]. .。針葉林下層有竹林,山區則有刺柏、紅豆杉和杜鵑花。中部和南部則有亞熱帶森林,有著多達146,000種植物。雲南省和海南島則是熱帶季風雨林,有四分之一的動物和植物物種。中华人民共和国森林面積2.08億公頃,為世界第五名中國森林資源(2009-2013年) . 國家林業局. 2014年2月25日 [2016年1月22日] .,并在2015年4月位于东北和内蒙古地区的重点国有林区已经停止商业性采伐,然而森林覆蓋率僅21.63%(2014),低於全球31%的平均水準陸大道. 《中国国家地理图鉴》. 河南: 大象出版社. 2005年4月1日. .。中华人民共和国有超過32,000種維管束植物,當中種子植物300個科、2,980個屬和24,600個種,而被子植物則有2,946個屬(占世界的23.6%)。另外包含水杉在內,約有62%的古老植物僅存於中华人民共和国。栽培植物有1,000多種有用材林木、4,000多種藥用植物、300多種果品植物、500多種纖維植物、300多種澱粉植物、600多種油脂植物、80多種蔬菜植物。另外中华人民共和国还有超過10,000種真菌Cons" +308,"79年制定《中華人民共和國環境保護法》等法規新華社. 中華人民共和國環境保護法(全文) . 人民網. 2014年4月25日 [2016年1月22日] .,但地方政府與社區重視經濟發展而忽视環境問題,使得政府未能严格執法Kimberley Soekov. China protesters force halt to Zhejiang factory plan . 英國廣播公司新聞網. 2012年10月28日 [2016年1月22日] .。長期的氣候乾旱與錯誤農業政策使得沙漠擴大(環境保護部於2007年時表示中华人民共和国每年有4,000平方公里土地荒漠化)Cliff Coonan. The gathering sandstorm: Encroaching desert, missing water. 《獨立報》. 2007年11月9日 [2016年1月22日] .。每年春季,黃沙沙塵暴会襲擊華北地區,並影響到朝鮮、韩国、日本等東亞国家Terry Waghorn. Fighting Desertification . 《富比士》. 2011年3月7日 [2016年1月22日] .Beiji" +309,"體的排放辛聞. 解振華:中國為減緩全球溫室氣體排放作出重要貢獻 . 中國網. 2011年12月8日 [2016年1月22日] .。截止到2016年,中华人民共和国已经成為世界最大的光能生產國,立下了自主發展可再生能源的典範,以及全球潔淨能源的里程碑。媒体:我国新能源越来越陷入“边建边弃”怪圈 . 观察者网. 2017年2月6日 [2017年2月7日]. 中华人民共和国很多城市面臨嚴重的空氣污染,並大大影響民眾健康。2013年,政府投資2,777億美元用于治理空氣污染及北部環境污染問題胡俊峰. 中國大氣污染防治計劃7月底公布 設PM2.5控制目標. 中國網. 2013年7月27日 [2016年1月22日] .。另外中华人民共和国还存在水源枯竭、水土流失和水污染等問題,大約有2.98億名农村居民仍然無法獲得良好的飲用水Lu Hui. China pumps more funding into safe drinking water . 新華網. 2012年6月29日 [2016年1月22日] .。除了喜馬拉雅山脈冰河的加速融化為數億人帶來危機外Michael Reilly. Himalay" +310,"tn on water projects. 《中國時報》. 2011年7月11日 [2016年1月22日] .。2020年、2022年中国相继出台《长江保护法》和《黄河保护法》加强长江流域和黄河流域的生态环境保护。2023年中国政府推出《重点流域水生态环境保护规划》推动全中国所有重要水域生态环境的保护治理。 == 政治 == === 國體政體 === 依照憲法規定,中華人民共和國的國體為“工人階級領導的、以工農聯盟為基礎的人民民主專政的社會主義國家”。中華人民共和國也是為數不多由共產黨執政的共產主義體制國家,遵循人民民主專政、中国特色社会主义和社會主義市場經濟方針An. Xi reiterates adherence to socialism with Chinese characteristics . 新華網. 2013年1月5日 [2016年1月22日] .,不過部分學者認為中華人民共和國以社會主義名義實行威權主義和法團主義。 依照憲法規定,中華人民共和國的政體為人民代表大會制度翟小波. 翟小波:代議機關至上的人民憲政――我國憲法實施模式的解釋性建構:以憲法觀、行憲歷史和條文類型化" +311,".。 1987年中共十三大上,中国共产党提出从1956年至21世紀中葉的社會主義初級階段,要以經濟建設為中心,堅持改革開放,堅持社會主義道路、人民民主專政、中國共產黨領導、馬克思列寧主義和毛澤東思想等四項基本原則,使中华人民共和国成为「富強、民主、文明」的社會主義現代化國家新華網. 四項基本原則 . 中國共產黨新聞網. [2016年1月22日] . 一個中心,兩個基本點 . 人民網. [2016年1月22日] .。中共十八大召開後,中国共产党提出全面建成小康社會、深化改革、依法治國與从严治黨的「四個全面」戰略布局周文彰和劉曉佳. 以「四個全面」引領行政文化建設 . 光明網. 2015年12月7日 [2016年1月22日] .。中共中央總書記習近平更提出「中華民族偉大復興」的「中國夢」,計畫在2020年全面進入小康社會,到2049年時建成社會主義現代化國家萬鵬. 辛鳴:「中國夢」的路線圖與時間表 . 中國共產黨新聞網. 2013年3月22日 [2016年1月22日] .。中共十九大将新提出的“习近平新时代中国特色社会主义思想”列入中国共产党章程。 === 政府結構 === 全國人民代表大" +312,"直轄市、縣、市、市轄區、鄉、民族鄉、鎮設立人民代表大會,产生本级人民政府、监察委员会、人民法院、人民检察院,各級人民代表大會由民主選舉產生,並對人民負責與受人民監督。選舉採分階段進行,在鄉鎮級、區縣級舉辦直接選舉Beijingers get greater poll choices . 《中國日報》. 2003年12月8日 [2016年1月22日] .。当选的鄉鎮級、區縣級人民代表大會再选出更高級別的人民代表大會,並由這些代表選出全國人大代表Sistemul politic . 中國百科. [2016年1月22日] .。不过因为沒有實質的反對黨,选举一般不會有激烈的竞争;中央政府通过官員任命能有效控制地方政府,中國共產黨亦掌握大部分權力,而各省級正副領導人具一定自主性Beina Xu和Eleanor Albert. The Chinese Communist Party . 美國外交關係協會. 2015年8月27日 [2016年1月22日] .。 中華人民共和國法律體系以馬克思主義法學理論為指導,發展出“有中國特色的社會主義法系”游勸榮. 中國法律體系的鮮明特色與 全球品質 . 新華網" +313,"名的法律《民法典》正式颁布,并于2021年1月1日起施行。 === 政治权利 === 《中華人民共和國憲法》規定的公民基本權利有言論自由、新聞自由、普遍選舉、宗教自由、集會自由、結社自由、公正審判權、財產權、教育與科學與文化權利、特定人權利及監督權利等。1998年中华人民共和国政府簽署《公民權利和政治權利國際公約》,不過全國人民代表大會尚未批准趙蕾. 學者建議人大批准公民權利和政治權利國際公約. 鳳凰網. 2008年1月11日 [2016年1月22日] .劉曉丹和羅德儀. 溫家寶總理中外記者會問答(全文) . 中國評論通訊社. 2008年3月18日 [2016年1月22日] .國務院新聞辦公室. China's Progress in Human Rights in 2004 (2005). 中華人民共和國政府. 2005年4月 [2016年1月22日] .。政府為維護社會穩定亦會審查政治言論和資訊流通China. 自由之家. 2012年 [2016年1月22日] .Michelle FlorCruz. In Rare Defiance, Chinese Journalists Pro" +314,"g-Deh),中國文化大學新聞暨新聞傳播學院院長、新聞學系教授、民國派Richard Bernstein, In Taiwan, Sympathies Lean Toward Home, New York Times, 4 June 1989, 121等不同中共黨外的政治群體,中共黨內又開始呈現改革開放的路線之爭,為此鄧小平則提出「四項基本原則」以應對。但政府仍嚴格限制政治自由,并嚴格管控社會團體的組建和活動Peter Patze. Service providers wanted . Development + Cooperation. 2012年8月2日 [2016年1月22日] .。部分政治界、學術界和民間人士曾公開支持民主化等政治體制的改革Keith B. Richburg. Chinese Premier Wen Jiabao talks reform, but most countrymen never get to hear what he says . 《華盛頓郵報》. 2010年10月13日 [2016年1月22日] .。中華人民共和國政府过往的改革行動主要由中國共產" +315,"2016年1月22日] .、折磨逼供、酷刑、限制基本權利China bans religious activities in Xinjiang . 《金融時報》. 2012年8月2日 [2016年1月22日] .新華社. China Bans Falun Gong. 《人民日報》. 1999年7月2日 [2016年1月22日] .、侵犯公民權的案件(如過度執行死刑)、刑事訴訟沒有有效保障China Events of 2008 . 人權觀察. 2008年 [2016年1月22日] .季·索爾孟. Empire of Lies: The Truth about China in the Twenty-First Century. 美國紐約: . 2010年3月2日: 第46、152頁 [2016年1月22日]. .等等。对于來自外界的指控,中華人民共和國政府則認爲生存權和發展权虽是最基本的人權,但應考慮國家經濟發展的水平。中國政府的觀點主張其已盡力讓該國公民的生活水平、識字率和平均預期壽命上升,並有效改善了工作場所的安全標準、支援抵禦自然災害和帮助农村贫困人口脱贫等Calum MacLe" +316,"西方國家質疑,認為香港原來的「一國兩制」已經名存實亡。2021年3月11日,第十三屆全國人大四次會議通過《關於完善香港選舉制度的決定》,外界認為此舉完全顛覆了原來的香港選舉制度。為了平息爭議和疑慮,中共中央總書記習近平在2022年香港特區成立二十五年之際到訪香港,提出「一國兩制必須長期堅持」的未來方針,並於同年召開的中共二十大中將「準確、堅定不移貫徹一國兩制」的內容列入《中共黨章》。 === 行政區劃 === 中華人民共和國行政區劃以省、地、縣、鄉四級架構為主,截止2022年12月31日,全國共有34個省級行政區、333个地級行政區、2843个縣級行政區和38602个鄉級行政區。其中,省級行政區包括23個省份、5個民族自治區、4個直轄市以及2個特別行政區David N. Keightley. China . 《大英百科全書》. 2015年12月28日 [2016年1月22日] .。中華人民共和國政府將全國劃分為東北、華北、華東、中南、西南、西北6個地區,並將4個直轄市和廣州列為國家中心城市;將瀋陽、南京、武漢、深圳、成都和西安列為區域中心城市《全國城鎮體系規劃綱要(2005-2020年" +317,"觀察國和領導國,積極爭取開發中國家的支持新華社. China says communication with other developing countries at Copenhagen summit transparent . 人民網. 2009年12月21日 [2016年1月22日] .。2003年,針對中國威脅論,以胡錦濤為總書記的第四代领导人提出中國和平崛起的外交政策,后修改為「和平發展」的论述。 目前,中华人民共和国與180個國家建立了大使級的外交關係,設有173個大使館。中华人民共和国对除海地之外其余未建立外交关系的国家的事务由驻其相邻的已建交国家的大使馆或领事馆代管。另外中华人民共和国有30个省級行政區和444个城市,這些城市和省級行政區與133个国家的488个省級行政區和1,499个城市建立了友好城市关系友城統計.截至2015年12月28日 . 中國國際友好城市聯合會. [2016年1月22日] .。截至2020年1月,中华人民共和国護照簽證受限指數為71,共71個國家實施免簽證和落地簽證,中华人民共和国护照在共产党执政的国家中排名第一。世界上绝大多数國家的公民持普" +318,"increased Chinese imports . 英國廣播公司新聞網. 2010年10月14日 [2016年1月22日] .Chinese President Hu Jintao resists Obama calls on yuan . 英國廣播公司新聞網. 2010年4月13日 [2016年1月22日] .,对美國有明显的貿易順差US says China not a currency manipulator . 英國廣播公司新聞網. 2012年12月27日 [2016年1月22日] .Doug Palmer. Obama should call China a currency manipulator: Romney aide . 路透社. 2012年9月24日 [2016年1月22日] .。中华人民共和国與俄羅斯在經濟、軍事等有密切關聯China, Russia launch largest ever joint military exercise . 德國之聲. 2013年7月5日 [2016年1月22日] .,在聯合國安全理事會经常意見一致Russia-China u" +319,"2日] .、並和阿根廷建立戰略夥伴關係张樵苏. 中华人民共和国和阿根廷共和国关于建立全面战略伙伴关系的联合声明 . 新華網. 2014年7月19日 [2016年2月19日] .An. China, Argentina agree to further strategic ties . 新華網. 2011年9月9日 [2016年1月22日] .。 中华人民共和国積極參與多邊關係以促進國際和平与經濟發展,是世界贸易组织、G20峰會、上海合作组织、亚太经济合作组织、孟中印缅区域合作论坛、亞洲相互協作與信任措施會議等組織的成员国辛聞. 中國關於聯合國成立70周年的立場文件:推動國際關係民主化 . 中國網. 2015年9月21日 [2016年1月22日] .。中华人民共和国、巴西、俄羅斯、印度和南非合称金砖五国,曾於2011年在中华人民共和国举行峰会Bric summit ends in China with plea for more influence . 英國廣播公司新聞網. 2011年4月14日 [2016年1月22日] .。中华人民共和国還向亞太鄰國提議擴大自由貿易區和安全條約,結合東" +320,"16年2月1日 [2016年2月18日] .。2014年,習近平表示中华人民共和国將致力於和其他國家合作,争取以和平方式解決在領土主權和海洋權益等問題Asian nations should avoid military ties with third party powers, says China's Xi . China National News. 2014年5月22日 [2016年1月22日] .。中華民國政府在第二次國共內戰後失去对中國大陸的统治,自1950年后仅統治臺灣地區Chinese Civil War - War of Liberation . Cultural China. [2016-01-22] .,美國的介入讓海峽兩岸關係具爭議且複雜。中華人民共和國聲稱擁有台灣地區的主權,並將「一個中國原则」視為與其他國家發展外交關係的前提,要求後者承認「台湾是中国的一部分」。此外,中華人民共和國还多次抗議美国向台湾銷售武器法新社. Taiwan's Ma to stopover in US: report . My Sinchew. 2010年1月12日 [2016年1月" +321,"土争端China denies preparing war over South China Sea shoal . 英國廣播公司新聞網. 2012年5月12日 [2016年1月22日] .How uninhabited islands soured China-Japan ties . 英國廣播公司新聞網. 2014年11月10日 [2016年1月22日] .。2012年,在釣魚島國有化事件后,中华人民共和国政府派遣海警船定期在釣魚臺列嶼海域巡邏巡航信息 . 釣魚島是中國的固有領土. [2016年1月22日] .,并于隔年在東海上空設立防空識別區李元傑. 中共公布東海防空識別區的大戰略意涵 . 中華民國國防部. [2016年1月22日] .。2013年1月22日,菲律宾单方面就中菲有关南海问题提起仲裁。2014年开始,中华人民共和国在南海的永暑礁、渚碧礁、美济礁等实控礁盘上,進行大规模填海工程并修建机场还原:中国已在南沙填出七座岛 . 中國日報. 2015年1月27日 [2016年2月22日] .蕭爾. 美國敦促中國停止在南沙島礁填海建機場 . 英國廣播公司新聞網. 2014年11月" +322,"6年1月3日 [2016年1月22日] .,為世界規模最大的正规军队,設有陆军、海军、空军、火箭军四个军种与军事航天部队、网络空间部队、信息支援部队、联勤保障部队四个独立兵种The new generals in charge of China's guns . 英國廣播公司新聞網. 2012年11月14日 [2016年1月22日] .China 'reveals army structure' in defence white paper . 英國廣播公司新聞網. 2013年4月16日 [2016年1月22日] .。中华人民共和国2019年軍事預算總額较上一年增长7.5%,達1.19万亿元人民币,僅次於美國。 ;武器装备 中华人民共和国拥有洲際彈道飛彈、弹道导弹核潜艇、战略轰炸机组成的三位一体的核打击能力。中华人民共和国近期研究開發了大量先進的飛彈技術亓樂義. 烏克蘭缺錢 洲際飛彈技術恐流向中國 . 風傳媒. 2014年4月12日 [2016年2月19日] .HongQi 9 Surface-to-Air Missile System. 今日中國防務. 2009年10月3日 [201" +323,"步,除購買苏-35等俄罗斯制战机外,也自行開發生產了隐身第五代戰鬥機、高超音速飛行器《中國日報》. 國防部回應「第三次高超音速飛行器試驗」 . 鳳凰網. 2014年12月10日 [2016年1月22日] .、重型运输机、无人机等現代化軍機P·W·辛格. INSIDE CHINA'S SECRET ARSENAL . 《科技新時代》. 2012年12月20日 [2016年1月22日] .。 中华人民共和国還以新研製的陸軍武器替換蘇聯製裝備,系統也從C3I增強至C4I,強化對於網絡中心戰的掌控Ground Forces. 今日中國防務. [2016年1月22日] .。2019年第一艘国产航母山东号正式服役。2020年首艘055型导弹驱逐舰南昌号开始服役,排水量达1.2万吨。2021年首艘两栖攻击舰海南号入列。目前解放军拥有8艘071型综合登陆舰,其坞舱可容纳4艘726型气垫登陆艇。另外,随着自2000年后开始建设的北斗卫星导航系统的逐步完善和发展,中华人民共和国在军用卫星导航方面也正在逐步摆脱对于美国GPS导航系统的依赖,2018年12月27日,北斗正式开始提供全球服务。 ;国防战略 中华" +324,"[2016年1月22日] .,而且在非洲吉布地建立了首個海外軍事基地美司令:中國簽訂首個「海外軍事基地」10年協議 . 搜狐. 2015年11月27日 [2016年1月22日] .。 == 科學技術 == 1960年代至1970年代,中华人民共和国相繼掌握兩彈一星與雜交水稻技術Q. Y. Yu. The Implementation of China's Science and Technology Policy. 美國聖塔芭芭拉: 格林伍德出版集團. 1999年10月30日: 第2頁 [2016年1月24日]. .;後來提出四個現代化、863計劃、973计划,強調科學研究傅高義. Deng Xiaoping and the Transformation of China. 美國劍橋: 哈佛大學出版社. 2011年9月26日: 第129頁. .,並改革學術制度。中华人民共和国在2009年有超過1萬名工程博士和50萬名理學士;出版数量世界第二,申請專利數世界第一,但也發生过多起学术腐败問題。2012年,政府投資1,630億美元,而研究開發经费位居世界第二名。近年来,中华人民共和国在航空-" +325,"过程。 ===化工=== 中华人民共和国于1965年成功实现人工合成结晶牛胰岛素,是世界首次实现人工合成蛋白质。氟硼铍酸钾晶体作为第一种可用直接倍频法产生深紫外波段激光的非线性光学晶体,其生长技术自1990年代被发现以来长期被中华人民共和国垄断。2021年中国科学院天津工业生物技术研究所在淀粉人工合成方面取得突破性进展,首次实现二氧化碳人工合成淀粉。中华人民共和国是世界稀土资源的主要提供者,在稀土的冶炼分离环节处于主导地位,是全球唯一具备稀土全产业链各类产品生产能力的国家。新材料产业方面,中华人民共和国的稀土功能材料、先进储能材料、光伏材料、超硬材料、特种不锈钢、玻璃纤维及其复合材料等产业产能居世界前列。 ===資訊科技=== 中华人民共和国擁有華為、聯想集團、小米、OPPO等個人電腦和手机企業;在移动通信领域,拥有5G、6G专利簇数量占比超过40%;在世界排名前500名的超級電腦中擁有173臺,為世界最多。2015年,中國電子信息產業集團研製首款ARM架構的64核晶片,2019年中芯國際亦開始量產14奈米級產品。2020年,中国人工智能创新指数排名第二,仅次于美国。 中华人民共和国亦" +326,"来,中华人民共和国航天技术发展迅速,至2021年12月,中华人民共和国已拥有酒泉、太原、西昌、文昌四大航天发射场,其长征系列火箭已完成400次发射,年发射次数位居世界前列。北斗卫星导航系统是中华人民共和国研发制造并管理的全球卫星导航系统,为联合国卫星导航委员会认定的全球卫星导航系统四大核心供应商之一。2020年7月31日,北斗卫星全球导航系统正式开通。 自21世纪以来,中华人民共和国在深空探测方面取得了大量成就。中国探月工程自2004年立项以来先后发射了嫦娥一号(2007)至嫦娥五号(2020年)探测器,实现了“绕”、“落”、“回”的任务。中国火星探测任务中,天问一号探測器包含轨道器、着陆器和祝融号火星车一次性完成了“绕、落、巡”三大任务。中国载人航天工程从1992年正式启动,至2022年完成了“三步走”的目标。第一步,发射神舟五号、神舟六号实现了载人航天。第二步,发射了神舟七号至神舟十一号载人飞船以及天舟一号货运飞船与天宫一号、天宫二号空间实验室。2021年至2022年间,先后发射了天和核心舱、问天实验舱与梦天实验舱组成了中国空间站三舱“T”字基本构型。随着2022年底的中国空间站的" +327,"16年1月22日] .Harsha V. Singh. 成为WTO的一员:对中国和全球贸易的影响 . . 2012年1月15日 [2016年3月15日] .。政府在1980年代廢除集體耕作、設立經濟特區、國有企業改革重組,讓中华人民共和国成為世界-{zh-cn: 增长; zh-tw: 成長}-最快的經濟體。中華人民共和國國內生產總值在1978年至2007年平均實質增長9.8%唐偉傑. 中國30年年均經濟增長率是同期世界的3倍多 . 中國新聞社. 2008年12月18日 [2016年1月22日] .,國民生產總值增長近十倍中華人民共和國國家統計局. 國內生產總值指數. 北京: 《中國統計年鑑—2007》. 2007年 .,人口貧困率從1970年代末的64%下降至2004年的10%以下。1990年代中华人民共和国凭借國家級經濟技術開發區和國家綜合配套改革試驗區(包括浦東新區、濱海新區和深圳市)深化改革開放張瑜. 津京滬穗渝入圍國家中心城市 區域發展戰略調整 . 北方網. 2010年2月8日 [2016年1月22日] .。1980年中华人民共和国GDP总量仅为3053亿美元(IMF数据),经" +328,"5年 [2016年1月22日] .。中华人民共和国從1990年的8家上市公司加至2010年的2,062家境內上市公司數量20年增長了150倍 . 網易. 2010年10月19日 [2016年1月22日] .,另有1.3億戶投資者、106家證券公司、62家基金公司和163家期貨公司馬婧妤. 中國股票總市值全球第二. 金融界. 2010年12月31日 [2016年1月22日] .。2014年第一产业、第二產業和第三產業分别占國內生產毛額的9.2%、42.6%、48.2%2014年1-4季度我国GDP(国内生产总值)初步核算情况 . 中華人民共和國國家統計局. 2015-01-21 [2016年1月22日] .,另外電子訊息產業占2.5%中國统计年鉴2016 . 中华人民共和国国家统计局. [2016年1月22日] .、文化產業占3.76%国家统计局:2014年文化产业增加值占GDP比重为3.76% . 中國經濟網. 2015年11月26日 [2016年1月22日] .。中华人民共和国經濟增长动力从工業转向第三產業2002及2011年中國三次產業結構比較圖 . 中商情報網. 2012年8月2" +329,"aze for luxury goods . 中國日報. 2010年3月3日 [2016年1月22日] .。經濟發展亦造成貧富差距Jennifer Duggan. Income inequality on the rise in China . 半島電視台. 2013年1月12日 [2016年1月22日] .,在數十年來不斷增加Damian Tobin. Inequality in China: Rural poverty persists as urban wealth balloons . 中華人民共和國國家統計局. 2011年6月29日 [2016年1月22日] .Steep rise in Chinese food prices . 英國廣播公司新聞網. 2008年4月16日 [2016年1月22日] .,2012年的吉尼係數為0.474Income inequality Delta blues . 《經濟學人》. 2013年1月23日 [2016年1月22日] .。經濟快速-{zh-cn: 增长; zh-tw: 成長}-及消費市場擴張造成國內嚴重的通貨膨脹彭博新聞社. Chi" +330,"將在21世紀成為具重大影響力的世界角色。也有评论者表示泡沫經濟威脅和人口結構失衡,將延緩甚至阻止中华人民共和国在本世紀的經濟成長;或質疑中华人民共和国作為「超級大國」的定義,認為中华人民共和国並沒有美國的軍事和文化影響力。 === 经济概况 === 2022年全年,中華人民共和國各省級行政區及特別行政區人均生產總值分布,2022年中國大陸最富有的地區爲长三角,珠三角和京津冀等沿海地区。2019年中华人民共和国经济的三产业比重分别为农业7.1%,工业39.0%,服务业53.9%(統計數字包括國家限制開發區域及禁止開發區域)。 中华人民共和国改革开放的主要目的是希望从高度集中的计划经济转变为市场经济,藉此解放和发展生产力。在中华人民共和国國際經濟策略的影响下,第二產業成爲中华人民共和国經濟的重要部分。中华人民共和国的劳动力成本较低,爲第二產業的發展創造出適合環境。以中美經貿關係爲例,1972年中美关系恢复,美國为实现生产要素优化配置,將其大部分勞力密集工業委托给中华人民共和国。世界其他發達國家亦有不同程度的將其低技术产业轉而至中华人民共和国,中华人民共和国遂成爲製造業第一大國張瑜. 工業:" +331,"第一产业增加值65468亿元,增长3.9%;第二产业增加值334623亿元,增长6.1%;第三产业增加值427032亿元,增长8.0%。中华人民共和国自2011年超越日本,成为僅次於美國的第二大經濟體《中國統計年鑑—2007》. 北京: 中華人民共和國國家統計局. 2007年 .,而依購買力平價则為世界最大的經濟體。 中华人民共和国經濟第二產業占比较高,其一是造成中华人民共和国人均國內生產總值偏低;2017年人均國內生產總值依国际汇率為8,643美元,列全球第72位;依購買力平價则為16,695美元,列全球第79位。其次是生態環境的破壞。此外,中华人民共和国廠商生產出的商品並不滿足國內市场,发达国家卻因其具有价格竞争优势而对此类产品进口需求巨大,中华人民共和国廠商會出口其商品至其他国家換取利润,這種經濟模式受制於進出口贸易環境及受控於國際贸易政策;匯率波动、進出口税等都會影响中华人民共和国廠商的利润。在中华人民共和国工資水平上升、出口贸易飽和、國際貿易環境嚴竣,及其他經濟誘因底下,中华人民共和国工业發展將面临瓶颈;2010年後-{zh-cn: 增长; zh-tw: 成長}-明顯地減速黃" +332,"的593亿人民币增长到2010年的18400亿人民币。复合年均增长率为36.65%。現時中华人民共和国软件产业面对的挑战是填補自身的核心系统软件和工业软件领域的缺陷,例如對操作系统、数据库、编程语言、计算机輔助設計软件、計算機輔助工程软件、電子設計自動化軟件的研发,減少依赖外国软件,去降低外部壓力造成的生产风险,以滿足未来中华人民共和国在复杂工業的自动化控制需求。科技日报.核心工业软件:智能制造的中国“无人区” . 人民网. 2018年5月17日 [2018年10月16日] .迄今中华人民共和国政府預算收入和支出都位居世界第二名中央情報局. FIELD LISTING :: BUDGET . 《世界概況》. [2016年1月22日] .,2010年的財政赤字有人民幣1兆元《環球時報》. 中國財政部研究人員:中國2010年財政赤字約占GDP的2.8% . 環球網. 2011年1月5日 [2016年1月22日] .;但政府因經濟增幅放緩而考慮擴大財政赤字中國考慮擴大財政赤字 . 法國國際廣播電臺. 2015年12月30日 [2016年1月22日] .,2015年時達到人民幣1.62兆元韩洁" +333,"te-Sector Economy . 《彭博商業周刊》. 2005年8月22日 [2016年1月22日] .,而在2008年有3,000萬家已註冊的民營公司China has socialist market economy in place . 人民網. 2005年7月13日 [2016年1月22日] .。2010年末,中國工商銀行、中國建設銀行和中國銀行名列世界銀行前十名陳四清. 中国银行副行长陈四清在《财经》杂志刊登署名文章 . 中國銀行. 2011年12月20日 [2016年3月15日] .。《-{zh-hans:财富; zh-tw:財星; zh-hk:財富;}-》在2015年-{zh-hans:财富世界500强; zh-tw:財富世界500大; zh-hk:財富世界500強;}-中有106家中國大陸公司上榜(占總數21.2%,數量僅次於美國)《-{zh-hans:财富; zh-tw:財星; zh-hk:財富;}-》. Global 500 . -{zh-hans:财富世界500强; zh-tw:財富世界500大; zh-hk:財富世界500強;}-. 2015年 [2016" +334,"品期貨市場成交量居世界首位。 === 能源与矿产 === 中华人民共和国經濟具高度能源密集和耗能傾向China . 世界銀行. 2014年 [2016年1月22日] .,為能源消耗量最大(溫室氣體排放量最大)、及能源生產最多的國家Spencer Swartz和Shai Oster. China Tops U.S. in Energy Use . 《華爾街日報》. 2010年7月18日 [2016年1月22日] .。2014年生產約5.523兆千瓦·時電力中央情報局. COUNTRY COMPARISON :: ELECTRICITY - CONSUMPTION . 《世界概況》. 2012年 [2016年1月22日] .,發電裝機容量有13.6億千瓦(其中火電915吉瓦、水電301吉瓦、風電96吉瓦、太陽能發電27吉瓦、核电20吉瓦),220千伏以上的電力線路達57.2萬公里中共中央辦公廳. 中共中央國務院 關於進一步深化電力體制改革的若干意見 . 界面新聞. 2015年3月22日 [2016年1月22日] .,發電量與電網規模都為世界第一中國電力工業聯合會:中國發電量躍居世界第一 ." +335,"地,风力发电多分布在内蒙古、新疆、甘肃、河北、山东等地,占全国装机容量的一半以上。东部沿海共有30座核反應爐機組運轉,總裝機容量達2,831萬千瓦,並有24座在建機組,機組數量僅次於美國和法國。 中华人民共和国礦產資源豐富,已知礦產有171種、探明儲量者有158種;當中石油、天然氣、煤、鈾、地熱等能源礦產10種,鐵、錳、銅、鋁、鉛、鋅等金屬礦產54種,石墨、磷、硫、鉀鹽等非金屬礦產91種。附近海域石油約250億噸,天然氣有8.4萬億立方公尺。 中华人民共和国長期是世界上最大的煤炭生產國及消費國,超過70%能源依靠煤炭供應Mamta Badkar. The Ultimate Guide To China's Voracious Energy Use . 《商業內幕》. 2012年8月17日 [2016年1月22日] .。中华人民共和国石油產量則位居世界第四,2014年每天產量達4,189,000桶中央情報局. COUNTRY COMPARISON :: CRUDE OIL - PRODUCTION . 《世界概況》. 2012年 [2016年1月22日] .。但因石油生產不能滿足經濟需求" +336,"為4.17億辆,其中汽车3.19亿辆。 截至2022年,中國鐵路總公司經營的鐵路里程數共計约16萬公里,其中高速铁路超4万公里,為全世界客運和貨運最繁忙的網絡,每年春節等假期都會出現人潮,通称春运。中华人民共和国在21世紀初建立世界最長的高速鐵路。2016年9月,随着郑徐高铁的开通,中华人民共和国高铁里程突破2万公里,形成四縱四橫格局并向八纵八横发展,主要线路由京廣高速鐵路、京滬高速鐵路等。中华人民共和国还向印度尼西亚,泰國、歐洲聯盟、寮國、美國等輸出高铁技術。 截至2022年1月,中國大陸累计超过50个城市开通城市軌道交通系統,运营线路总长度接近9,000公里,其中上海地鐵、北京地鐵、廣州地鐵等城市軌道交通系統的通車里程位居世界前列,由于是政府运营管理的关系,中國大陸的地铁票价非常平价。另有時速431公里的上海磁浮示範營運線,為世界上最快的商業軌道。 底,中华人民共和国共有颁证民用航空运输机场254个(不含港、澳、台地区),定期航班航线里程超过838万公里。2015年機場旅客達9.15億人次、貨郵達1409萬噸、飛機起降856.6萬架次。“十三五”期间(2016-2020年)将新建至" +337,"客周轉量32.99億人公里。 截至2022年末,中华人民共和国铁路营业里程15.5万公里,其中高铁营业里程4.2万公里。公路总里程超528万公里,国内河航道通航里程12.77万公里,其中三级及以上航道里程1.44万公里。全国港口生产用码头泊位22142个,全国港口万吨级及以上泊位2592个其中沿海港口万吨级及以上泊位2138个内河港口万吨级及以上泊位454个。 === 商贸合作 === 中华人民共和国是世贸组织、亞太經合組織的成員,亦是金砖国家之一。2020年中国签署《区域全面经济伙伴关系协定》,这是世界上最大自由貿易經濟體系。中国是世界第一大贸易国黃林昊. 中國成為最大貿易國之際訪商務部部長高虎城 . 中華人民共和國政府. 2014年3月1日 [2016年1月22日] .,2022年出口額3.7万亿美元位居世界第一、進口額2.7万亿美元位列世界第二,进出口保有大量顺差,前三大外贸伙伴是东盟、欧盟和美国。 2009年推動人民幣國際化,2016年國際貨幣基金組織將人民幣納入特別提款權。截至2022年4月,人民币占外汇交易量的7%,排名第五。截至2023年5月,包括阿根廷、巴西、俄羅斯、沙" +338,",而60歲以上的人口佔13.26%。2013年時,中华人民共和国人口增長率估計為0.46%。中华人民共和国在1978年約有64%民眾為貧窮人口,但1978年經濟快速增長後,已經有數億名民眾脫離窮困生活。今日約有10%人口仍處在貧窮線以下,城鎮失業率在2007年一度降到4%以下,目前城鎮失業率則在4.1%。 中华人民共和国新生嬰兒的男性比例偏高,2010年人口普查显示男女比例為118.06比100,比正常的105比100的男女比例高。2010年人口普查中,男性佔總人口的51.27%左右;不過相較於1953年男性佔總人口51.82%左右,今日中华人民共和国性別比例較為平衡許多。据全国老龄委2015年预计,未来20年中华人民共和国将进入老龄化高峰。到2050年左右,中华人民共和国老年人口将达到全国人口的三分之一。人口老龄化将使中华人民共和国劳动力人口逐渐减少。截至2017年底,中华人民共和国60岁以上老年人口约2.41亿,占总人口的17.3%,高于10%的联合国传统老龄社会标准。 ==== 各地區人口密度 ==== 中华人民共和国整体人口密度是每平方公里145人,有96%的人口生活在占全国总" +339,"和城市影響力。而100萬人至500萬人的城市為大城市,是都市化进程以及推動各省地方性文化发展的主力,其對中华人民共和国城市的未來樣貌有著重要影响。 ==== 族群与移民 ==== 中华人民共和国是多民族國家,政府登記而正式承認的民族群體共有56個,合稱「中華民族」。中华人民共和国最大族群為漢族,佔總人口約91.51%。中华人民共和国的漢族是世界上最大的漢族族群,漢族在大部分省級行政區中多於其他族群,僅有西藏自治區和新疆維吾爾自治區為例外Gregory Veeck、Clifton W. Pannell、Christopher J. Smith和Youqin Huang. China's Geography: Globalization and the Dynamics of Political, Economic, and Social Change. 美國蘭哈姆: . 2011年7月16日: 第102頁 [2016年1月24日]. .。漢族以外的55個民族則被稱為「少數民族」,人數較多者有壯族(1.30%)、滿族(0.86%)、回族(0.79%)、苗族(0.79%)、維吾爾族(0.72" +340,"能撫養一個孩童,在傳統偏愛男孩的觀念和務農需勞動力的情況下,以農村為首的地區常違背此規定,按規定超生需上交罰款。根據2010年人口普查,当时中华人民共和国的總和生育率可能達1.4左右,沒有登記的黑戶非常多,因而這些人常沒有基本公民應有的保障以及可能伴隨之而來的家庭衝突成為急需解决的社會問題。 在中华人民共和国人口持續增長而超過13億人、及自然資源日益減少的情況下,政府起初非常關心人口的增長率,並利用政策工具進行控制。然而21世紀以後生育率長期較低,不足以撫養老年人,為了解決中华人民共和国的老齡化危機,2013年,政府在《中共中央關於全面深化改革若干重大問題的決定》頒布新政策,同意父母一方為獨生子女時,家庭便能生育2名孩童,藉此促進人口均衡發展,以放開壓縮的出生率,專家預期可達到2甚至以上被釋放出來,但次年統計後發現存在嚴重誤判,生育反響出乎意料的微弱,顯示生育率並非受到政策壓力而低,而是处於危險的下降水平已久。國家人口和計劃生育委員會原本计划一胎化政策會延續到2020年甚至更久,但政府考慮形勢變化異常嚴峻,在2015年加速提前取消一胎化政策,改以兩孩政策取代,政策轉變僅用时4年,但仍被" +341,"hinese Characters. Multilingual Matters. 2008年8月7日: 第42頁 [2016年1月24日]. .。另外一方面,現今少數族群使用的地方口語語言,則有蒙古語、藏語、維吾爾語、哈薩克語、朝鮮語、彝语、壯语等。 === 教育 === 中华人民共和国自1986年以來实行九年义务教育,包含6年小学和3年初级中学,学生年龄在6岁至15岁之间。2010年,約有82.5%的學生會選擇繼續為期3年的高級中學教育。由政府舉辦的普通高等學校招生全國統一考試,則是進入大多數高等教育機構的先決條件。2010年,27%的高級中學畢業生會繼續接受4年的本科教育。另外政府還設立中級和高級專科學校,提供學生職業教育內容。2019年,中华人民共和国共计有161,811所小学、51,982所初级中学、13,737所高级中学、10,299所中等职业学校、以及2663所普通高等学校和1418所高职(专科)院校。 thumb|216x216px|清华大学常被认为是中华人民共和国排名第一的大学。图为清华大学的二校门。 2010年数据显示,15歲以上人口中有94%接受過教育訓練、而得以識" +342,"獲批准的宗教組織会遭到政府機關的壓制。根據2015年進行的民意調查,61%民眾認定自己為「堅定的無神論者」。2006年研究指出,有46%民眾信奉宗教;而2007年調查指出,16歲以上人士中有31.4%信奉宗教。數千年來,中华人民共和国文明受到各種宗教運動的影響;其中有道教、佛教和儒教Xinzhong Yao和Yanxia Zhao. Chinese Religion: A Contextual Approach. 英國倫敦: 布魯姆斯伯里出版社. 2010年5月1日: 第9頁至第11頁 [2016年1月24日]. .Chinese Religions in Contemporary Societies. 美國聖塔芭芭拉: . 2006年4月3日: 第57頁 [2016年1月24日]. .,常被納入民間信仰中Zhibin Xie. Religious Diversity And Public Religion in China. 英國法納姆: . 2006年12月11日: 第73頁 [2016年1月24日]. .。許多知識分子將儒家視為宗教。 從人口結構來看,分佈最廣的宗教是和道教結合的" +343,"的麽教、羌族的多神信仰、及回族和維吾爾族的伊斯蘭教。伊斯蘭教最早在唐朝由穆罕默德叔父瓦卡斯出使唐朝時引入,之後因阿拉伯商人而在南方沿海發展,並於廣州市建立首座清真寺懷聖寺。現今中华人民共和国穆斯林人口約有2,000多萬人,在非穆斯林為主的國家中排名第三。過去藏區信奉的傳統宗教為苯教,不過今日大部分藏族則信奉受密宗影響的藏傳佛教Lee W. Bailey. Introduction to the World's Major Religions. 美國聖塔芭芭拉: 格林伍德出版集團. 2006年: 第65頁 [2016年1月24日]. .。 == 社會 == === 醫療衛生 === 国家卫生健康委员会連同轄下各行政區的地方委員會,負責管理民眾健康事務。中华人民共和国社会医疗保险依不同人群而有不同就医支付保障,如城鎮居民享有城镇居民医疗保险、农村居民則由新型农村合作医疗制度提供保障。2009年,政府宣布展開為期3年的大規模醫療保健制度,提供醫療補助和相關健保,並主動投資1,240億美元。2011年時,該計畫讓95%人口擁有基本的醫療保險。2013年,中华人民共和国藥品市場排行世界第二名,但" +344,"共有1,519座污水處理廠,同時每星期增加18座處理廠。儘管民眾健康狀況顯著改善、而先進醫療設施陸續建設,中华人民共和国開始出現一些新興的公眾健康問題,包括嚴重空氣污染所引起的呼吸系統疾病、數百萬名吸煙人士、及城市地區青年人口不斷增加的肥胖症。 中华人民共和国发生的重大疫情有:从70年代开始的乙肝大爆发,在短短20年中,中华人民共和国乙肝病毒携带者增长至1.2亿人,成为世界第一乙肝大国;1990年代中期的艾滋病大规模传播事件,目前中國大陸愛滋病感染人數估計約有43萬至150萬人間,2014年有49.7萬起感染愛滋病案例、及15.4萬名患者喪生,而在該年度則新增10.4萬例感染患者;2002年底爆發的SARS疫情,持续到2003年才得以控制;2019年底湖北省武汉市爆发了2019冠状病毒病疫情,疫情严重程度远超2003年的SARS疫情。2020年1月,全国陆续采取“封城措施”阻止病毒蔓延。通过严格的清零政策,中华人民共和国一度控制住了境内疫情。随着自身經濟發展受严重影响,民众对「過度防疫」的批评不断增多和全国各地陆续爆发白纸运动的形势下,国务院联防联控机制于2022年12月7日发布“新十" +345,"視劇、娛樂節目等內容,嚴格審查個別通訊傳播的不當內容、及核准外國作品上市。政府禁止發布的主題有西藏自治區和新疆維吾爾自治區的抗議事件、公眾示威、反政府制度、不同政見者等,避免出現威脅國家統一、主權、領土完整和安全的內容。這讓一些外國電影因政府審查而遭取締禁止,國外傳播媒體亦受嚴格限制。 隨著網際網路在中华人民共和国國內普及,政府也對網際網路的內容進行系統性審查。Google、Facebook、Twitter、YouTube、Flickr、LINE、Instagram、路透社、維基百科等源于外國的网络服务,因不同意接受自我审查,而無法進入中國大陆。因不符合中华人民共和国的網路安全法,這些網站遭中华人民共和国政府相關的網絡安全管理中心屏蔽,中国大陆的網民亦無法直接訪問這些網站;而部分網站如-{zh-cn:必应;zh-tw:Bing;zh-hk:Bing}-、Flipboard等則事前接受審查標準,再推出特别的中國大陸地區服務。另外中华人民共和国也有類似功能的網站服務以取而代之,包括QQ空間、新浪微博、百度。2019年6月30日,QQ空間月活跃账户数达到5.53亿名,微信的月活跃账户数达到1" +346,"成比例的稅收,這讓許多民眾陷入貧窮。2000年代初開始,農村的徵稅已經降低或取消,並為提供居民更多社會服務。2013年,中國共產黨將土地、財稅、金融、收入分配等問題視為實行改革的目標王建華、曹凱和賴雨晨. 三中全會:經濟體制改革成全面深化改革重點. 《亞太日報》. 2013年11月13日 [2016年1月22日] .。隨著貧富差距加大,犯罪率平均每年增長約14%,並持續-{zh-cn: 增长; zh-tw: 成長}-。2009年,中华人民共和国發生990萬起治安案件,案件增長率達20%,謀殺案發生率為每10萬人1.1起。而自2008年以來,境內恐怖主義活動明顯增加,在2011年時全球恐怖主義指數超過美國、而接近以色列。 2012年,國家預防腐敗局指出過去30年有420多萬名黨政人員受懲處,465人是省部級官員。在2014年時,中华人民共和国清廉印象指數得分僅36分而屬於高腐敗國家。在中共中央委員會總書記習近平和中央紀律檢查委員會書記王岐山上任後,第十八次全國代表大會後的反腐敗工作持續擴大與深入人民網. 法媒:中國2014年或選某個 大老虎 作為反腐突破口 . 《環球時報》. 2014年" +347,"時尚和建築等都出現大規模的復興運動。 === 文學 === 因五四運動和新文化運動的推動,普通民眾開始閱讀官話白話文。其間,中國大陸出現許多傑出的文學家柳詒徵. 《中国文化史》. 長沙: 嶽麓書社. 2010年1月1日. .,胡適、魯迅等人成為中國現代文學先驅。中華人民共和國成立后,現代文學則依循社會主義方向,表現社會大眾革命精神與歌颂新生活樣貌。文化大革命結束後,受魔幻寫實主義影響而出現朦朧詩、傷痕文學、尋根文學等新興體裁。受到金庸武俠小說影響,虛構小說在漢字文化圈廣為流行。2000年,高行健成為首位獲得諾貝爾文學獎的华裔作家;2012年,莫言則成為首位獲得此獎的中华人民共和国作家。另外劉慈欣的科幻作品《三體》,則接連獲得星雲獎提名和雨果獎最佳小說獎。路遥、汪国真、王朔、余華、王小波、韩寒等作家也红极一时。 === 表演艺术 === 中华人民共和国民族音樂包括汉民族音乐和少数民族音乐,也包含传统音乐与新音乐。其中,新音乐是在20世紀至21世紀受西方樂曲影响而出現的新風格音樂。許多流行音樂結合過去傳統元素,發展出具特色的中文流行音樂。 中华人民共和国舞蹈表演包括民間舞蹈、傳統舞蹈、歌劇" +348,"喜剧小品、二人转、双簧、快板书等侯宝林、汪景寿和薛宝昆. 《曲艺概论》. 北京: 北京大學出版社. 1980年 [2016年3月20日].。 1896年出現首部中國題材的電影,但首部中國製作的電影為1905年《定軍山》,由當時京剧演員演出Chinese Film History - A Short Introduction. 奧地利維也納: 維也納大學. 2006年 .。在整個20世紀,中国大陆电影因政治局勢的發展而有所波折。1970年代後,中华人民共和国電影開始在其他國家放映。直到1997年,馮小剛執導的《甲方乙方》成為首部獲商業成功的中华人民共和国電影,在西方評論界取得廣泛讚譽Rui Zhang. The Cinema of Feng Xiaogang: Commercialization and Censorship in Chinese Cinema After 1989. 香港: 香港大學. 2008年10月7日 [2016年1月25日]. .。中华人民共和国所有放映的電影都須經過國務院批准,並要求刪除暴力、情色或敏感政治議題等場景,也限制外國電影進口數量,此舉協助自身電影產" +349,"筑设计基本方针中共中央、国务院.关于进一步加强城市规划建设管理工作的若干意见.2016-02-06。中国现代建筑的代表有東方明珠廣播電視塔、上海中心大厦、上海環球金融中心、國際金融中心、廣州塔等高层建筑或摩天大楼,以及國家體育場(鸟巢)、国家游泳中心等结构独特的建筑。 === 節日 === 中國大陸境內主要使用公曆和農曆,國務院將新年、春節、清明節、勞動節、端午節、中秋節和國慶節訂為“全體公民放假的節日”。此外,國務院還訂有國家公祭日、抗戰勝利紀念日等歷史紀念日;國際婦女節、五四青年節、兒童節、建軍節等特定紀念日《全國年節及紀念日放假辦法》. 北京: 中華人民共和國國務院. 2013年12月11日 .。此外,民族自治地方亦有特定的假日,如藏族雪頓節、穆斯林古爾邦節和開齋節、傣族潑水節、壯族三月三等。中國大陸實行五天工作制,規定雙休日、法定假日和帶薪休假為公眾假期。政府還設有調休制度,將雙休日與法定假日相連為「小長假」或「黃金周」《國務院關於修改〈全國年節及紀念日放假辦法〉的決定》. 北京: 中華人民共和國國務院. 2007年12月27日 .;2011年後,當法定假日適逢周末時,則會在工" +350,"京市、上海市、廣州市、西安市、成都市、青島市、廈門市、杭州市、重慶市等,而著名景點有長城、北京故宮、秦始皇陵兵馬俑、桂林山水、長江三峽、莫高窟、布達拉宮、九寨溝、黃山、苏州古典园林、西湖、泰山等。 泰山日出 - Sunrise at Mount Taishan - 2012.07 - panoramio.jpg|山东的泰山为五岳之首,亦为符合最多标准的世界遗产。图为从瞻鲁台上看到的日出之景 西湖平湖秋月.JPG|杭州西湖为中国传统文化的代表性景观之一。图为西湖十景之首“平湖秋月” Old Dragon's Head of Great Wall 2.jpg|万里长城是中华文明的重要象征。图为位于河北秦皇岛的明长城的入海处“老龍頭” 1 jiuzhaigou valley sleeping dragon falls 2011.jpg|四川九寨溝風景名勝區于1992年被联合国教科文组织列為世界遺產。图为九寨沟的卧龙海 Zhangjiajie National Forest Park 37828-Zhangjiajie (48757572061).jpg|张家界武陵源的石英砂岩峰林地貌为世界罕" +351,"廣播體操、廣場舞等運動能提升身體體適能能力,而在中华人民共和国廣受歡迎。2015年,國家體育總局推出12套廣場舞優秀作品;同年中华人民共和国舉辦133場馬拉松活動,並計畫在隔年增加至200場。 今日中华人民共和国社會歡迎的運動有中國武術、籃球、足球、乒乓球、羽毛球、游泳等,當中許多城市年輕民眾喜愛練習足球和籃球。中國足球協會超級聯賽於1994年成立後,發展出亞洲最大的職業足球市場。在中华人民共和国,許多年輕球迷關注中國男子籃球職業聯賽和美國NBA,也有像姚明、易建聯等球員參與這兩項職業籃球聯賽。中华人民共和国也流行圍棋、象棋、麻將、西洋棋等圖版遊戲,除了舉辦專業比賽外,還培養出職業圍棋選手柯潔、西洋棋大師侯逸凡等人。同時還有許多參與-{zh-cn:自行车运动; zh-hk:踩單車運動; zh-tw:腳踏車運動;}-的民眾。另外許多傳統體育項目也獲得廣泛歡迎,包括龍舟競賽、蒙古搏克、賽馬比賽等。 1984年,許海峰在夏季奧林匹克運動會上贏得首枚金牌。隨著體育競賽水準提升,中华人民共和国在夏季奧林匹克運動會贏得的金牌數量快速增加,也因而被譽為「體育大國」。中华人民共和国在2008年於北京舉" +352,". * Meng Fanhua. The Phenomenon of Chinese Culture at the Turn of the 21st Century. 新加坡: Silkroad Press. 2011年3月. . * Ye Sang. China Candid: The People on the People's Republic. Oakland, CA: 加利福尼亞大學出版社. 2006年11月4日. . == 外部連結 == * 中华人民共和国中央人民政府门户网站 * 中国网 * 中华人民共和国文化和旅游部 * 中华人民共和国外交部 * 中华人民共和国自然资源部 * * == 导航链接 == * * * * * * * * Category:1949年中國建立 Category:1949年建立的國家或政權 Category:一黨制國家 Category:共产主义国家 Category:共和国 Category:联合国会员国 Category:世界贸易组织成员 Category:二十國集團成員國 Category:APEC成员经济体 Category:金砖国家成" +353,"中国计算机科学大事年表按照时间顺序罗列了中华人民共和国建国以来,在计算机科学领域发生的重大事件。 ==大事记== 中华人民共和国计算机科学大事记 时间 事件 研发单位 1956 3月,闵乃大、胡世华、徐献瑜、张效祥、吴几康等组成代表团参加在莫斯科办的“计算技术发展道路”国际会议。从苏联“取经”,为中国制定12年规划的计算机部分作技术准备。12年科技规划中确定中国科学院成立计算技术、半导体、电子学及自动化等四个研究所。 1956 华罗庚受命筹建中国科学院计算技术研究所 1956 夏培肃设计了中国第一台电子计算机运算器和控制器,同时编写了中国第一本電子計算機原理讲义。 1956 复旦大学数学系和物理系师生合作研究、设计,成功研制中国第一台大型电子模拟计算机——复旦601型电子积分机。 1956 清華大學創辦半導體專業。 1957 哈尔滨工业大学研制成功国产第一台结构式模拟计算机。 1958 哈尔滨工业大学研制成功国产第一台能说话、会下棋的数字计算机。清华大学和东北大学(原名东北工学院)分别研制成功一台非线性电子模拟计算机和一台模拟电子计算机。 1958 中国第一台计算机103机(即DJS-" +354,"基础,开创了国产晶体管成功应用于计算机的先河。该机研制成功,奠定了此单位成为后来巨型计算机系列“银河”和“天河”诞生的揺蓝。 长沙国防科技大学(原中国人民解放军军事工程学院) 1965 DJS-II型机的操作系统编制完成 1965 108乙型机由华北计算所设计,内存储器由2组大磁芯体构成,可以内存32768个全字长单元,外存储器由4个磁鼓构成,每个磁鼓存储量16384个全字长单元,还可配有磁带机,每个磁带机的容量是磁鼓的16倍。输入采用光电输入和电传打字机输入两种,使用打印机输出,使用修改后的ALGOL-60。北京有线电厂生产,前后共生产56台。 华北计算所 1965 筹建电子计算机外部设备研究所,集中了一大批技术力量,同时开展外部设备中最薄弱的磁盘机,磁带机,打印机的研制工作。 1965 中科院计算所研制成功中国国第一台大型晶体管计算机109乙机,浮点32二进制位,每秒6万次。 中国科学院计算所 1967 中国科学院计算所研制成功109丙大型晶体管计算机。字长48位,内存容量分数据存贮器32K字,指令存贮器4K字,运算速度每秒11.5万次。 中国科学院计算所 1970 哈尔滨中国人民" +355,"它带动了我国计算机产业的发展,全国有三十多个工厂相继投产过DJS 100系列的130机、高端的140机和142机、低端的120和110机,和其他派生机型;它带动形成过我国计算机应用的一个高潮,DJS 100系列是从1956年到改革开放前那段国产计算机中产量和应用最多的计算机机型。据统计,DJS 100系列各个型号的计算机生产总量一共达到一千多台。 清华大学、华东师范大学等单位 1976 DJS-183机大型通用集成电路电子计算机研制成功,软件与美国DEC公司PDP-11系列兼容 华北计算所和国营南丰机械厂联合研制 1977 中国第一台微型计算机DJS-050机微型机通过鉴定,该机CPU为14片电路组成,兼容Intel 8080A。 清华大学、安徽无线电厂和四机部电子技术推广应用研究所等联合设计 1977 银河巨型计算机由慈云桂主持开始研制,长沙国防科技大学(原中国人民解放军军事工程学院)承担研制工作。 长沙国防科技大学(原中国人民解放军军事工程学院) 1978 DJS-051A 微机完成研制,指令与Intel 8080A一致,CPU 8位,主频500Khz~1Mhz,由4片IC组成,输" +356,"运算450万次的集成电路计算机HDS-9机 华东计算技术研究所 1979 王选教授用中国第一台激光照排机排出样书。 1980 DJS-210计算机通过系统鉴定 南京大学、常州计算机厂和广州计算机厂等单位联合设计 1981 GB2312国家标准《信息处理交换用汉字字符集(基本集)》颁布实施 1982 DJS153小型计算机通过鉴定。该机为100系列的高档机,字长16位,内存容量128K字,运算速度每秒125万次。先后在天津、潍坊、苏州和上海投产。 天津计算机研究所(原无线电研究所) 1982 DJS185小型计算机通过鉴定,字长16位,内存容量16~128K,运算速度每秒50万次,功能与PDP11相当。 上海计算机厂 1982 华北计算所研制出DJS186小型计算机。该机是180系列的高档机,字长16位,内存容量256K字,运算速度每秒100万次。由北京有线电厂投产。 华北计算所 1982 DJS186小型计算机研制,是180系列的高档机。由北京有线电厂投产,字长16位,内存容量256K字,运算速度每秒100万次。 到1982年底,中国已经有了4000台计算机(不包括微型机),其中进口计" +357,"EC-1型)投入生产 清华大学主持联合设计,有电子部六所、国营734厂(南京有线电厂)、陕西省计算机厂以及华明计算机有限公司参加研制 1987 中国首次实现与国外的计算机联网,北京计算机应用技术研究所向西德卡尔斯鲁厄大学发出中国第一封电子邮件(E-mail)。 1988 长城386推出 中国长城计算机集团公司(原中国计算机发展公司) 1988 我国第一个工作站系列——华胜3000系列研制成功 电子部六所等单位 1988 发现首例计算机病毒 1989 EST/IS4260智能工作站 1992 长沙国防科技大学研制出银河-Ⅱ通用并行计算机,峰值速度达每秒4亿次浮点运算(相当于每秒10亿次基本运算操作),为共享主存储器的四处理机向量机,其向量中央处理机是采用中小规模集成电路自行设计的,总体上达到80年代中后期国际先进水平,主要用于中期天气预报。 长沙国防科技大学(原中国人民解放军军事工程学院) 1993 曙光一号全对称共享存储多处理机完成研制,这是国内首次以基于超大规模集成电路的通用微处理器芯片和标准UNIX操作系统设计开发的并行计算机。 国家智能计算机研究开发中心 1994 7月,“中国教" +358,"海尔集团、长城集团长软公司、中软股份、中科红旗、曙光集团、神州龙芯等国内七家联手发起的“龙芯联盟”正式成立,这意味着龙芯从此有了一条完整的产业链条。 2004 8月17日,山东大学王小云、中国科学院软件研究所冯登国、上海交通大学来学嘉、山东大学于红波四人在美国加州圣巴巴拉举行的国际密码学会议(Crypto’2004)上公布对于MD5、HAVAL-128、MD4和RIPEMD四个著名HASH算法的破译结果。 2004 12月23日,中国国家顶级域名.CN服务器的IPv6地址登录到全球域名根服务器。 2005 龙芯2号正式面世 2006 漢芯騙局、“方舟3號”芯片停止研發,打擊中國芯片製造業 上海交大汉芯科技有限公司、方舟科技(北京)有限公司 2009 “天河一号”诞生。 2013 “天河二号”在国家超算广州中心投入运行 2016 使用中国自主芯片制造的“神威太湖之光”取代“天河二号”登上全球超级计算机500强榜单之首。 ==参见== * 中国超级计算机 * 中华人民共和国互联网 * 计算机科学 == 参考文献 == === 引用 === === 来源 === * 周蕾《中国计算机产业发展" +359,"克利斯登·奈加特(,),挪威计算机科学家,社会活动家,图灵奖和冯·诺伊曼奖获得者,Simula的创始人,面向对象技术的先驱。 ==生平== 克利斯登·奈加特1926年生于挪威的奥斯陆,1956年于奥斯陆大学获得数学硕士学位,论文是《蒙特卡罗方法的理论方面》。 1948年—1960年间克利斯登·奈加特全职工作于,进行计算、编程(1948年—1954年)和运筹学(1952年—1960年)方面的研究。 1957年—1960年间他是挪威防务系统中最早的运筹研究小组的负责人。他是挪威运筹研究学会(Norwegian Operational Research Society)的共同创办者和第一任主席(1959年—1964年)。1960年他受聘于挪威计算中心(Norwegian Computing Center),负责把挪威计算中心建设成为一个研究机构,并且于1962年成为研究主管(Director of Research)。 与奥利-约翰·达尔(Ole-Johan Dahl)一道,他开发了最早的面向对象的程序设计语言SIMULA-I(1961年—1965年)和SIMULA-67,首次引入了面向对象" +360,"。他是挪威科学院的成员。 1990年10月美国的协会—为了社会责任的计算机职业者(Computer Professionals for Social Responsibility,CPSR)—因为他社会责任感和职业工作而授予他诺伯特·维纳奖。1992年他因“使挪威在信息科学领域闻名世界”而获得计算机世界(Computer world)的荣誉奖金。1999年他与奥利-约翰·达尔一起获得了罗辛奖(Rosing Prize)。罗辛奖是挪威数据协会的为专业上特别的进步而设立的新奖项。2000年6月面向对象技术的国际标准化组织——对象管理组织(Object Management Group)因他在“对象技术概念上的开创工作”而授予荣誉会员。 2001年11月他与奥利-约翰·达尔一起获得了電機電子工程師學會(IEEE,Institute of Electrical and Electronics Engineers)的約翰·馮諾依曼獎,并评价:“奖予他们在设计和实现SIMULA67时引入的概念,这些概念是面向对象技术的基石。” 2002年2月他又一次与奥利-约翰·达尔一起获得了美國計算機協會(AC" +361,"languages)的。因位人工智能和信息技术正成为专业工作的一部分,这些研究工作因而是必要的。 克利斯登·奈加特在1995年—1999年的研究与分布式系统有关。他是GOODS项目(General Object-Oriented Distributed Systems,通用面向对象分布式系统)的领导者。这个为期三年的项目,由挪威研究理事会(Norwegian Research Council)资助,始于1997年,目标是通过引入新的基本概念来丰富面向对象语言和系统开发方法,这些新的基本概念可以使我们来描述分层和(或)分布的程序、硬件和实现这些程序的人之间的关系。GOODS小组还包括Haakon Bryhni、Dag Sjoberg和Ole Smordal。他也领导挪威计算中心的一个小组,这个小组致力于建立一个GOODS想法的商业化实现——STAGE项目(STAGing Environments)。STAGE小组还包括Dag Belsnes、Jonn Skretting、Kasper Osterbye和Birger Moeller-Pedersen。 克利斯登·奈加特生前最后的研究兴趣是" +362,"工作,大多数青年组织反对挪威加入欧洲共同市场组织,最后他们获得胜利。1971年—2001年克利斯登·奈加特是工党成员,并成为该党科学研究政策委员会的成员。 克利斯登·奈加特于1988年11月成为挪威信息委员会主席。 在1996年—1997年间克利斯登·奈加特为致力于成立欧洲反对马斯特里赫特条约运动(TEAM-The European Anti-Maastricht Movement)而担任协调员。欧洲反对马斯特里赫特条约运动是一个反对欧洲经济与货币联盟(Economic Monetary Union)和马斯特里赫特条约的国际组织间的协调网络。欧洲反对马斯特里赫特条约运动成功地于1997年3月3日建立起来。 == 家庭 == 克利斯登·奈加特与约翰娜·奈加特于1951年结婚。约翰娜·奈加特现已退休,曾工作于挪威援助发展中国家机构(NORAD, The Norwegian Agency for Aid to Developing Countries)。多年来她专于招募工作并为工作在东部非洲的专家提供行政支持。他们有三个孩子和七个孙子。 == 参见 == * 计算机科学家 * 奥利-约翰·达" +363,"Self语言,是一种基于原型的面向对象的程序设计语言,也是一个集成开发环境和运行环境,由David Ungar和Randy Smith,最初在1986年于施乐帕罗奥多研究中心设计。Self语言在Smalltalk的基础上,采用“槽”取代了“变量”,从而彻底体现了一切都是对象的风格。在实现Self系统的过程中,设计研究人员发展出了一种动态自适应编译技术。 == 简介 == Self语言把在概念上精简Smalltalk作为设计原则。它在把消息作为最基本的操作的同时,取消了类的概念,只有对象的概念。它把对象的特性,理解为获取或更改特性的这两种方法,从而把特性的概念简化为方法,并且通过消息来读槽和写槽的方式,取代了变量及其赋值。Self提出了特质的概念,用实现了委托。 尽管Self系统一次运行在一个进程中,但实际上可以分成两个部分:Self虚拟机和Self世界。Self世界是一个Self对象库,Self对象包括数据对象和方法对象,方法对象的代码部份,是用一种指令非常简单的字节码表示的,字节码由Self虚拟机来解释。当Self程序从终端、文件或者图形用户界面输入到系统之中时,Self系统把源程序" +364,"布了2.0版。1993年1月,Self 3.0版发布。 1995年7月,Self 4.0版发布。在这个版本中包括了一个全新的图形用户环境Morphic。在2016年发行了4.3版本并可运行在Mac OS X和Solaris上。在2010年发行了版本4.4,由最初团队的某些人和独立编程者形成的小组开发,它和所有后续版本可以运行在Mac OS X和Linux上。2014年1月发行了4.5版本。2017年5月发行了版本2017.1。 Self的发展基本已经停滞,但在发展Self过程中探索出的一些技术,在其他的系统中得到了应用。在Self的实现中采用的各种编译优化技术,直接导致了Java Hotspot虚拟机的产生;在Smalltalk的一个实现Squeak中,采用了Self图形用户界面Morphic的设计方案,放弃了Smalltalk-80中采用的MVC的方案。Self是对JavaScript编程语言设计有最主要影响者之一。 == 基于原型编程 == 传统的基于类的面向对象语言,基于了根深蒂固的二元性: #类,定义对象的基本品质(quality)和行为(Behavior)。 #对象实例,是类" +365,"的方式迸发出对象。早期的面向对象语言如Smalltalk的实验,显示出这种问题反反复复的出现。系统趋向于增长到一定程度后,就变得非常僵化,因为在编程者的代码下的深层的基本类,简直就像是逐渐变成了一个“错误”;没有变更原来的类的容易方式,就会出现严重的问题。 动态语言如Smalltalk,允许通过周知的按照类的方法进行这种变更;即通过改变类,基于它的对象就可以改变它们的行为。但是,进行这种变更必须非常小心,因为基于相同类的其他对象,可能把它当作“错误行为”:“错误”经常是依赖于场景的,这是问题的一种形式。进一步的说,在静态语言如C++中,这里的子类可以从超类分别的编译,对超类的变更实际上可以破坏预编译的子类方法;这是脆弱基类问题的另一种形式,也是的一种形式。 在Self和其他基于原型的编程语言中,消除了在类和对象之间的这种二元性。不再有基于某种“类”的一个对象“实例”,在Self中,你可以复制一个现存的对象,并改变它。故而Bob's car可以通过制作现存的Vehicle对象的复本来建立,并增加“快速行驶”方法,建模它恰好是一辆保时捷911的事实。 主要用来制作复本的基本对象叫做“原型”" +366,"叫做name的数据槽,则通过,可返回在这个槽中的值;如果它有对应的赋值槽name:,则通过,可设置数据槽name的值为'foo'。 任何槽都可以通过增加星号后缀,来制成父槽。星号不是槽名字的一部份,在将名字与消息进行匹配时候忽略它。例如一个初始化了的可变的点可以定义为: (| parent* = traits point. x <- 3 + 4. y <- 5. |) 一个对象的代码是以点号分隔的一序列的表达式。尾随的点号是可选的。每个表达式有一系列的消息发送和文字组成。在一个对象的代码中最后的表达式,可以前导着指示返回的^算符。 一个真正的空对象,指示为(| |)或简单的(),它根本不接收任何消息。 === 消息 === 通过消息访问槽的语法,类似于Smalltalk,有三类消息可以获得: ;一元: 接收者 槽名字 ;二元: 接收者 算符 参数 ;关键字: 接收者 关键字1: 参数1 关键字2: 参数2 所有消息都返回结果,所以显式指定的接收者和参数自身可以是其他消息的结果。下面是Self版本的hello world程序例子: 'Hello, World!' print. 组合可以通" +367,"===方法=== 方法(method)是除了参数槽局部槽之外,还包含代码的对象。参数(argument)槽名字开始于一个冒号,它不是槽名字的一部分,在将名字与消息进行匹配时候忽略它。参数槽总是只读的,并且不能对它们指定初始化者。下面例子是计算平方的方法对象: (| :arg | arg * arg ) 一个普通方法(简称方法),是不嵌入到其他代码之中的方法,它只能存放在只读槽中。普通方法总是有一个叫做self的隐含的父参数槽。Self的普通方法等价于Smalltalk的方法。 如果一个槽包含一个方法,在求值这个槽来响应发来的消息的时候,这个方法对象被(clone),从而新建它的一个活动(activation)对象,它包含这个方法的参数槽和局部槽;复制体的self父槽,初始化为这个消息的接收者;复制体如果有参数槽,将它们初始化为实际参数;在这个新的活动对象的上下文中,执行这个方法的代码。例如计算点的加法的一个方法: (| + arg = ( (clone x: x + arg x) y: y + arg y ) |) 可以被无歧义的分析,其含义同于: (| + = (| :arg | (" +368,"两个参数是value:With:,随着参数增多With:个数也随之递增。此外,它还有一个叫parent*的父槽,指向含有块对象都共享的行为的那个对象(traits block)。 *块方法对象,它含有这个块的代码。不同于普通方法对象,它不包含self槽,转而有一个匿名父槽,它被初始化指向在词法上处于外围的块或方法的活动对象。匿名的含义,是这个槽的名字在Self层面是不可见的,而不能显式的访问。作为结果,在一个块方法内发送的隐含接收者消息,被限定在这个块所在表达式的词法作用域之内,而非这个块经过可能有的多次转送,最终向它发送适当的value消息变体之时的那个作用域。 相应的,块求值分为如下两个阶段: *在这个块被求值的时候,即它被用作发送消息的参数,比如例子中to:By:Do:消息的参数之时,建立块对象(即块数据对象);这个块被浅层复制(clone),并将指向这个块在词法上外围的活动记录,即当前的活动记录的一个指针交给它匿名保存。 *在向这个块发送适当的value消息变体的时候,求值这个块方法;这个块方法接着被浅层复制,并填充此复制体的方法槽,使用第一阶段确定的指针来初始化匿名父槽,最后" +369,"unt)。通常建立的这个对象,具有内部的方法,比如说“存款”(deposit)和“取款”(withdraw),和任何它所需要的数据槽,比如说“余额”(balance)。这只是一个原型,它只在使用方式上特殊,因为它恰好是一个全功能的银行帐号。 为“Bob的账户”制作银行帐号对象的复制品(clone),将建立一个新对象,它在起初时完全同于原型。在这种情况下,将复制(copy)包括方法和任何数据的槽。但更常用的解决方案,是首先建立叫做它的特质(trait)对象的一个简单对象,它包含通常与一个类有关的项目。 在这个例子中,“银行账户”将没有存款和取款方法,而是委托给一个父对象来做这些。采用这种方式,可以制作银行帐号对象的很多复本,但是我们仍可以通过改变它所委托的特质对象中的槽,来改变它们全体的行为。 ===Self世界=== 当处在于提示符下键入表达式的场景时,由叫做“大厅”(lobby)的一个对象,引领用户进入Self世界。当建立一个新对象的脚本被读入系统的时候,脚本中的表达式都在大厅的上下文中求值。就是说大厅是这个脚本中所有发送给self的消息的接收者。 要引用在脚本中的某个现存的对象,必" +370,"py,并使用同一性来测试相等。 *全局(globals)对象,即不唯一性的原型对象,和“每种只有一个”的独特(oddball)对象。一些对象比如true、false和nil是唯一性的,在系统中它们只需要有一个。因为一个oddball不需要在它的很多实例间共享它的行为,它不需要有分立的特质对象和原型对象。很多oddball对象从traits oddball继承copy方法,它返回对象自身而非一个新复本。 *混入(mixins)对象,即小而无父对象的行为束(bundle),是持有共享行为的一种对象,通常用来混入那些有其他父对象的对象之中。混入对象的一个例子是mixins identity。两个对象测试相等,通常基于在一个共同的域(domain)内是否有相同的值。例如,在数的域内3.0 = 3,即使它们不是相同的对象甚至不是同种类的对象。但是在一些域中,两个对象相等当且仅当它们是相同的对象,例如两个进程即使有相同的状态也不被当作是相等除非它们是同一个。在这种情况上,使用同一性比较来实现相等测试,并混入mixins identity来得到想要的行为。 在大厅的defaultBehavior槽中" +371,"它的槽将被复制进入接收者。例如,在大厅中新增叫做newObject的对象,并初始化这里举出的它的叫做entries的槽,采用如下这样的表达式: _AddSlots: (| newObject = (| entries <- list copy …… |) |) 因为_AddSlots:原语未指定接收者,这里的消息隐含接收者self是大厅,由它来理解产生初始值的消息list copy,list是周知的原型对象。 == 例子 == 在下面的例子中,将基于类语言中叫做Vehicle的一个简单的车辆类,重新构造为Self中的vehicle对象,从而能够区分出在轿车和卡车之间共有的行为: _AddSlots: (| vehicle <- (|parent* = traits clonable|) |). 在大厅中创建了一个叫做vehicle的槽,它的值是一个对象文字,同时还创建了一个叫做vehicle:的赋值槽。作为vehicle槽初始值的对象文字,也就是新建的叫做vehicle的对象,包括了一个单一的父槽parent,没有相应的parent:,它委托了traits clonable对象,这个顶" +372,"lk系统所用的某种虚拟机系统。就是说,程序不是像C语言中那样的独立实体,而是需要它们的整体内存环境来运行。这要求应用程序被装载入保存内存的(chunk)之中,这叫做“快照”或。这种方式的缺点,是映像有时很大并且笨重;但是调试一个映像,经常被调试一个传统程序要简单,因为运行时状态更容易检查和修改。在基于源代码和基于映像的开发之间的不同,是类似于在面向类的和面向原型的面向对象编程之间的区别。 此外,环境是为了让在系统之中的对象能快速和可持续的变更而定制的。重新构建一个“类”设计,就像从现存的祖先拖动出来方法放入新造的之中一样容易。简单任务像测试方法,可以通过制作复本来处理,拖动方法进入这个复本,接着变更它。不同于传统系统,只有变更了的对象有新代码,不需要重建任何东西来测试它。如果这个方法有效,可以简单的把它拖动回祖先之中。 ===性能=== Self的VM实现的性能,在某些测试之中大约是优化的C程序速度的一半。这是通过即时编译技术达到的,它是在Self研究中首创并改进的,能够使高级语言表现得这么好。 ===垃圾收集=== Self的垃圾收集器使用分代垃圾回收,它按年龄分离对象。通过使用内存" +373,"for and in Self * Gordon's Page on Self * dSelf: distributed extension to the delegation and language Self Category:动态编程语言 Category:动态类型编程语言 Category:面向对象的编程语言 Category:基于原型的编程语言 Category:Smalltalk编程语言家族 Category:1987年建立的程式語言" +374,"Linux内核()是一种开源的类Unix操作系统宏内核。整个Linux操作系统家族基于该内核部署在传统计算机平台(如个人计算机和服务器,以Linux发行版的形式)和各种嵌入式平台,如路由器、无线接入点、专用小交换机、机顶盒、、智能电视、数字视频录像机、网络附加存储(NAS)等。工作于平板電腦、智能手机及智能手表的Android操作系统同样通过Linux内核提供的服务完成自身功能。尽管于桌面电脑的占用率较低,基于Linux的操作系统统治了几乎从移动设备到主机的其他全部领域。截至2017年11月,世界前500台最强的超级计算机全部使用Linux。 Linux内核最早是于1991年由芬兰黑客林納斯·托瓦茲为自己的个人电脑开发的,他当时在Usenet新闻组comp.os.minix登载帖子,这份著名的帖子标志着Linux内核计划的正式开始。如今,该计划已经拓展到支持大量的计算机体系架构,远超其他操作系统和内核。它迅速吸引了一批开发者和用户,利用它作为其他自由软件项目的核心,如著名的 GNU 操作系统。而今天,Linux 内核已接受了超过1200家公司的近12000名程序员的贡献,其中包括一些知" +375,"作系统还没有摆脱合法的阻碍。因此,尽管早期版本的 Linux 功能有限,但它迅速获得了开发人员和用户。 到1991年9月,Linux内核版本 0.01 在芬兰大学和研究网络(FUNET)的FTP服务器(ftp.funet.fi)上发布。它有10,239行代码。在1991年10月,0.02版本的内核发布了。 1991年12月,0.11版本的内核发布。由于它可以由运行相同内核版本的计算机编译,因此该版本是第一个的 Linux 内核。当托瓦茲于1992年2月发布0.12版本时,他采用了 GNU 通用公共许可证(GPL),而不是以前的自行起草的许可证,原先的许可证不允许商业再分发。 1992年1月19日,第一篇文章提交给新的新闻组alt.os.linux出现。1992年3月31日,该新闻组更名为 comp.os.linux。 X Window 系统随后被移植到Linux上,所以在1992年3月,Linux 0.95 是第一个能够运行X的版本。从0.1x到0.9x的版本号大幅跨越是因为期望没有大的缺失部分的版本1.0的即将出现。然而,这被证明是错误的。从1993年到1994年初,出现了0.99版" +376,"本3.10包含15,803,499行代码,而2015年6月发布的4.1版本已发展到超过1950万行代码,由近14000名程序员贡献。 ==== 塔能鮑姆-林納斯辯論 ==== Linux不是微内核架构的事实曾经引起了林納斯·托瓦茲与安德鲁·斯图尔特·塔能鲍姆之间一场著名的争论。1992年在Usenet討論群組comp.os.minix開始了一場網路論戰,討論的主題在於作業系統架構的選擇。稍後一些著名的駭客也加入討論,如大衛·米勒、曹子德。這場辯論影響了Linux核心的設計走向。塔能鲍姆認為Linux内核採用的宏内核已經過時了,應該採取比較先進的微內核架構,引起了林納斯的反擊。 在2006年5月9日,这个主题被重新审视,并且在2006年5月12日塔能鲍姆写了一份立场声明。 == 架构 == Linux是一个单体内核,支持真正的抢占式多任务处理(于用户态,和版本2.6系列之后的内核态)、虚拟内存、共享库、、共享写时复制可执行体(通过)、内存管理、Internet协议族和线程等功能。 设备驱动程序和内核扩展运行于内核空间(在很多CPU架构中是ring 0),可以完全访问硬件,但也有运行于用户" +377,"Intel主张通过修改内核,以便Intel C++編譯器能正确编译内核。在2009年,有通过修改内核2.6.22版而成功编译的报告(並帶來平均8-9%效能增長)。 自从2010年,已经开始进行使用Clang建造Linux内核的努力,Clang是一个可作为替代的C语言编译器;截止2014年4月12日,官方内核几乎可以完全用Clang编译。致力于这个目标的计划叫做“LLVMLinux”,得名于Clang所基于的LLVM编译器下部构造。LLVMLinux不意图复制Linux内核或LLVM,因此它是由最终提交给上游计划的补丁构成的一个元计划。使Linux内核可以用Clang编译最大的好处是比GCC有更快的编译速度,内核开发者可以得益于由此而来的更快的工作流程。 === 接口 === 符合标准是Linux内核内部的普遍策略。另一个规则是Linux内核主线不接受只由专有用户空间软件使用的内核模块。 ==== 内核至用户空间API ==== 源代码可移植性确保符合标准的C程序可以在符合同样标准的任何系统上编译和运行。Linux内核开发、GNU C函数库和相关的实用工具致力于追随POSIX和单一UNI" +378,"4,只有用户进程是抢先式的,就是说除了时间片用尽,在用户模式下执行的当前进程,如果有更高态优先级的进程进入TASK_RUNNING状态,它就会被中断。自从2.6系列Linux内核,增加了中断执行内核代码的任务的能力,但不是对于内核代码的所有段落。 Linux内核含有不同的调度器类。内核缺省使用的调度机制叫做完全公平调度器,它介入于内核版本2.6.23。这个缺省调度器类在内部也叫做SCHED_OTHER,而内核还含有两个遵循POSIX的实时调度类,分别叫做SCHED_FIFO(实时先进先出)和SCHED_RR(实时轮流式),二者都优先于缺省类。 通过使用实时Linux内核补丁PREEMPT_RT,可以支持对关键段落、中断处理器和“中断禁用”代码序列的完全抢先。 实时Linux内核补丁部分地集成入主线内核已经带给它一些功能。抢先机制改善延迟、增进响应性,并使得Linux更加适合桌面和实时应用。老版本内核有所谓的,用于锁定粒度为整个内核的同步,它最终由Arnd Bergmann在2011年移除了。 还有叫做的调度策略,实现了(EDF)算法,它增加于2014年3月30日发行的内核版本3.14。" +379,"Linux内核的行为不正确,并产生了一份相关的错误日志。许多类型的oops会导致内核错误,即令系统立即停止工作,但部分oops也允许继续操作,作为与稳定性的妥协。这个概念只代表一个简单的错误。当内核检测到问题时,它会打印一个oops信息然后杀死全部相关进程。oops信息可以帮助Linux内核工程师调试,检测oops出现的条件,并修复导致oops的程序错误。 === 安全 === 计算机安全是一个非常公众化的主题,关系到Linux内核,因为大量在内核中的错误可能成为潜在的安全漏洞,是否允许提升权限漏洞或拒绝服务攻击源漏洞。在过去的几年中,许多这样的缺陷被发现,并在Linux内核中被修补好。新的安全功能被继续实现,以解决在Linux内核中的电脑不安全问题。 批評者指責內核開發人員,稱他們掩蓋(至少並未公佈)安全漏洞。2008年,作為回應,Torvalds稱:「個人認為,安全漏洞只是『正常的漏洞』。這些漏洞我並不去掩蓋,不過我不認為應當把它們特殊化,更不認為應該追蹤並公示它們……我不理會整個安全團隊,原因之一就是,我認為這些漏洞不僅美化還鼓勵了錯誤的行為。這令安全人員成了『英雄』,就猶如不修" +380,"假定Linux内核开发者社区由5000或6000名成员组成。 Linux基金会发表的2016年Linux内核开发报告的更新表明,从版本3.18(2014年12月)至4.7(2016年7月)期间:平均每次发行有来自200-250个公司的大约1500位开发者作出贡献。顶端30位开发者贡献了稍大于16%的代码。在公司中,顶端贡献者是Intel(12.9%)和Red Hat(8.0%),第三和第四位为“none”(7.7%)和“unknown”(6.8%)类属。 === 开发过程与模式 === 一个想要对 Linux 内核进行修改的开发者一般就从对那个修改的开发和测试开始着手。接下来的过程取决于变化的重要程度,及修改该变更的子系统数量是由单个还是多个修补程序组成。如果仅仅是修改了由单个维护人员维护的单个子系统,那么这些修改的补丁代码就直接通过Cc中某个邮件列表发送给相关的维护人员。邮件列表的阅读者和子系统的维护人员将检查补丁代码并提供反馈。一旦审查过程完成,维护者接受他内核代码树中的补丁。如果这些更改被认为是够重要的错误修复,那么包含这些修补程序的拉取请求(pull request)将在几天内" +381,"。此外,他们同时也会增添一些新特性和对新硬件的支持,而这些支持是这些发行商分支基于的稳定分支所不包括的。 === 主线Linux === 包含Linux内核的Linus Torvalds的Git树被称为主线Linux。每个稳定的内核发布都源自主线树,并经常在kernel.org上发布。主线Linux只对运行Linux的众多设备中的一小部分设备提供坚实的支持。非主线支持由独立项目(如Yocto或Linaro)提供,但在许多情况下需要设备供应商的内核。使用供应商内核可能需要一个板级支持包。 在主线Linux之外维护一个内核树已被证明是困难的。 主线化(Mainlining)是指将对设备的支持添加到主线内核中的努力,而以前只有在分支中支持或根本没有支持。这通常包括添加驱动程序或文件。完成后,该功能或安全修复被视为mainlined。 === 重新开发的估价 === 按照传统商业软件开发的方式,重新开发Linux 2.6.0内核的估计代价将是6.12亿美元(4.67亿欧元、3.94亿英镑),以2004年的COCOMO人月估计模型.在2006年,欧盟资助的一项研究表明,重新开发Linux 2.6" +382,"改的内核,内核使用了传统的奇数次要版本号码的软件号码系统(用偶数的次要版本号码来表示稳定版本);C是指轻微修订的内核,这个数字当有安全补丁,bug修复,新的功能或驱动程序,内核便会有变化。自2.6.0(2003年12月)发布後,人们认识到,更短的发布周期将是有益的。自那时起,版本的格式為A.B.C.D,其中A,B,C,D代表:A和B是无关緊要的,C是内核的版本,D是安全补丁。 自3.0(2011年7月)发布后,版本的格式為3.A.B,其中A,B代表:A是内核的版本,B是安全补丁。而4.0(2015年4月)釋出後,則延續3.A.B的命名格式,只是將主版號變更為4。 == 法律層面 == === 许可证 === 原先托瓦茲将 Linux 置于一个禁止任何商业行为的条例之下,但0.12版本之后改用 GNU 通用公共许可证第二版。 该协议允许任何人对软件进行修改或发行,包括商业行为,只要其遵守该协议,所有基于Linux的软件也必须以该协议的形式发表,并提供源代码。 托瓦茲曾经公开声称将Linux置于GNU通用公共许可证之下是他一生中所做的“最好的决定”。 ==== GPL第三版 ==== Li" +383,"了他的批评。Linus Torvalds says GPL v3 violates everything that GPLv2 stood for Debconf 2014, Portland, Oregon (accessed 11 March 2015) ==== 韌體爭議 ==== 許可證爭議的一個重點是Linux使用韌體二進位包以支援某些硬體裝置。理察·馬修·斯托曼認為這些東西讓Linux某部份成為非自由軟體,甚至以此散佈Linux更會破壞GPL,因為GPL需要完全可獲取的原始碼。 林纳斯·托瓦兹及Linux社群中的領導者,支持較寬鬆的許可證,不支持理察·馬修·斯托曼的立場。社群中的Linux-libre提供完整的自由軟體韌體。 ==== 載入式核心模組許可證 ==== 另一個爭論點,就是載入式核心模組是否算是智慧財產權下的衍生創作,意即LKM是否也受GPL約束?托瓦茲本人相信LKM僅用一部分「公開」的核心介面,因此不算衍生創作,因此允許一些僅有二進位包裹的驅動程式或不以GPL宣告的驅動程式用於核心。但也不是每個人都如此同意,且托瓦茲也同意很多LKM的確是純粹的衍生創作,也寫下" +384,"林納斯·托瓦茲 * * Linux内核版本历史 == 参考文献 == == 外部連結 == * Linux内核官方下载中心 * 日渐膨胀的Linux邮件列表内容摘要 * 林納斯與塔能包姆爭論微核心的筆仗 * Greg Kroah Hartman on the Linux Kernel at youtube * 如何查看Linux内核版本信息 Category:使用GPL許可證的軟件 Category:類Unix系統 Category:芬蘭發明 Category:用C編程的自由軟件" +385,"林纳斯·贝内迪克特·托瓦兹(,,),生於芬兰赫尔辛基市,擁有美國國籍,Linux内核的最早作者,隨後發起了這個开源项目,擔任Linux內核的首要架構師與專案協調者,是当今世界最著名的电脑程序员、黑客之一。他还发起了开源项目Git,并为主要的开发者。 == 生平 == 托瓦兹出生于芬兰赫尔辛基市。父亲尼尔斯·托瓦兹是一名活跃的电台记者,母親安娜·托瓦兹(Anna Torvalds)也是一名記者。祖父奥勒·托瓦兹(Ole Torvalds)是一名詩人和记者。外祖父是芬兰第一批统计学教授。芬兰统计学会设立Leo Törnqvist Award,授予在颁奖之前的两年内杰出的统计学硕士学位完成者。林纳斯·托瓦兹在11岁时,应其外祖父要求用BASIC语言编写一些统计学方面的小程序,这是他编程经历之始。 托瓦兹家族属于在芬兰占6%的芬兰瑞典族,其父母都畢業於赫爾辛基大學,在學期間是積極的學運份子。他的名字來自於萊納斯·鮑林(Linus Pauling),一位諾貝爾化學獎得主。 1988年,他進入赫尔辛基大学計算機科學系。1989年,他進入芬蘭陸軍新地区旅,服11個月的,軍階為少尉,主要服務於計算機部" +386,"n。 2005年,為了管理Linux內核的原始碼,開發了Git。 2007年1月22日,自由標準組織與開源碼發展實驗室合併,成立了Linux基金會。Linux基金會提供薪水及各種協助,以支持托瓦茲繼續投入Linux內核的開發工作。 ==家庭與私人生活== 托瓦兹与妻子托芙(Tove,曾獲得六次芬蘭空手道冠軍頭銜的前女子空手道選手)於1993年結婚,育有三名孩子。 2010年,托瓦茲宣誓成為美國公民。 == 行事風格 == 托瓦兹坚持开放源代码信念,并对微软等对手的FUD战略大为不满。例如,在一封回应微软资深副总裁克瑞格·蒙迪批评开放源代码运动破坏了知识产权的电子邮件中,托瓦兹写道:“我不知道蒙迪是否听说过艾萨克·牛顿爵士?他不仅因为创立了经典物理学而出名,也还因为说过这样一句话而闻名于世:‘我之所以能够看得更远,是因为我站在巨人肩膀上的緣故。’”托瓦兹又说道:“我宁愿听牛顿的也不愿听蒙迪的。他(牛顿)虽然死了快300年了,却也没有让房间这样地臭气熏天。” 林纳斯在网上邮件列表中也以火暴的脾气著称。例如,有一次与人争论Git为何不使用C++开发时与对方用“放屁”(原文为“bullshit" +387,"tur级)。两年后,他在斯德哥尔摩大学接受名誉博士学位,并在2000年在他的母校获得了同样的荣誉。Torvalds, 2001, page 28 在2005年8月,托瓦兹获得里德学院的獎。 ;工业界 * 1998年,托瓦兹接受了电子前哨基金会先锋奖。 * 2000年,他被英國電腦學會授予。Talking to Torvalds , British Computer Society, September 2007. * 2001年,他与理查德·斯托曼和分享为了社会/经济福祉的。 *2008年,他入选到加州山景城的计算机历史博物馆的资深会员堂(Hall of Fellows)。 ;媒体 时代杂志多次确认了托瓦兹的成就: *2000年,在时代100人:本世纪最重要的人物投票中,他位居第17名。 *2004年,他被评为世界最有影响力的人之一。 *2006年,该杂志的欧洲版命名他是在过去60年的革命英雄之一。 “InfoWorld”颁发了“2000工业成就奖”给他。Nicholas Petreley. This year's Award for Industry Achievement goes" +388,"世界十大杰出青年 Category:美国程序员 Category:千禧技术奖获得者 Category:芬兰瑞典族人 Category:林纳斯·托瓦兹 Category:TR35获得者" +389,"理查德·马修·斯托曼(,簡稱rms,有时也用大写的RMS,),美国程序员,自由软件活动家。他发起自由软件运动,倡导软件使用者能够对软件自由进行使用、学习、共享和修改,确保了这些软件被称作自由软件。斯托曼发起了GNU项目,并成立了自由软件基金会。他开发了GCC、GDB、GNU Emacs,同时编写了GNU通用公共许可协议。 为了创建一个完全由自由软件组成的类Unix计算机操作系统,斯托曼在1983年9月推出了GNU项目。借此又发起了自由软件运动。他迄今为止一直是GNU项目的组织者,作为主要开发者的他开发了一些被广泛使用的GNU软件,其中包括GCC、GDB、GNU Emacs。在1985年10月他创立了自由软件基金会。 斯托曼开创了Copyleft的概念,它使用版权法的原则来保护使用、修改和分发自由软件的权利,并且是描述这些术语的自由软件许可证的主要作者。最为人所称道的是GPL(最广泛使用的自由软件协议)。 1989年,他和别人一起创立了。自20世纪90年代中期以来,斯托曼花他大部分时间组织参与宣传自由软件,反对软件专利和数字版权管理的以及他认为剥夺用户自由的其他法律和技术系统运动。这包括" +390,"年,斯托曼在哈佛大学第一年快结束的时候,他成为了麻省理工学院人工智能实验室的一名程序员,同时也成为黑客社区的常客,并以他的名字缩写RMS而闻名,1974年斯拖曼从哈佛大学毕业并取得了物理学学士学位。 1975年斯托曼考虑过留在哈佛大学,但是他却转而决定去麻省理工学院去读研究生。他攻读物理学博士学位一年,但随后选择了放弃,专注于他在麻省理工学院AI实验室的编程。 他开始在麻省理工学院担任杰拉德·杰伊·萨斯曼的研究室助理,在1977年与萨斯曼发表了一篇名为dependency-directed backtracking的关于 AI truth maintenance system的论文。这篇论文是对約束滿足問題智能回溯的早期研究。截至2009年,斯托曼和萨斯曼介绍的技术仍然是智能回溯中最通用和最强大的解释。有关()的技术也在该论文中被提及。 作为MIT计算机科学与人工智能实验室的黑客,斯托曼从事软件项目,如文本編輯器,不兼容的分时系统上的Emacs,还有lisp机器的操作系统项目。在这期间,他成为了实验室电脑限制访问技术的热心评论家,当时这个计划是由国防高级研究计划局资助的。当MIT计算机" +391,"基本完成,此操作系统被命名为GNU/Linux(这类操作系统常常被称为Linux。斯托曼坚持认为 Linux 应该被稱作 GNU/Linux,因為 GNU 計劃更早出現,且在 Linux 作業系統的早期,GNU 社群的原始碼在其中起了關鍵的作用,例如 GCC 編譯器。 === 自由軟體与开源软件 === 斯托曼是一名坚定的自由软件运动倡导者,与提倡开放源代码开发模型的人不同,斯托曼并不是从软件的-{zh-hans:质量; zh-hant:品質}-的角度而是从道德的角度来看待自由软件。他认为不自由的軟體是非常不道德的事,只有尊重用户自由的程序才是符合其道德标准。对此许多人表示异议,并也因此有了自由软件与开源软件之分。而事实上,1998年“开源”一词最初从自由软件社区中分化出来仅仅是因为在英文中“自由”()一词有歧义,而其支持者认为“开源”()一词更好。 == 争议 == 2019年,由於其在MIT計算機科學與人工智慧實驗室的邮件列表中,在对杰弗里·爱泼斯坦案和馬文·閔斯基的关联的表述中的用词隐含对在馬文·閔斯基面前受害少女受迫表现为自愿且未受馬文·閔斯基虐待,這些言論在網路上引起某些人不" +392,"件运动 * Emacs * GCC * Linux * GNU/Linux命名爭議 == 延伸阅读 == == 参考文献 == == 外部链接 == * 斯托曼个人主页 * 自由软件基金会宗旨 * 自由软件基金会 * Free as in Freedom--Biography of Richard Stallman(斯托曼传记) * Free as in Freedom--Biography of Richard Stallman斯托曼传记中文書評 Category:美国程序员 Category:美國猶太人 Category:自由软件程序员 Category:美国国家工程院院士 Category:GNU人物 Category:Unix人物 Category:哈佛大學校友 Category:麻省理工学院理学院校友 Category:康考迪亚大学荣誉博士 Category:麥克阿瑟學者" +393,"自由軟件基金會(,縮寫:FSF)是一個致力於推廣自由軟件的美國民間非營利性組織。它於1985年10月由理查德·斯托曼建立。其主要工作是執行GNU計劃,開發更多的自由軟件。 從其建立到1990年代中自由軟件基金會的基金主要被用來僱用編程師來發展自由軟件。從1990年代中開始寫自由軟件的公司和個人繁多,因此自由軟件基金會的僱員和志願者主要在自由軟件運動的法律和結構問題上工作。 == 最近工作 == ; GNU工程 : 自由软件基金会最早的目的在于促进自由软件的开发,但自由软件基金会也有开发GNU操作系统的任务。 ; 施行GPL : 自由软件基金会具有施行GNU通用公共许可证和其它GNU许可证的能力和资源,但自由软件基金会只对它拥有版权的软件负责。其它软件必须由它们自己的拥有人来负责,原因是从法律规定上自由软件基金会无法为这些其它软件负责。自由软件基金会每年约接触到50个违反GNU通用公共许可证的事件,自由软件基金会试图不通过法院使对方遵守GNU通用公共许可证。 ; GNU许可证 : GNU通用公共许可证是自由软件工程中最普及的许可证。最新的版本(版本3)发表于2007年6月29日。自由软件" +394,"列表,FSF認為這些計畫需要自由軟體社群們的注意,這些計畫所開發的項目目前並沒有自由軟體可以用來取代非自由軟體。 目前計划: * Gnash — 自由的Flash player軟件 * Coreboot — 一個自由BIOS的運動 * 一個代替Skype的自由軟件 * 一個自由的視訊編輯軟件 * 一個代替Google Earth的自由軟件 * gNewSense — 一個只使用自由軟件的GNU/Linux作業系統 * GNU Octave — 一個代替Matlab的自由軟件 * 代替函式庫 * GDB的可逆性偵錯 * 自由的網絡路由器驅動程式 * 一個代替的自由軟件 * 自動轉錄Automatic transcription == 结构 == === 成员 === 2002年11月25日自由软件基金会向个人提供自由软件基金会附属会员的可能性。到2005年3月它拥有3400多位附属会员。2003年3月5日它向商业企业提供公司保护计划,到2004年4月它拥有45位公司保护。 === 组织 === ==== 理事会成员 ==== * ,SFA公司高级程序员 * ,CivicActions公司" +395,"2003年7月的新闻事件: 请参看: *香港基本法第23条 *加拿大同性婚姻 *猴痘 *以巴冲突 *中东和平路线图 *欧盟扩大 *北朝鲜核危机 *恐怖主义战争 *阿富汗战争 *美军占领伊拉克 == 2003年 == === 7月31日 === *十名疑似朝鲜脫北者的人士闯入曼谷的日本駐泰國大使館要求政治庇护。 *秘鲁星期三再次向日本提出引渡藤森的要求。 *菲律宾总统阿罗约星期四要求国家检察官对参与兵变的军人提起诉讼。 === 7月30日 === *阿拉伯一家电视台最近播放一盘据称是萨达姆的录音带。美国联邦调查局正在确认这个录音带是否真是萨达姆所录制。 *菲律宾武装部队情报部长维克托·科尔普斯准将辞职。 *比利时迫于国外的压力,修改了“万国管辖权法”。修正案将在本周末生效。 === 7月29日 === *美军占领伊拉克:美军今天继续对伊拉克境内主要城市进行搜捕,包括萨达姆的家乡提克里特市,但是根据情报说,萨达姆比他们早一步离开被搜捕地点,但美军仍抓获了萨达姆的一名终身保镖。 *美国著名喜剧演员鲍勃·霍普在华盛顿逝世,终年100岁。 *中国河北省一烟花厂发生爆炸,造成多人伤亡。 === 7" +396,"比1991年海湾战争死亡人数要多14人。 === 7月25日 === *美国回应台湾公投议题时指出,美国没有看到台湾非“公投”不可的理由,美国坚持“一个中国”原则。 *台灣当局表示台湾将继续举行“全民公投”,并将于下周核定出“全國性公民投票委员会”。 *世界著名足球队西班牙甲级联赛冠军皇家马德里今天中午抵达昆明。 *中国外交部发言人孔泉25日否认了印度报纸报道的关于6月26日中国人民解放军越过中印边界控制线的说法。 *被小布什提名为海军部长的科林·麦克米伦被证实于24日在自家的农场里开枪自杀。 *联合国秘书长安南25日在一次讲话中呼吁美国和朝鲜尽早恢复北京会谈以找到解决当前朝鲜核危机的办法。 *美国总统布什已经下令向利比里亚派遣一支美军部队以协助该国恢复国家秩序。 *有消息指台湾总统陈水扁将派萧万长代表出席10月在曼谷举行的亚太经合会。 *中国水灾:台风伊布都对广西的影响基本结束。这次台风的袭击给广西带来直接经济损失4.996亿元。共有12人死亡。 *香港保安局局长叶刘淑仪今天正式离任。她在多份报章刊登启事,感谢一直支持她的朋友和家人。 *英超劲旅利物浦球队抵港,引起了大批球迷的欢迎。" +397,"北京前往上海继续访问行程。 *英国首相托尼·布莱尔到清华大学与师生进行座谈。 *正在中国访问的英国首相托尼·布莱尔表示,他将配合对英国国防部生化武器顾问戴维·凯利之死的调查。 *最新的民意调查显示,由于戴维·凯利之死引起的情报风波使得布莱尔政府陷入了严重的信任危机。表示相信布莱尔的民众从上个月的51%下降到39%,而对他的评分则跌到-17。工党的领先优势也从12%剧降到2%。 === 7月21日 === *6名俄罗斯军人在与车臣非法武装交火中丧生。 *李钟郁成为世界卫生组织新的总干事。 === 7月20日 === *法国尼斯税局外面的炸弹袭击造成16人受伤。 === 7月19日 === *纽约中央公园庆祝建园150周年。 *美国伊拉克管理委员会表示新伊拉克总统的选举失败。 *维也纳医生成功进行了人类舌头移植。 === 7月18日 === *英国国防部生化武器顾问戴维·凯利被发现在住处附近割脉自杀。有人认为这与英国政府有关。并称戴维·凯利是当前政策下的牺牲品。 === 7月17日 === *印度拒绝了美国要求其向伊拉克派兵的要求。美国外交大臣表示这不会影响印美关系。 === 7月16日" +398,"性取向表述成“灰色地带”。 * 香港發生屯門公路雙層巴士墮坡事故,一輛行走麗瑤邨至天恆邨的九巴265M線雙層巴士在早上於屯門公路疑被貨櫃車切線,將巴士推至路邊,不久巴士即墮下數十米山坡,車長及20名乘客死亡,另造成20名乘客受傷,成為香港史上最多人死亡的車禍。 === 7月9日 === *美国政府星期二宣称另有两名前伊拉克政府的高级官员被捕。Mizban Khadr al-Hadi是伊拉克前政府Baath Party地区的高级指挥官以及革命司令委员会的成员,Mahmud Dhiyab al-Ahmad是前内政部大臣。 *耐克宣布以3.05亿美元收购Converse。 === 7月8日 === *苏丹一架波音客机坠毁,116人丧生。 *美国白宫发言人7号表示,乔治·沃克·布什总统对伊拉克关于伊拉克从非洲购买用于生产核武器的指控是根据“不完整和不准确”的情报发表的。美国驻加蓬大使威尔逊表示曾向美国政府报告说情报可能是假的,但美国白宫官员则表示没有收到过报告。 *在应意大利要求下举行的欧盟会议最近就建立欧洲国家“电子政府”的决定表示同意。 *8日,在黎巴嫩广播公司和卡塔尔半岛电视台播放了一盒" +399,"死去”。同性恋权力组织GLAAD对这个决定表示欢迎。 === 7月6日 === *美国科学家最近表示他们通过混合男女细胞首次在实验室培养出了一个双性人类胚胎,这个消息的传出引起了人们的广泛争论。 *美国将向利比亚派出一个人道主义援助小组以评估该国局势,有分析人士认为,这可能意味着美军将大军派兵利比亚。 *法国科西嘉岛6号举行全民公投,以扩大当地的自治权和改革政治体制。这将有利于结束当地分离主义分子对法国中央政府的敌对活动。 === 7月5日 === *香港特别行政区行政长官董建华和全国人大常委会今天都发表谈话表示支持香港基本法第23条的立法。 *台湾已经被世界卫生组织从SARS疫区名单上除名,现在全球已经没有SARS疫区。 *俄罗斯莫斯科的一个摇滚音乐会上发生自杀性爆炸事件,至少造成20人死亡,30多人受伤。两个引爆者当场死亡。目前警方对此事还没有作出评论。 *印度尼西亚亚齐省的军事行动达到45天,已经有325名“自由亚齐运动”武装分子被打死,258人被捕,256人投降。 *美国总统今天在庆祝美国独立日时发表讲话重申美国将继续“先发制人”的打击对美国构成威胁的“恐怖组织或非法政权”。" +400,"·布什对伊拉克武装分子发出警告。 *淮河汛情告急,王家坝开闸泄洪。 *温哥华获2010年冬奥会举办权。 *北京昌平的明十三陵和南京的明孝陵作为“明清皇家陵寝”的扩展项目,列入世界文化遗产。 *美国政府决定悬赏2500万美元查找萨达姆的下落。 === 7月2日 === *菲律宾首都马尼拉地区最近出现潜在反华危机,部分大街上贴满排华传单,且有相关的手机传言满天飞,事件已在华侨社会引起紧张气氛。 *一艘载有21名船员的日本渔船在日本南部与一艘巴拿马注册的货船相撞,随后沉没,造成1人死亡,6人失踪。 *中国人民银行下调境内小额外币存款利率。 *联合国教科文组织世界遗产委员会一致决定将中国云南省的“三江並流”列入该组织的《世界遗产名录》,从而使中国列入这一名录的自然和文化遗产项目达到29个。 *欧洲议会通过的关于转基因食品的新法规。 * 加拿大温哥华成为2010年第21届冬季奥林匹克运动会的主办城市。 === 7月1日 === *伊朗邀请国际原子能机构官员到伊朗访问,讨论对伊朗的核查问题。 *代号为“比尔”的热带风暴登陆墨西哥湾,该风暴已经造成4人受伤,并造成了一些地区停电。 *美国对伊拉克人实" +401,"孫中山(),名文,後化名中山樵。字德明,號日新,改號逸仙。廣東香山翠亨鄉(今中山市翠亨村)人,中國近代政治人物。 1894年,孫中山成立興中會。1895年,興中會發動乙未廣州起義。1905年,中國同盟會在日本东京組成,孫中山被推為总理;確定「驅除韃虜,恢復中華,建立民國,平均地權」的革命政綱,提出三民主義學說;創辦《民報》,宣傳革命,同當時的中國改良派激烈論戰。此後孫中山在國內外發展革命組織,聯絡華僑、會黨和新軍,多次發動武裝起義。1911年10月10日武昌起義,各省紛紛響應。12月29日,孫中山被十七省代表在江蘇省江宁府推選為中华民国临时大总统,1912年1月1日在南京宣佈就職,建立中華民國臨時政府,月底組成臨時參議院。2月13日,因革命黨人與袁世凯妥協,孫中山被迫提請辭職;3月經臨時參議院通過,公布孫中山主持制訂的《中華民國臨時約法》;8月中國同盟會改組為國民黨,孫中山被選為理事長。1913年3月因國民黨黨魁宋教仁在上海北站腰部中彈身亡,孫中山即起兵討袁,旋即失敗。1914年,孫中山在日本建立中華革命黨,先後兩次發表討袁宣言;1917年段祺瑞解散國會,孫中山在廣州召開國會非常會議," +402,"有米飯吃,因為米飯太貴了。他主要的食物是白薯。」孫中山幼年喜歡玩游戲如放風箏、踢毽子、跳田鷄、劈甘蔗等,常爬大樹取鳥蛋,用石頭擲小鳥,鄉里稱其綽號「石頭仔」。 1876年,孫中山入村塾讀書,課餘參加農業勞動;常與同學楊帝賀等到鄰村觀看三合會練武。長兄孫眉在檀香山做工匯款回家,家境漸好轉,孫中山得進入村塾讀書;第一位老師是位癮君子,煙癮發作時就曠課;由於老師煙癮頻作,孫中山無法好好讀書。孫中山喜聽前太平軍老人講戰鬥故事,仰慕太平天國。 1877年,孫中山繼續讀書,懷疑一味背誦之教學方式;曾問老師:「讀這些書一點也不懂,有什麼意思?」9月22日,孫眉再赴檀香山,孫中山擬隨兄赴檀未果。1878年春,孫中山主張反對纏足、蓄婢和賭博。 ===求學=== 吳相湘在《孫逸仙先生傳》評述孫中山第一次出洋,是其「生平轉變之重大關鍵」。1879年5月21日,孫中山隨母坐駁艇到澳門坐英輪「格蘭諾去」號(S. S. Grannoch)赴檀香山。1879年6月,孫中山到檀香山茂宜島茄荷蕾埠(今名卡胡盧伊)孫眉開設的商店當店員;後入盤羅河學校習算術等科。9月,孫中山到英國聖公會主教主持的教會學校伊奥拉尼書院當寄宿" +403,",不為鄉人所容,經崖口、淇澳島逃離家鄉前往香港。 1883年8月18日,喜嘉理牧師收留孫中山在其香港中環之傳道所寄宿。秋,孫中山赴香港入讀英國聖公會主辦的拔萃書室,在香港為日校走讀生。10月29日,孫中山在喜嘉理牧師宿舍重逢檀香山傳教士、曾任教於奧阿厚書院之芙蘭·諦文(Frank Damon)及其父母。冬,孫中山與陸皓東到基督教綱紀慎會位於港島必列者士街的布道所,由美國公理宗差會傳教士喜嘉理牧師於1884年5月4日為孫中山主持洗禮加入教會;在其所屬教會存留之受洗名单中,陸皓東署名「中桂」排第四,孫中山署名「日新」排第二。喜嘉理牧師與孫中山情誼真摰,欣賞孫中山對宗教熱忱,孫追隨他四處宣教。 1884年4月15日,孫中山轉學香港中央書院,用孫帝象姓名註冊。秋,受香港工人反法鬥爭鼓舞,孫中山深感中國人民「已經有相當覺悟」。得知孫中山皈依基督後,孫眉試圖藉由切斷弟弟所有奧援。11月,孫中山奉孫眉急召赴檀香山,交還家產,芙蘭·諦文為孫中山籌募盤川。孫中山在茄荷蕾埠商店當店員。孫中山在中央書院讀書兩年半期間,受洗成為基督徒、第一次婚姻、與孫眉激烈衝突。 1885年4月,孫中山自檀香山經日本回中國。" +404,"國思想第一人,孫中山評其「為人豪俠尚義,廣交游,所結納皆江湖之士,同學中無有類之者」。 1887年10月1日,華人西醫書院教務長孟生醫生在香港大會堂宣佈該院成立;10月3日,孫中山在香港華人西醫書院(College of Medicine for Chinese, Hong Kong,今香港大學醫學院)正式上課,第一課是康德黎醫生講授解剖學。學堂隸屬於倫敦傳道會,由香港菁英何啟出資籌辦,該校教務長是蘇格蘭名醫康德黎。何啟曾留英學習醫學和法律,作《新政真詮》。孫中山與康德黎師生關係密切,在校5年餘,勤奮學習,除正課外廣泛閲讀「新學」書籍,在政治思想上受何啟等維新思潮影響。孫中山對達爾文的進化論發生興趣。在《孫文學說》第一章裡,可看到孫中山為破除「知易行難」,在大學時代潛心研究「知難行易」學說。孫中山時常審視懸在壁上的中國地圖,慨嘆「如此江山,付之非人」。1887年冬,孫達成病篤,孫中山回鄉伺奉湯藥,孫眉聞訊亦自檀香山歸,兄弟芥蒂盡釋。 1888年3月24日,孫達成在翠亨村病逝,享年76歲,孫眉對孫中山加倍愛護備至,凡所需學費,均允供給。8月,孫中山在西醫書院第一學年期終考試中,名列全級第" +405,"醫院簽訂〈揭本生息贈藥單〉,貸款2,000銀元開設中西藥局。由於孫中山醫術高明,態度認真,頗受歡迎,「聲名鵲起」。 1893年春,孫中山遷設藥局於廣州,改名東西藥局,藉醫術掩護革命。孫中山對貧苦患者施醫贈藥;因受澳門葡籍醫生排擠,轉赴廣州行醫;孫中山常與陸皓東、鄭士良、陳少白等議籌組革命團體。7月,孫中山在澳門議事亭前地十六A號開設「孫醫館」;7月29日,其澳門中西藥局開業。 孫中山用口號「天命無常」,宣傳反對忠君:「暴君肆虐,罪惡滔天。信任奸邪,濫施淫毒,應天順人,除彼獨夫。凡我同胞,毋稍瞻顧!」據-{云}-「(孫)先生在港曾接納三點會首領,並親自切實調查其實力,約定時間在茶樓飲茶,先生入時,凡起立者即會員。先生如約前往,至十餘處,每處茶客起立者百數十人,喜出望外。實則其頭目事先邀集工人充數,為一騙局」。 ===上書李鴻章=== 1894年1月,孫中山藉廣州城外南關的聖教書樓懸壺、西關的冼基設東西藥局;同月,孫中山在翠亨村寫就〈上李鴻章書〉。孫中山並決定親赴天津面呈給直隸總督李鴻章。不久,孫中山偕陸皓東前赴上海。2月15日,廣州《中西日報》東西藥局廣告:「……大醫生孫君逸仙……舊歲底" +406,"拜會日本駐香港領事中川恆次郎,請其援助起義,未果。3月16日,首次幹部會議決定先攻取廣州,並採用陸皓東設計的青天白日旗為起義軍旗;此旗史稱「兩色旗」,孫中山稱:「青天白日,取義宏美,示光明正照自由平等之義。」從事革命之秋,教會懼孫中山波及,宣言把孫驅逐出堂。3月18日,《德臣西報》發表社論,指出革命黨準備成立君主立憲;5月30日,謝纘泰在英文《德臣西報》致光緒皇帝公開信,促請憲政改革。8月下旬,孫中山制定武裝起義方案。 新組織「會長」人事案,延至10月10日,是日孫中山、楊衢雲和其各人馬對峙,劍拔弩張,孫中山、陳少白、鄭士良、楊衢雲、黃詠商、謝纘泰等在香港集議選舉總辦,經過妥協,「會長」職位給楊衢雲,孫中山則獲得廣州起義現場指揮權。興中會選舉會長(稱總辦,時稱「伯理璽天德」,President之音译),楊衢雲志在必得,孫中山退讓,楊衢雲當選。 孫中山利用官方許可,在10月初設立「農學會」。孫中山於汕頭及西江沿岸募集兩軍,同時向廣州進逼;兩軍期於1895年10月某日,一由西南,一由東北,同時向廣州進發。省河緝捕統帶李家綽,憚於孫中山是名醫又是基督徒,不敢輕舉妄動。是为孫中山在廣州發動的第" +407,"活。孫中山與母親、長子孫科以及一雙女兒重逢。兩廣總督譚鐘麟要香港引渡孫中山等人;3月4日,香港總督部堂會同議政局認為孫在港從事反清活動,威脅香港安定和秩序,於是簽發驅遂令,禁止孫中山5年內踏足香港境內Order of banishment,1896-03-04,by the Governor in Council, the Colony of Hong Kong。9月下旬,孫中山赴英國。 ===倫敦蒙難=== File:Sun Yat-Sen plaque.JPG|孫中山逃亡於英國倫敦期間的住處,今為紀念,设有孫的浮雕像 File:1896年10月31日,英國The Graphic報導孫中山获釋.jpg|1896年10月31日,英國The Graphic報導孫中山获釋 1896年9月30日,孫中山在收到康德黎邀請後,抵達倫敦。清駐英國公使龔照瑗即派英籍參贊馬凱尼向英國政府交涉逮捕、引渡孫中山,並僱偵探監視。 1896年10月1日晨,孫中山離開旅社前往波蘭區覃文省街46號,晤見康氏伉儷。10月11日孫中山被龚照瑗誘捕,計畫是逮捕孫中山、秘密遣回,受法律制裁。孫中山得機會說服公使館內一英國" +408,"使館的特別主權和外交權利不受損」,隨即對孫中山稱:你現在自由了!清使館恢復孫中山自由,當天下午康德黎陪孫中山走出使館,向聲援者致謝,後又向公眾講真相。10月24日,孫中山致函倫敦各報主筆:「我對立憲政府和文明國民的認識和感受,更加堅定。這一切促使我更積極地投身於,我那可愛而受壓迫的祖國的進步、教育和文明事業。」孫中山向報紙投稿,召開多次記者會,接受多次訪問,得普遍支持。 1896年11月,孫中山在倫敦應英國劍橋大學漢學家翟理斯邀請,撰寫自傳。從12月5日起,孫中山每日到大英博物館讀書。冬,孫中山為揭露綁架事件真相,開始寫《倫敦蒙難記》。同時孫中山將英國柯士賓所著《赤十字會救傷第一法》譯為漢文。康德黎稱贊孫中山:「他不歇地工作,閲讀有關政治、外交、法律、軍事、海軍的書籍;礦產與礦業、農業、畜牧、工程、政治經濟等類,占據了他的注意,而且細心地和耐心地研究。」 1897年1月至7月,孫中山居倫敦,經常到大英博物館圖書館讀書,認真觀察英國社會政治、經濟狀況。孫中山研究並考察歐洲政治,其間曾訪問第二國際,形成民生主義觀念,初步形成三民主義思想體系;2月,孫中山在倫敦《雙周論壇》發表《中國的現在和" +409,",示願以身家保孫中山回國,據奕劻稱:慈禧太后說:「今聯日已妥,新政待舉,正需孫文回國效用。他人尚優容不暇,自己何獨不能吸引之!」冬,孫中山斷然拒絕清政府各種渠道誘降。 1899年春夏,孫中山在東京、橫濱和長崎等地聯絡策動,準備在廣東、湖南、湖北起事;6月至7月,孫中山在橫濱同章太炎訂交。7月,劉學詢在宗方小太郎安排下與孫中山密談,孫中山稱:「我之革命宗旨始終在興起中國」,劉稱:「若政治革命,可以協力;種族革命,恐其事甚難。天下事當先為其易而後圖其難」;孫中山為菲律賓起義軍購運軍械,租用日輪運抵菲律賓。夏秋間,孫中山再與梁啟超等在橫濱會談聯合反清,仍無結果。10月11日,興中會、洪門、哥老會首領在香港成立興漢會,公推孫中山為總會長。孫中山為「喚起國民及不為康梁所惑」,宣傳反清革命主張與留學生和華僑。秋,孫中山派陳少白赴香港籌辦《中國日報》,於翌年1月出版;孫中山又命鄭士良等在香港設立聯絡會黨機關,密切聯繫廣東三合會。日本人提供報紙經費,報紙開辦先得何啟大力襄助,爾後香港富商之子李紀堂做靠山。冬,孫中山繪制《支那現勢地圖》付印,圖中對「已割之岩疆,已分之鐵路」均以「着色表明」,以便「覽者觸" +410,"港;6月17日孫中山一行抵達香港海面,宮崎、內田等代表孫中山去廣州與劉學詢談判。孫中山準備同李鴻章商談合作,希圖策動兩廣總督在華南「自主」。後因李鴻章奉詔北上,於7月17日趕赴上海回北京協調庚子條約,未獲結果。孫中山主動求見法國駐越北大使,6月21日孫中山抵達西貢,攜帶哈爾蒙德大使給法屬印度支那總督之信。7月,孫中山偕宮崎寅藏等離開香港赴日本,尋求起義後援。9月25日,孫中山化名「吳仲」乘船赴台,謀求武器援助,希望爭取台灣總督兒玉源太郎支持。9月,孫中山由日本抵達台北,建立起義指揮中心。11月10日,台灣總督下驅逐令,孫中山從基隆乘「橫濱丸」赴日本。 1901年1月下旬,孫中山在橫濱為楊衢雲開追悼會。春,孫中山贊助留日廣東籍學生鄭貫一、馮自由等組織廣東獨立協會;6月,秦力山等在東京創辦《國民報》月刊,為中國留學生革命報刊先聲,孫中山捐助出版費1,000元。鄭士良與朋友吃飯時病倒身亡,孫中山恢復廣州活動一切希望幻滅。 1902年,宮崎滔天出版孫自傳《三十三年落花夢》,1903年中文譯本問世。孫中山在日本和章太炎談論時曾涉及革命發難及成功後建都地點,孫中山認為「謀本部,則武昌;謀藩服,則西" +411,"立場,加入三合會。12月,孫中山返回火奴魯魯,多次演説抨擊保皇派,又改組《檀香新報》,親撰《敬告同鄉書》等,揭露保皇派「假革命」、「真保皇」。 1904年1月11日,孫中山決定在檀香山加入洪門致公堂。孫中山並受「洪棍」(元帥)職務,在《檀香新報》發表《駁保皇派》,批判保皇派宣揚革命可招瓜分、中國當前只能實行君主立憲等。孫中山預先取得偽造文件。3月4日,孫中山親到夏威夷司法巡迴處宣誓自己在夏威夷出生。3月14日,孫中山憑着誓詞,拿到出生證明:「他自己的宣誓以及證人所提供的證據,顯示他是在紀元後1870年11月24日出生於夏威夷島。」孫中山再據此領到夏威夷總督頒發之美國島居人民所持護照,然後才加入洪門;洪門為他「特別開台戲」,「由主盟員某大佬封總理為洪棍。洪門軍職,例分洪棍、紙扇、草鞋三級。洪棍,即元帥之別名也」。3月31日,孫中山帶著地方堂口給三藩市三合會之介紹信,啟程前往美國。 1904年4月6日,孫中山抵舊金山,為阻其入境,保皇黨促清朝駐舊金山總領事轉告美國海關,指控孫中山持假護照。4月,孫中山被拘留在碼頭木屋,等候原船下次到舊金山時,撥回檀島;孫中山忽睹被禁鄉人中所閱《中西日報》有" +412,"在留學生中宣傳革命,並於柏林、巴黎組織革命團體;孫中山到布魯塞爾訪問第二國際書記處,闡明革命主張,同情社會主義,稱將「採用歐洲的生產方式,使用機器,但要避免其種種弊端」。 ===創立中國同盟会=== 1905年6月11日,孫中山上「東京」號輪船,離開馬賽赴日,7月19日抵橫濱。7月30日,孫中山邀約各省留學生及華僑70餘人,有來自興中會、華興會、光復會、科學補習所,在東京召開中國同盟會籌備會議,討論建立統一革命組織。孫中山在日本極端民族主義黑龍會總部舉行大型會議,會中決議奉孫中山為領導。會議決定組建中國同盟會。8月20日,中國同盟會在東京召開正式成立大會。 1905年8月13日,孫中山出席東京留學生舉行的歡迎大會並作長篇演説,號召摒棄保皇派反對革命之説,以革命推翻清政府建立民主共和國。孫中山演說會場是東京富士見樓(麴町區飯田河岸),會場能容納千人。大會由宋教仁主持,有逾700名學生雲集在大廳內,許多學生不得其門而入,簇擁在門外,聆聽孫中山首度在東京公開發表演說。屆時爆滿,後來者猶絡繹不絕,門外擁擠不通;警吏令封門,諸人在外不得入,甚為喧嘩;又開門聽其進場。 ===第三至第十次起義===" +413,"當局受清政府施壓,決定驅逐孫中山離境;為表達善意,日方除發布驅逐令之外,還以外交辭令表達歉意,並饋贈孫中山大筆款項。3月,孫中山與胡漢民、汪精衛離日赴越南,在河內甘必達街61號設立廣東、廣西、雲南地區武裝起義總機關;孫中山又在河內、海防、南圻建立中國同盟會分會,並向旅越華僑募集起義經費。孫中山謊報金額,章太炎與宋教仁譴責,認為孫被日本政府收買;章太炎撤掉掛在《民報》社的孫照片,要求孫辭去中國同盟會總理一職。4月,孫中山派胡漢民赴香港策應潮、惠、欽、廉起義。5月,孫中山派黃興、胡毅生分赴郭人漳、趙聲部,爭取新軍反正;5月下旬至6月上旬潮州黃岡起義和惠州七女湖之役,不久失敗;9月1日,孫中山命王和順於欽州王光山起義,防城起義爆發,因補給困難退入十萬大山,失敗;9月至10月,孫中山派中國同盟會會員赴海防、西貢、新加坡、暹羅、檳榔嶼、吉隆坡等地籌集革命經費。10月,孫中山私自委託宮崎滔天在東京幫他做財務處理,洽商購買軍火,形式上把同盟會其他領袖排除在軍事機密外。12月2日廣西鎮南關之役,孫中山偕黃興等親赴陣地參戰。 1908年3月,孫中山離開河內,經西貢赴新加坡籌款;3月27日,孫中山指示黃興" +414,"致函中國同盟會會員王子匡等,解釋陶成章、章太炎之攻擊,指其危及「革命前途」。11月,章炳麟猛烈反擊偽《民報》,呼籲海外華人不要捐資給孫中山;陶成章譴責乾綱獨斷,再度要求同盟會開除孫中山。11月至12月,孫中山自英國抵美國,向紐約、波士頓等地華僑籌款。 1910年1月至2月,孫中山由紐約經芝加哥抵達舊金山,建立10多個同盟會分會。3月,孫中山建立「美洲三藩市中國同盟會總會」(通稱「美洲同盟總會」),建議改組《美洲少年》周刊為《少年中國晨報》作機關報。同月,孫中山抵檀香山,改組興中會為中國同盟會檀香山分會。6月,孫中山密赴日本,晤黃興、趙聲等;7月,孫中山抵新加坡;8月至10月,孫中山在檳榔嶼指導整頓南洋同盟會組織,準備起義;11月13日,孫中山在檳榔嶼主持秘密會議,「鼓其勇氣,乘此良機,重謀大舉」;11月中下旬,孫中山派趙聲往香港聯絡廣州新軍;12月,孫中山再赴歐美籌款。 1911年1月至2月,孫中山經歐洲到美洲,在紐約、舊金山、溫哥華等地籌款。3月中旬,孫中山往美國東部,沿途在加拿大籌款。5月3日,孫中山在美國芝加哥召集會議,商討起義善後及再圖大舉等問題。6月,孫中山在舊金山聯合美洲同" +415,"同盟會在新會縣(今江門市)發動武装起义。11月23日,孫中山於馬賽會晤東方滙理銀行總裁西蒙(Stabislas Simon)。11月24日孫中山由法國乘「丹佛」號輪船,行前電集會上海討論中央政府組成之各省代表,稱「但求早鞏固國基」,可推舉黎元洪或袁世凱任總統,「此後社會當以工商實業為競點」。 ===建立民國=== File:孫中山先生.jpg|1911年6月,孫中山在旧金山留影 File:Xh3 Color.jpg|摄于1912年1月,孫中山就职临时大总统 File:1912年元旦,孙中山在南京就任中华民国临时大总统。就职仪式上,宣读的大总统誓词。.jpg|1912年元旦,孫中山在就职仪式上,宣读的大总统誓词 File:1912Jimingxiaoling.jpg|1912年2月15日,孙中山祭明孝陵 1911年12月14日,各省諮議局代表雲集南京,共計44人,分別代表17個省份。12月21日,孫中山過香港,晤胡漢民、廖仲愷,拒絕留在廣東,堅持赴上海、南京,因「滬寧在前方」而「今之大患在無政府」。孫中山稱:「今日中國如能以和平收革命之功,此亦足開世界未有之例,何必言兵。」12月25日," +416,"抵達南京,晚上10時在南京總統府舉行中華民國臨時大總統就職典禮,宣誓就任。 孫中山《臨時大總統就職宣言》和《告全國同胞書》,宣告中華民國成立;《宣言》規定臨時政府:「盡掃專制之流毒,確定共和,普利民生,以達革命之宗旨」,對內「民族之統一」、「領土之統一」、「軍政之統一」、「內政之統一」、「財政之統一」,對外消除「辱國之舉措」、「排外之心理」,主張「和平主義」。 南京臨時政府定用五色旗為國旗。1912年1月2日,孫中山通電各省改用陽曆,並以1月1日為民國建元之始;1月3日,孫中山公布各部總長、次長名單組閣,各省代表會改組臨時參議院;1月4日,孫中山電令廣東代理都督陳炯明出兵北伐:「和議無論如何,北伐斷不可廢」;1月7日,孫中山《告友邦書》,稱民國承認「革命軍興」前清與各國之條約、借貸和權利。1月8日,孫中山批准發行中華民國軍需公債,定額1億元,只售出700多萬。孫中山不願延長內戰,雙方遂派代表在上海議和,孫中山提出:一、清帝退位;二、袁絕對贊同共和;三、孫自清帝退位後辭職;四、由參議院選舉袁為臨時大總統;五、袁被選後,當宣誓遵守《臨時約法》,袁必「誓守參議院所定之憲法,乃能接受事權」,袁" +417,"災民;嚴禁官吏違法;鼓勵華僑投資;保護工商業;改革教育。三個月中,南京臨時政府共頒布30餘件法令有利建政和工商業。3月3日,中國同盟會本部於南京召開會員大會,5,000人參加,宣布以「鞏固中華民國,實行民生主義」為宗旨,制訂9條政綱,舉孫為總理。3月11日,《中華民國臨時約法》頒布,強調「主權屬於國民全體」。參議院為防袁野心,特設「責任內閣制」,以唐紹儀為國務總理,並設國務會議以行使職權,但袁不能容,唐紹儀被迫辭職,以陸徵祥代之。3月29日,孫中山出席臨時政府各部總次長、衛戌總督、各軍師旅司令官舉行餞別宴會。1914年5月,臨時約法遭罷廢,新法除去所有行政權約束,並無限期延長總統任期。 ===任鐵路督辦=== 1912年4月1日,孫中山在南京正式解除臨時大總統職務,袁世凱繼位。同日中國同盟會會員舉行餞別會,孫中山重申要社會革命,「一面圖國家富強,一面當防資本家壟斷之流弊」。孫中山辭職後不再過問政治,離開南京,前赴上海。4月9日,孫中山應黎元洪邀抵武漢。孫中山在廣東說:「吾國乃社會主義之民國,欲行社會主義。吾等皆是真誠的社會主義領袖。」6月,唐紹儀內閣垮台。 8月,宋教仁以中國同盟會為基" +418,"2月上旬,孫中山通電呼籲政府和國民反對沙俄強迫外蒙簽訂《》和《商務專條》。 1913年2月11日,孫中山赴日本考察,藉以籌措外資興建鐵路。2月中旬至3月下旬,孫中山訪問長崎、門司、下關、神戶、東京等,參觀工廠和學校,闡明中日「互為提攜」,與三井物產董事山本條太郎等籌設「中國實業公司」。2月至6月,袁世凱提供巨額,誘使國民黨員另組政團,有國事維持會、政友會、超然議員社等從國民黨分裂出來。3月4日晚,孫中山在東京設宴答謝日本各界人士,牧野外相等內閣閣員應邀出席。3月11日,孫中山與宋嘉樹、戴季陶等參觀大阪《每日新聞》社。3月14日,孫中山與戴季陶、馬君武、宋嘉樹、何天炯等出席神戶國民黨交通部正支部長吳錦堂在寓所所設午餐會。 ===二次革命與中華革命黨=== 1913年3月20日,宋教仁在上海滬寧車站遇刺,兩天後重傷不治。3月22日,孫中山在長崎悉宋教仁在上海被刺;3月23日,孫中山參觀長崎三菱造船所後,下午即乘船返國。3月25日,孫中山抵上海力主公開與北京政府決裂,並以武力抗袁。 3月27日,孫中山在黃興寓所商討對策時,主張「聯日」、「速戰」,願親任其事,大部與會者希望「法律解決」;6月中" +419,"幼年,從學私墪,僅識之無。不數年,得至檀香山,就傅西校,見其教法之善,遠勝吾鄉。故每於課暇,輒與周圍同學諸人,相談衷曲,而改良祖國,拯救同類之願,於是乎生。當時所懷,一若必使我國人人皆免苦難,皆享福樂而後快。」7月8日,中華革命黨在東京築地精養軒舉行成立大會,與會者約300餘人。孫中山任總理,公布手書《中華革命黨總章》。入黨者必须按指印、立誓約,絕對服從孫,發誓「為救中國危亡,拯生民困苦,願犧牲一己之身命自由權利。」孫中山的提案遭到部分革命黨人抵制,特別是黃興。 1915年3月14日,孫中山致書日本外務省大臣,向日本政府承諾優於二十一條要求之權利讓渡。11月15日,孫中山委居正為中華革命軍東北軍總司令。袁着「國體投票」,要各區、各省之國民代表,投票贊成改行君主政體,又指使參議議員上書擁戴袁為皇帝。 1916年4月9日,孫中山、宋慶齡等在日本友人田中昂寓所舉行小型聲討袁集會(又稱「帝-{制}-取銷一笑會」)。4月27日,孫中山為討袁大計,偕廖仲愷、戴季陶、張繼等,離日返國。5月,孫中山函黃興,稱「中國問題實為新舊之爭。換言之,則為民黨與官僚派之爭」;主張加強團結及武裝。6月6日,袁逝世。" +420,"會走錯誤的一步。因此我們不願意配合當前動作。」9月10日,孫中山正式就任。9月13日,廣州軍政府對德宣戰。財政方面,自軍政府部長到辦事員,每人月支零用費20元;孫中山委托廖仲愷經營,僑胞百餘萬元捐款起支撐。蔣介石撰上對北洋軍作戰計劃及滇粵兩軍對於閩浙單獨作戰之計劃。10月上旬,護法戰爭開始衡山、寶慶戰鬥;10月12日《中華新報》刊出孫中山發布討伐段祺瑞令,指段祺瑞「陽托共和,陰行帝制」,成為「共和之蟊賊,人民之大蠹」,號召全國「討滅偽政府,還我約法,還我國會,還我人民主權」。11月18日,孫中山通電反對南北調和,堅持「恢復約法及舊國會」。 由於南方軍閥無意護法,故不久即醞釀議和。1918年5月,軍政府改組,實權掌握在軍閥手中。軍政府以七總裁取代大元帥,領導南方與西南各省。國會非常會議通過《軍政府改組大綱修訂案》,選舉唐紹儀、唐繼堯、孫中山、伍廷芳、林葆懌、陸榮廷、岑春煊為政務總裁;不久,岑春煊被推舉為主席總裁。5月4日《軍政府改組修正案審查報告》通過後,孫中山即向國會辭去大元帥職;5月21日孫中山離開廣州,5月26日到三河壩。6月1日,孫中山由汕頭經台北前赴日本。 1918年5月下旬," +421,"「以分縣自治為立國基礎」。 ===為非常大總統=== 1921年年初,舊國會在廣州重開,因不足法定人數,故稱國會非常會議,推舉孫中山為中華民國非常大總統。孫中山設法號召舊國會225名議員,於4月7日在廣州集會,推選總統。4月,國會通過《中華民國政府組織大綱》。5月5日,孫中山就職,實際轄區僅有廣東,實權在陳炯明。民國政府成立。7月,孫中山下令討伐廣西,兩個月克南寧、桂林、龍州。10月,廣州非常國會批准孫北伐計劃。10月中旬,孫中山巡廣西,3萬人準備北伐,組織大本營;10月29日孫中山在梧州設大本營,進軍桂林,又派汪精衛到廣州籌餉。 1922年2月3日孫中山下動員令,10萬餘人出師北伐。4月18日,孫中山派廖忡愷晤陳炯明,勸其晤商北伐;陳炯明拒不前往,電辭本兼各職。4月21日,孫中山下令免陳炯明廣東省長、內務部長兼粵軍總司令職,專任陸軍部長;晚,陳炯明退惠州。5月4日孫中山令從韶關北伐,李烈鈞為北伐軍總司令,許崇智為三路總指揮;5月6日孫中山赴韶關督師,旋大本營總攻擊令,北伐軍三路進江西。陳炯明主張聯省自治,與直系軍閥勾結。6月1日,孫中山返回廣州。6月16日,陳炯明事變,葉舉炮打孫中山" +422,"。 1921年12月底,共產國際代表馬林由張太雷陪同,在桂林訪問孫中山長談3次。 1922年8月,蘇聯政府秘密以馬林聯繫孫中山;馬林和李大釗多次晤孫,討論振興中國國民黨。 1923年1月17日至1月26日,蘇聯政府全權代表越飛到上海與孫會談,月底發表《孫文越飛聯合宣言》,確立孫中山「聯俄」政策,包括:蘇聯願援助中國完成統一;蘇聯願拋棄沙俄對華之不平等條約,另行開始中蘇交涉;中東鐵路問題由中蘇協商,暫維持現狀;蘇聯無意使外蒙脫離中國,蘇軍也不必立時撤退。同年,孫中山派出「孫逸仙博士代表團」回訪蘇聯,邀請蘇聯政治與軍事顧問回到廣州,幫助中國革命。 1924年1月20日至1月30日,廣州召開中國國民黨第一次全國代表大會。1月20日上午的開幕演說上,孫中山強調必須改組黨,將黨變成革命、建國工具。大會主席團由胡汉民、汪精卫、林森、李大钊及谢持5人組成,蘇聯顧問鮑羅廷也出席大會;大會決議中國共產黨黨員可以個人身份加入中國國民黨,但須服膺三民主義及遵守黨綱黨紀;與會代表百多人,包括20多名中國共產黨黨員。 1924年6月16日,孫中山在鮑羅廷等陪同主持黃埔軍校開學典禮。6月,黃埔軍校正式開學,第一期" +423,"龄、蒋介石、廖仲恺,台下身著白色西裝的歐洲人為苏联代表 File:Sun Yat-sen 1924 Guangzhou.jpg|孫中山晚年照(1924年),被引用在新台幣100圓紙鈔上 ==北上与逝世== 1924年10月下旬,直系第三軍軍長馮玉祥發動北京政變。10月30日,孫中山捨棄在韶關建立之總部,返回廣州。馮玉祥、張作霖、段祺瑞分別致電孫中山,請他北上共商國是。11月,孫中山應邀北上討論國是,提出「召開國民會議和廢除不平等條約」兩大號召,同帝國主義和北洋軍閥段祺瑞、張作霖等作鬥爭。孫中山發表《北上宣言》,主張召開國民會議,以謀求中國和平統一,對外廢除一切不平等條約,對內實行民治。11月17日,孫中山抵上海。孫中山稱:「第一點就要打破軍閥,第二點就要打破援助軍閥的帝國主義。打破了這兩個東西,中國才可以和平統一,才可以長治久安。」11月24日,孫中山與宋慶齡抵達日本神戶。11月25日,孫中山在神戶,頭山滿前來拜訪長談。11月28日,孫中山在神戶演講《大亞洲主義》,主張日本政府不要做「西方霸道的鷹犬」,而應做「東方王道的干城」。孫中山在《入京宣言》中稱:中國「仍處在帝國主義各國殖民地之" +424,"明。又字載之:孫於乙未廣州起義失敗後,漫遊歐美時之假名,倫敦蒙難後,劍橋大學教授翟理斯撰傳,孫遂以載之寫入傳中,然自後絕不稱用。 教名日新:1884年,孫領洗為基督徒時取名「日新」,取自《禮記·大學·盤銘》「苟日新,日日新,又日新」。 號逸仙:區鳳墀據「日新」二字,為孫改號「逸仙」,寓意「自由神」。羅馬化:「日新」的廣州話音譯為英語時是Yat Sen ,因此與洋人交往時一律採孫逸仙(Sun Yat Sen)。 化名中山樵:1897年秋到日本後化名中山樵,事緣日本人平山周回憶他與孫在日本最初交往時說:「總理來京曰:『昨夜熟慮,欲且留日本』。即同車訪犬養,歸途過日比谷中山侯爵邸前,投宿寄屋橋外對鶴館,掌櫃不知總理為中國人,出宿泊帖求署名。弟想到中山侯爵門標,乃執筆書〔姓〕中山,未書名;總理忽奪筆自署〔名〕樵。曰:『是中國山樵之意也』。總理號中山,蓋源於此」。化名高野長雄:孫易名以秘密通信,蓋日本明治維新志士中有高野長英者,由平山周代擬。甲辰年(1904年)以前秘密通信署名多用中山,以後改用高野。孫中山還有化名陳文、陳載之、中山二郎、吳仲、高達生、杜嘉諾、艾斯高野、阿羅哈等,總數達30餘個。" +425,"提出「人能盡其才,地能盡其利,物能盡其用,貨能𣈱其流」的變法自強主張遭拒絕;後創立興中會,提出「驅除韃虜,恢復中華,創立合眾政府」的民主革命主張。 孫中山思想的核心是堅決、完整的民主主義;在孫領導下,中國歷史由「帝國」轉變到「民國」,此後歷史雖變,但將中國建成高度民主的現代國家始終是中華兒女不變的目標。 1905年,組成中國同盟會,確定「驅除韃虜,恢復中華,建立民國,平均地權」的資產階級革命政綱。 1912年,列寧在《湼瓦明星報》發表《中國的民主主義和民粹主義》:「孫中山綱領的每一行都滲透了戰鬥的、真實的民主主義。他充分認識到『種族』革命的不足,絲毫沒有對政治表示冷淡,甚至絲毫沒有忽視政治自由或容許中國專制制度與中國『社會革命』、中國立憲改革等並存的思想。這是帶有建立共和制度要求的完整的民主主義。它直接提出群眾生活狀況及群眾鬥爭問題,熱烈地同情勞動者和被剝削者,相信他們是正義的和有力量的。」 馮自由在《革命逸史》稱:「國父之信教,完全出於基督救世之宗旨。……然其所信奉之教義,為進步的及革新的,與世俗之墨守舊章,思想陳腐者,迴然不同。」 在哲學上,孫中山提出「知難行易」說,批判「知之非艱" +426,"後已。像很多站在正面指導時代潮流的偉大歷史人物大都有他們的缺點一樣,孫先生也有他的缺點方面。這是要從歷史條件加以說明,使人理解,不可以苛求於前人的。」 1925年1月31日,已卸任日本遞相的犬養毅於東京回憶稱:「孫實不愧為一廉潔之士,處世接物,既決無半點私心,且又常以實現其理想為懷,其為自己之主義而苦戰奮鬥,備極勞瘁者,蓋數十年。雖其所志未克盡伸,然其勇往直前之氣,固數十年如一日也。」 《孫逸仙傳記》作者美國人林百克認為:孫中山做革命事業並不把錢當做最重要緊,他曾說:「金錢並不是常常有用的,北京政府有很多的錢,但是他仍舊得不到真正替他出力的人。人民覺醒之後,金錢在革命事業裡面是比較不重要的。」 法國漢學家白吉爾認為,孫中山是20世紀之子,是現代中國肇建者之一。 ==紀念== 1912年3月10日,孫中山與胡漢民、孫科等前往紫金山狩猎,稱希望百年後葬於此。 1925年4月16日,廣東省政府將香山縣改稱中山縣;永豐艦改名中山艦,以資紀念。 1929年6月1日,孫中山靈柩安葬於南京紫金山中山陵。 ==家庭== 孫中山祖父孫敬賢有三個兒子,依序是孫達成(1813年-1888年,孫的父親)、孫學" +427,"再婚):宋慶齡(1893年1月27日—1981年5月29日) **日本籍平妻:大月薰(1887年9月16日—1970年) ***女:宮川富美子(1906年5月12日—1990年5月6日) **日本籍外婦或妾:淺田春(1882年—1902年) ===家族世系圖=== ==著作== 孫中山著有書籍《三民主義》(1924年十六講版)、《革命方略》、《五權憲法》、《中國革命史》(1923年)、《中國存亡問題》(1917年)、《建國大綱》(1924年)、《建國方略》(1921年)、《民權初步》(1917年)(後編為《建國方略》之三)、《孫文學說》(1919年)(後編為《建國方略》之一)、《實業計畫》(1920年)(後編為《建國方略》之二)。孫中山遺著編有《孫中山選集》、《孫中山全集》等。台北1969、1973、1985年出版的《国父全集》等。 1917年2月21日,孫中山寫成《社會建設》(又名《民權初步》),後編為《建國方略》之三;書中述及會議細則,「教吾國人行民權之第一步」。 1919年,《實業計劃》首次發表,兩年後由上海民智書局出版英文版和中文版單行本,孫中山在本書中繪制一幅中國近代化的藍圖" +428,":明報報業,2004年10月27日世紀版D4版. * 陳沂:《中山先生的名字》,《中央日報》,台北:中央日報社,2001-08-18. * 唐德剛:《晚清七十年:袁世凱、孫文與辛亥革命》,臺北:遠流出版,1998年,. * 陳破空:《百年反思,孫中山是問題人物》,香港:開放雜志,2011年,〈百年反思,孫中山是問題人物〉. * 古屋奎二:《蔣總統秘錄》,日本《產經新聞》連載,中央日報社 譯. * 中山學術文化基金會:《中山先生與日本》. * 黃宇和:《三十歲前的孫中山:翠亨.檀島.香港,1866-1895》,香港:中華書局,2011年. * 黃宇和:《孫逸仙倫敦蒙難真相》,上海:上海書店出版社,2004年. * 黃宇和:《孫逸仙在倫敦,1896-1897:三民主義思想探源》,台北:聯經出版,2007年. * 張緒心、Leonard H.D. Gordon 著,卜大中 譯:《孫中山未完成的革命》,台北:時報文化,1993年. * 沙培德:〈民權思想與先鋒主義:民國時期孫中山的政治主張〉. * C. Martin Wilbur(韋慕庭)著,楊慎之 等 譯:《孫中山——壯志未酬的愛國者》,廣" +429,"gory:嶺南大學校友 (廣州市) Category:香港大學校友 Category:香港中文大學校友 Category:澳門醫生 Category:澳門外交史 Category:中国政治哲学家 Category:清末革命家 Category:香港革命家 Category:辛亥革命人物 Category:中華民國大陸時期政黨創始人 Category:醫生出身的總統 Category:中華民國大總統 Category:中華民國政治理論家 Category:中國國民黨主席 Category:二次革命人物 Category:广州政治人物 Category:罹患胆囊癌逝世者 Category:葬于南京 Category:高台教圣人 Category:寶光嘉禾勳章獲得者 Category:建国勋章获得者 Category:中国纸币上的人物 Category:亞洲硬幣上的人物 Category:民主社会主义者 Category:中国社会运动者 Category:任內身亡的中国政治人物 Category:在英屬香港的清朝人 Category:亚细亚主义者 Category:在北京身亡者 Catego" +430,"操作系统(,縮寫:)是一组主管并控制计算机操作、运用和运行硬件、软件資源和提供公共服务来组织用户交互的相互关联的系统软件程序,同时也是计算机系统的核心与基石。操作系统需要处理如管理與配置内存、決定系統資源供需的優先次序、控制輸入與輸出裝置、操作网络與管理文件系统等基本事務。操作系统也提供一個讓使用者與系統互動的操作界面。 操作系统的型態非常多樣,不同機器安裝的操作系统可從簡單到複雜,可從行動電話的嵌入式系统到超級電腦的大型作業系統。許多操作系统製造者對它涵盖范畴的定义也不尽一致,例如有些操作系统整合了图形用户界面,而有些僅使用命令行界面,將图形用户界面視為一種非必要的應用程式。 操作系统理论在计算机科学中,為歷史悠久而又活跃的分支;而操作系统的设计与实现则是软件工业的基础与核心。 == 歷史 == 綜觀電腦之歷史,作業系統與電腦硬體的發展息息相關。作業系統之本意原為提供簡單的工作排序能力,後為輔助更新更複雜的硬體設施而漸漸演化。從最早的批次模式開始,分時機制也隨之出現,在多處理器時代來臨時,作業系統也隨之添加多處理器協調功能,甚至是分散式系統的協調功能。其他方面的演變也類似於此。另一方" +431,"現代的IBM機器上執行。 OS/360也包含另一個優點:永久貯存裝置—硬盘的面世(IBM稱為DASD)。另一個關鍵是分時概念的建立:將大型電腦珍貴的時間資源適當分配到所有使用者身上。分時也讓使用者有獨佔整部機器的感覺;而Multics的分時系統是此時眾多新作業系統中實踐此觀念最成功的。 1963年,奇異公司與貝爾實驗室合作以PL/I語言建立的MulticsMulticians.org and the History of Operating Systems , Thomas Haigh, 13 September 2002,是激發1970年代眾多作業系統建立的靈感來源,尤其是由AT&T貝爾實驗室的丹尼斯·里奇與肯·湯普遜所建立的Unix系統,為了實踐平台移植能力,此作業系統在1973年由C語言重寫;另一個廣為市場採用的小型電腦作業系統是VMS。 === 1980年代 === 第一代微型计算机並不像大型電腦或小型電腦,沒有裝設作業系統的需求或能力;它們只需要最基本的作業系統,通常這種作業系統都是從ROM讀取的,此種程式被稱為監視程式(Monitor)。1980年代,家用電腦開始普及。通常" +432,"他公司產品的原因在於他有可攜性,並且相容於任何符合IBM PC架構的機器上。這樣的PC可利用Intel-8088處理器(16-bit暫存器)定址,並最多可有1MB的記憶體,然而最初-{只}-有640KB。軟式磁碟機取代了過去的磁帶機,成為新一代的儲存裝置,並可在它512KB的空間上讀寫。為了支援更進一步的檔案讀寫概念,磁碟作業系統(Disk Operating System,DOS)因而誕生。此作業系統可以合併任意數量的磁區,因此可以在一張磁碟片上放置任意數量與大小的檔案。檔案之間以檔名區別。IBM並沒有很在意其上的DOS,因此以向外部公司購買的方式取得作業系統。1980年微軟公司取得了與IBM的合約,並且收購了一家公司出產的作業系統,在將之修改後以MS-DOS的名義出品,此作業系統可以直接讓程式操作BIOS與檔案系統。到了Intel-80286處理器的時代,才開始實作基本的儲存裝置保護措施。其後,MS-DOS成爲了IBM PC上面最常用的作業系統(IBM自己也有推出DOS,稱為IBM-DOS或PC-DOS)。MS-DOS的成功使得微软成為地球上最賺錢的公司之一。 MS-DOS在個人電" +433,"業系統。由於圖形化使用者介面日趨繁複,作業系統的能力也越來越複雜與巨大,因此強韌且具有彈性的作業系統就成了迫切的需求。此年代是許多套裝類的個人電腦作業系統互相競爭的時代。 上一年代於市場崛起的蘋果電腦,由於舊系統的設計不良,使得其後繼發展不力,蘋果電腦決定重新設計作業系統。經過許多失敗的專案後,蘋果於1997年釋出新作業系統——Mac OS X的測試版,而後推出的正式版取得了巨大的成功。讓原先失意離開蘋果的史蒂夫·乔布斯風光再現。 除了商業主流的作業系統外,從1980年代起在开放源代码的世界中,BSD系統也發展了非常久的一段時間,但在1990年代由於與AT&T的法律爭端,使得遠在芬蘭赫爾辛基大學的另一股開源作業系統——Linux興起。Linux核心是一個標準POSIX核心,其血緣可算是Unix家族的一支。Linux與BSD家族都搭配GNU計畫所發展的應用程式,但是由於使用的许可证以及歷史因素的作弄下,Linux取得了相當可觀的開源作業系統市佔率,而BSD則小得多。相較於MS-DOS的架構,Linux除了擁有傲人的可移植性(相較於Linux,MS-DOS衹能運行在Intel CPU上)," +434,"open、fopen、socket、exec、calloc等) C函式庫(open、fopen、socket、exec、calloc等) C函式庫(open、fopen、socket、exec、calloc等) C函式庫(open、fopen、socket、exec、calloc等) 核心模式 系統中斷、呼叫、錯誤等軟硬體訊息 系統中斷、呼叫、錯誤等軟硬體訊息 系統中斷、呼叫、錯誤等軟硬體訊息 系統中斷、呼叫、錯誤等軟硬體訊息 核心模式 核心(驅動程式、行程、網路、内存管理等) 核心(驅動程式、行程、網路、内存管理等) 核心(驅動程式、行程、網路、内存管理等) 核心(驅動程式、行程、網路、内存管理等) 核心模式 硬體(處理器、内存、各種裝置) 硬體(處理器、内存、各種裝置) 硬體(處理器、内存、各種裝置) 硬體(處理器、内存、各種裝置) 另一方面,微軟對於更強力的作業系統呼聲的回應便是Windows NT於1993年的面世。 1983年開始微軟就想要為MS-DOS建構一個圖形化的作業系統應用程式,稱為Windows(有人說這是比尔·盖茨被蘋果的Lisa電腦上市所刺激)。一開始Windo" +435,"用者模式,因此可以避免使用者程式執行非法行動。 簡化版本的Windows NT抽象架構 簡化版本的Windows NT抽象架構 簡化版本的Windows NT抽象架構 簡化版本的Windows NT抽象架構 簡化版本的Windows NT抽象架構 簡化版本的Windows NT抽象架構 簡化版本的Windows NT抽象架構 使用者模式 OS/2應用程式 Win32應用程式 Win32應用程式 DOS程式 Win16應用程式 POSIX應用程式 使用者模式 OS/2應用程式 其他DLL函式庫 DOS系統 Windows模擬系統 POSIX應用程式 使用者模式 OS/2子系統 Win32子系統 Win32子系統 Win32子系統 Win32子系統 POSIX.1子系統 核心模式 系統服務層 系統服務層 系統服務層 系統服務層 系統服務層 系統服務層 核心模式 輸入輸出管理檔案系統、網路系統 物件管理系統 / 安全管理系統 / 行程管理 / 物件間通訊管理 / 行程間通訊管理 / 虛擬記憶體管理微核心 物件管理系統 / 安全管理系統 / 行程管理 / 物件間通訊管理 / 行程間通訊管理 /" +436,"這套系統,因此很多早期遊戲便無法在Windows NT上執行。Windows NT有3.1、3.5、3.51與4.0版。Windows 2000是Windows NT的改進系列(事實上是Windows NT 5.0)、Windows XP(Windows NT 5.1)以及Windows Server 2003(Windows NT 5.2)與Windows Vista(Windows NT 6.0)也都是立基於Windows NT的架構上。 而本年代漸漸增長並越趨複雜的嵌入式設備市場也促使嵌入式作業系統的成長。 === 今日 === 現代作業系統通常都有一個使用的繪圖裝置的图形用户界面(GUI),並附加如滑鼠或觸控面版等有別於鍵盤的輸入裝置。舊的OS或效能導向的伺服器通常不會有如此親切的介面,而是以命令行界面(CLI)加上鍵盤為輸入裝置。以上兩種介面其實都是所謂的殼,其功能為接受並處理使用者的指令(例如按下一按鈕,或在命令提示列上鍵入指令)。 選擇要安裝的作業系統通常與其硬體架構有很大關係,-{只}-有Linux與BSD幾乎可在所有硬體架構上執行,而Windows NT僅移植到了DE" +437,": * Burroughs MCP-- B5000,1961 to Unisys Clearpath/MCP, present. * IBM OS/360 -- IBM System/360, 1964 to IBM zSeries, present * UNIVAC EXEC 8 -- UNIVAC 1108, 1964, to Unisys Clearpath Dorado, present. 现代的大型主机一般也可运行Linux或Unix变种。 ==== 嵌入式 ==== 嵌入式系統使用非常廣泛的系統(如VxWorks、eCos、Symbian OS及Palm OS)以及某些功能縮減版本的Linux或者其他作業系統。某些情況下,OS指稱的是一個內建了固定應用軟體的巨大泛用程式。在許多最簡單的嵌入式系統中,所謂的OS就是指其上唯一的應用程式。 ==== 類Unix系統 ==== File:Unix history-simple.png|256px|thumb|Unix-{zh-hant:系統的演變;zh-hans:系统的演变}- default 所謂的類Unix家族指的是一族種類繁" +438,"家族及貝爾實驗室研發小組設計的九號計畫與Inferno系統取代,皆用於分散運算式環境。它們並不像其他Unix系統,而是選擇內建图形用户界面。九號計畫原先並不普及,因為它剛推出時並非自由軟體。後來改在自由及開源軟體許可證Lucent Public License釋出後,便開始擁有廣大的使用者及社群。Inferno已被售予Vita Nuova並以GPL/MIT許可證釋出。 當前,計算機按照計算能力排名世界500強中全數皆使用Linux系統。 ==== 微軟Windows ==== Microsoft Windows系列作業系統是在微軟給IBM機器設計的MS-DOS的基礎上設計的圖形作業系統。現在的Windows系統,如Windows 2000、Windows XP皆是建立于現代的Windows NT核心。NT核心是由OS/2和OpenVMS等系統上借用來的。Windows可以在32位元和64位元的Intel和AMD的處理器上運行,但是早期的版本也可以在DEC Alpha、MIPS與PowerPC架構上運行。 雖然由於人們對於開放原始碼作業系統興趣的提升,Windows的市場佔有率有所下降," +439,"功能,並且其擁有介面華麗的Aero Glass。但是整體而言,其在全球市場上的口碑卻並不是很好。其後繼者Windows 7則是於2009年10月22日發售,Windows 7改善了Windows Vista為人詬病的效能問題,相較於Windows Vista,在同樣的硬體環境下,Windows 7的表現較Windows Vista為好。 Windows 8 於2012年10月26日發售,與Windows Phone 8共用核心,取消了開始按鈕,並使用了全新動態磚介面,並可使用Windows 市集購買安裝App,但由於整體介面相較於前幾版是很大的更改,以及為觸控設計的介面,讓不少鍵盤滑鼠使用者比較不習慣。微軟於隔年2013年10月18日發布了Windows 8.1,並加回了開始按鈕。 而最多人使用的Windows 的Windows 10則是於2015年7月29日發售。而每次大更新後就更改版本號,目前最新為21H2。會在2025年10月25日停止支援Windows 10家用版及專業版 Windows 11於2021年10月5日發行ISO等安裝檔,但無發售盒裝版。是目前最新的Windows," +440,"nux套件為例外。 少數較舊的OS今日依然在一些需要穩定性的市場中活躍,例如IBM的OS/2、BeOS以及XTS-400。 在達康時代狂潮過後,如AmigaOS與RISC OS等少數人使用的OS依然持續建立,以滿足狂熱的愛好者社群與特殊專業使用者。 优秀的操作系统必定要具备能让各种能力级别的用户满足各种需求的工具包可视化的高效简便程序环境,以程序来创建程序,就如立法的法律指导规范新的法案的创建。这其实是操作系统所隐含的一部分,决定了可以有多强的扩充能力。比如,某个操作系统有对应的开发者工具包集成环境程序并且是个可视化的,还能让能力不太高的人一看能懂的,用于修改某个声音合成器包含频谱显示,可要是操作系统本身不支持也没有提供可用的工具程序,就会变得很繁琐,很可能为了达到这个目标,开发者就要预备好各种相关的工具程序,甚至自己来编写一系列相关的工具程序创建专门的工具包,最后还要检查代码。若是有人想通过音乐来生成五线谱,要对类似标记语言的五线谱规则对应一下,对各种音色,音准采集并总结出一整套规律,通过声音识别可以识别出乐器的类别等,进而标出乐符完成从声音到乐谱的转换并生成。可是对人声却又更为复杂" +441,"作,正努力讓自己改進使用環境。 eyeOS是一套基於PHP實現的半開源模擬雲端運算操作系统,但其實質只是在網路瀏覽器中提供一種類似無界限的類獨立操作系统,其並非於任何類似虛擬化中實現的真正電腦操作系统,能提供諸如Word、PPT、Excel的在線處理功能… GNU Hurd是一個以完全相容Unix並加強許多功能为目标的微核心架構。微軟Singularity是一個奠基於.Net並以建立較佳記憶體保護機制為目標的研究計畫。 == 功能 == 操作系统位于底层硬件与用户之间,是两者沟通的桥梁。用户可以通过操作系统的用户界面,输入命令。操作系统则对命令进行解释,驱动硬件设备,实现用户要求。以现代标准而言,一个标准PC的操作系统应该提供以下的功能: * 进程管理(Processing management) * 内存管理(Memory management) * 文件系统(File system) * 网络通信(Networking) * 安全机制(Security) * 用户界面(User interface) * 驱动程序(Device drivers) === 进程管理 === 不管是常駐" +442,"動式操作系统也提供某種程度的回饋機制,讓直接與使用者互動的行程擁有較高的特權值。 除了行程管理之外,操作系统尚有擔負起行程間通訊(IPC)、行程異常終止處理以及死锁(Dead Lock)偵測及處理等較為艱深的問題。 在行程之下尚有執行緒的問題,但是大部分的操作系统並不會處理執行緒所遭遇的問題,通常操作系统僅止於提供一組API讓使用者自行操作或透過虛擬機器的管理機制控制執行緒之間的互動。 === 内存管理 === 根據帕金森定律:「你给程序再多内存,程序也会想尽办法耗光」,因此程式設計師通常希望系統給他無限量且無限快的記憶體。大部分的現代電腦記憶體架構都是階層式的,最快且數量最少的暫存器為首,然後是快取、記憶體以及最慢的磁碟儲存裝置。而操作系统的記憶體管理提供尋找可用的記憶空間、配置與釋放記憶空間以及交換記憶體和低速儲存裝置的內含物……等功能。此類又被稱做虛擬記憶體管理的功能大幅增加每個行程可獲得的記憶空間(通常是4GB,即使實際上RAM的數量遠少於這數目)。然而這也帶來了微幅降低執行效率的缺點,嚴重時甚至也會導致行程崩潰。 記憶體管理的另一個重點活動就是藉由CPU的幫助來管理虛擬位置。" +443,"、FAT16、FAT32、EXFAT與NTFS。NTFS系統是Windows上最可靠與最有效率的檔案系統。其他的FAT家族都比NTFS老舊,且對於檔案長度與分割磁碟能力都有很大限制,因此造成很多問題。而UNIX的檔案系統多半是UFS,而UNIX中的一個分支Solaris最近則開始支援一種新式的ZFS。 大部份上述的檔案系統都有兩種建置方法。系統可以以日誌式或非日誌式建置。日誌式檔案系統可以以較安全的手法執行系統恢复。如果一個沒有日誌式建置的檔案系統遇上突然的系統崩潰,導致資料建立在一半時停頓,則此系統需要特殊的檔案系統檢查工具才能復原;日誌式則可自動恢复。微软的NTFS與Linux的ext3、ext4、reiserFS與JFS都是日誌式檔案系統。 每個檔案系統都實作相似的目錄/子目錄架構,但在相似之下也有許多不同點。微軟使用「\」符號以建立目錄/子目錄關係,且檔案名稱忽略其大小寫差異;UNIX系統則是以「/」建立目錄架構,且檔案名稱大小寫有差異。(其實這是給系统调用的, / 或 \ 並不實際存在硬碟) === 网络 === 許多現代的操作系统都具備操作主流網路通訊協定TCP/IP的能力" +444,"作系统會給程式一個識別代號,並且在此程式發出請求時,檢查其代號與所需資源的存取權限關係。 ** 外部來源:從非本地端電腦而來的資源請求,例如遠端登入本機電腦或某些網路連線請求(FTP或HTTP)。為了識別這些外部請求,系統也許會對此請求提出認證要求。通常是請求輸入使用者名稱以及相對應的密碼。系統有時也會應用諸如磁卡或生物識別資料的它種認證方法。在某些例子,例如網路通訊上,通常不需通過認證即可存取資源(例如匿名存取的FTP伺服器或P2P服務)。 除了允許/拒絕形式的安全機制,一個高安全等級的系統也會提供記錄選項,允許記錄各種請求對資源存取的行為(例如「誰曾經讀了這個檔案?」)。 肇因於軍方與商業組織將敏感資料記錄在電腦上,安全機制在操作系统歷史上是一個被長久關注與討論的問題。美國國防部(DoD)便創立了《可信賴之計算機系統評鑑程序》(),此手冊確立了評鑑安全機制成效的基本原則。這對操作系统作者來說非常重要,因為TCSEC是用於評鑑、分類與選拔出用於處理、儲存與獲取敏感或機密資料的電腦系統的標準程序。 ==== 內部通讯安全 ==== 內部資訊安全可視為防止正在執行的程式任意存取系統資源的" +445,"務通常藉由端口或操作系统網路地址後的數字存取點提供。通常此服務包括提供檔案共享(NFS)、列印共享、電子郵件、網頁服務與檔案傳輸協定(FTP)。外部資訊安全的最前線,是諸如防火墙等的硬體裝置。在操作系统內部也常設置許多種類的軟體防火牆。軟體防火牆可設定接受或拒絕在操作系统上執行的服務與外界的連線。因此任何人都可以安裝並執行某些不安全的網路服務,例如Telnet或FTP,並且設定除了某些自用通道之外阻擋其他所有連線,以達成防堵不良連線的機制。 === 使用者界面 === 今日大部分的操作系统都包含图形用户界面(GUI)。有幾類較舊的操作系统將圖形化使用者介面與核心緊密結合,例如最早的Windows與Mac OS實作產品。此種手法可提供較快速的圖形回應能力,且實作時不需切割模組因而較為省工,但是會有強烈副作用,例如圖形系統崩潰將導致整個系統崩潰,例如藍白當機畫面。許多近代的操作系统已模組化,將圖形介面的子系統與核心分開(已知Linux與Mac OS X原先就是如此設計,而某些擴充版本的Windows終於也採用此手法)。 許多操作系统允許使用者安裝或創造任何他們喜歡的图形用户界面各項多采多姿" +446,")時所需的中斷處理常式。 設計驅動程式的主要目的在於操作抽象化,任何硬體模組,即使是同一類的裝置,在硬體設計面上也有巨大差異。廠商推出的較新模組通常更可靠更有效率,控制方法也會有所不同。電腦與其操作系统每每不能預期那些現有與新裝置的變異之處,因此無法知道其操作方法。為解決此問題操作系统通常會主動制訂每種裝置該有的操作方式,而驅動程式功能則是將那些操作系统制訂的行為描述,轉譯為可讓裝置瞭解的自訂操作手法。 理論上適合的驅動程式一旦安裝,相對應的新裝置就可以無誤地執行。此新驅動程式可以讓此裝置完美地切合在操作系统中,讓使用者察覺不到這是操作系统原本沒有的功能。 == 结构 == 操作系统理论研究者有时把操作系统分成四大部分: * 驱动程序 - 最底层的、直接控制和监视各类硬件的部分,它们的职责是隐藏硬件的具体细节,并向其他部分提供一个抽象的、通用的接口。 * 内核 - 操作系统之最核心部分,通常运行在最高特权级,负责提供基础性、结构性的功能。 * 支承库 - (亦作“接口库”)是一系列特殊的程序库,它们职责在于把系统所提供的基本服务包装成应用程序所能够使用的编程接口(API),是最靠近应用" +447,"心最基础的构件,其结构往往对操作系统的外部特性以及应用领域有着一定程度的影响。尽管随着理论和实践的不断演进,操作系统高层特性与内核结构之间的耦合有日趋缩小之势,但习惯上,内核结构仍然是操作系统分类之常用标准。 内核的结构可以分为单内核、微内核、超微内核、以及外核等。 单内核结构是操作系统中各核心部件杂然混居的形态,该结构产生于1960年代(亦有1950年代初之说,尚存争议),历史最长,是操作系统内核与外围分离时的最初形态。 微内核结构是1980年代产生出来的较新的内核结构,强调结构性部件与功能性部件的分离。20世纪末,基于微内核结构,理论界中又发展出了超微内核与外内核等多种结构。尽管自1980年代起,大部分理论研究都集中在以微内核为首的“新兴”结构之上,然而,在应用领域之中,以单内核结构为基础的操作系统却一直占据着主导地位。 在众多常用操作系统之中,除了QNX和基于Mach的UNIX等个别系统外,几乎全部采用单内核结构,例如大部分的Unix、Linux,以及Windows(微软声称Windows NT是基于改良的微内核架构的,尽管理论界对此存有异议)。微内核和超微内核结构主要用于研究性" +448,"-{zh-hans:实时;zh-hant:即時;}-性”所指的通常是特定操作所消耗的时间(以及空间)的上限是可预知的。比如,如果说某个操作系统提供实时内存分配操作,那也就是说一个内存分配操作所用时间(及空间)无论如何也不会超出操作系统所承诺的上限。-{zh-hans:实时;zh-hant:即時;}-性在某些领域非常重要,比如在工业控制、医疗器材、影音频合成、以及军事领域,-{zh-hans:实时;zh-hant:即時;}-性都是无可或缺的特性。 常用-{zh-hans:实时;zh-hant:即時;}-操作系统有QNX、VxWorks、RTLinux等等,而Linux、多数UNIX、以及多数Windows家族成员等都属于非实时操作系统。操作系统整体的-{zh-hans:实时;zh-hant:即時;}-性通常依仗内核的-{zh-hans:实时;zh-hant:即時;}-能力,但有时也可在非-{zh-hans:实时;zh-hant:即時;}-内核上建立-{zh-hans:实时;zh-hant:即時;}-操作系统,很多在Windows上建立的-{zh-hans:实时;zh-hant:即時;}-" +449,"用「段」加「線性地址」的二維平面地址空間的設計。分配記憶體時通常需要考慮「段置換」的問題,同時,應用程序所能夠使用的地址空間也往往有比較小的上限。 在32位作業系統中,4GB的定址範圍對於一般應用程序來說是綽綽有餘的,因而,通常使用一維的線性地址空間,而不使用「段」。 == 注释 == == 参考文献 == === 引用 === === 来源 === * * (俗稱“恐龍書”,OS教科書“聖經”) * (Linux由此書而啟發,可惜新版本將實作內容移除) * == 参閱 == * 計算機 * 操作系统内核 * 实时操作系统-分时系统-多任务-嵌入式系统-单一用户-多用户 * 对称多处理机(SMP)-集群(Cluster)-分布式计算 * 64位操作系统 * 操作系统列表 == 外部連結 == * OS的历史 * 简略OS介绍的教育网站.... * OSDP,所有作业系統的文件网站 * Unix OS家族的歷史樹 * Windows的歷史 * TLDP,Linux核心徹底講解,適用於想要徹底了解OS核心結構者 * 作業系統原理介紹 OS" +450,"此列表列出了早期至今的所有操作系统名称。 ==早期操作系统(专利保护)== *TRS-DOS,ROM OS's *TI99-4 *Commodore PET,64,和 VIC-20, *第一套IBM-PC *System v1.0~7.6.1,Mac OS 7.6~9.2.2(苹果电脑) *Sinclair Micro和QnX等 ==商业操作系统(非Unix)== *CP/M *CP/M-86 *MP/M-80 *UCSD P-system *Mini-FLEX *SSB-DOS *DR-DOS *FreeDOS *MS-DOS *PC-DOS *Mach 由卡纳尼基梅隆大学研究 *L4微内核 第二代微内核 *CHORUS *Choices *Multics *OS-9 *NSJ *Netware:一种网络服务器操作系统 ==Unix及类似系统== *Unix System V *Xenix-微软 *OS/2-IBM 微软 *SCO UNIX-SCO *IRIX-SGI *iOS-Apple *A/UX-Apple->Mac OS X->macOS *HP-UX-HP *AIX-IBM" +451,"*MI-SOM1.1 **MI-SOM1.2 ===Acorn=== *Arthur *ARX *RISC OS *RISCiX ===Amiga=== *AmigaOS ===Atari ST=== *TOS *MultiTOS *MiNT ===苹果电脑(Apple/Macintosh)=== *Apple DOS *Apple ProDOS *Apple SOS *Apple GS/OS *A/UX *Classic Mac OS *Mac OS系列,2011年及之前稱Mac OS X,2012年至今為macOS。2007年發布的Mac OS X 10.5 Leopard已經通過了The Open Group的UNIX03認證。 ===Be=== *BeOS *BeIA *Haiku ===Digital/康柏(Compaq)=== *AIS *OS-8 *RSTS/E *RSX-11 *RT-11 *TOPS-10 *TOPS-20 *VMS(后更名为OpenVMS) ===IBM=== *OS/2 *AIX *OS/400 *OS/390 *VM/CMS *DOS/VSE *" +452,"2008 **Windows 7 (NT 6.1) **Windows Server 2008 R2 **Windows 8 (NT 6.2) **Windows Server 2012 **Windows 8.1 (NT 6.3) **Windows Server 2012 R2 **Windows 10 (NT 10.0) **Windows Server 2016 **Windows Server 2019 **Windows 11 (NT 10.0) ===Novell=== *NetWare *Unixware *SUSE Linux ===NeXT=== *NEXTSTEP(即之后的Mac OS X) *九號計畫 *Inferno ===Prime Computer=== *Primos ===西门子=== *BS2000 - 用於西門子公司的大型主機。 *SINIX(也稱Reliant UNIX) - 用於西門子公司的UNIX電腦系統。 ===個人數位助理(PDA)操作系统=== *Palm OS *Pocket PC *EPOC *Microsoft Windows C" +453,"Linux是一種自由和開放源碼的類UNIX作業系統。该操作系统的内核由林纳斯·托瓦兹在1991年10月5日首次发布,再加上使用者空間的應用程式之後,就成為了Linux作業系統。Linux也是自由软件和开放源代码软件发展中最著名的例子。只要遵循GNU 通用公共许可证(GPL),任何个人和机构都可以自由地使用Linux的所有底层源代码,也可以自由地修改和再发布。大多數Linux系統還包括像提供GUI的X Window之類的程序。除了一部分專家之外,大多數人都是直接使用Linux 發行版,而不是自己選擇每一樣組件或自行設置。 Linux嚴格來說是單指作業系統的内核,因作業系統中包含了許多用戶圖形介面和其他实用工具。如今Linux常用来指基于Linux的完整操作系统,內核則改以Linux内核稱之。由于这些支持用户空间的系统工具和库主要由理查德·斯托曼于1983年发起的GNU计划提供,自由软件基金会提议将其组合系统命名为GNU/Linux,但Linux不屬於GNU計劃,這個名稱並沒有得到社群的一致認同。 Linux最初是作为支持英特尔x86架构的个人电脑的一个自由操作系统。目前Linux已经被移" +454,"都可以创建一个符合自己需求的Linux发行版。 == 歷史 == === UNIX渊源 === UNIX操作系统(),是美国AT&T公司贝尔实验室于1969年完成的操作系统。最早由肯·湯普遜(Ken Thompson),丹尼斯·里奇(Dennis Ritchie),道格拉斯·麥克羅伊(Douglas McIlroy),和喬伊·歐桑納于1969年在AT&T贝尔实验室开发。于1971年首次发布,最初是完全用汇编语言编写。后来,在1973年用一个重要的开拓性的方法,Unix被丹尼斯·里奇用编程语言C(内核和I/O例外)重新编写。高级语言编写的操作系统具有更佳的兼容性,能更容易地移植到不同的计算机平台。 1983年,理查德·馬修·斯托曼創立GNU計劃。這個計劃有一個目標,是為了發展一個完全自由的類Unix作業系統。自1984年發起這個計劃以來,在1985年,理查德·馬修·斯托曼發起自由軟體基金會並且在1989年撰寫GPL。1990年代早期,GNU開始大量地產生或收集各種系統所必備的元件,像是——函式庫、編譯器、偵錯工具、文字編輯器,以及一個Unix的使用者介面(Unix shell)——但是像" +455,"原始碼可以自由使用,這對Linux的发展有益。使用GNU GPL協議的原始碼可以被其他項目所使用,只要這些項目使用同樣的協議發布。為了讓Linux可以在商業上使用,林納斯·托瓦茲決定更改他原來的協議(這個協議會限制商業使用),以GNU GPL協議來代替。之后许多開發者致力融合GNU元素到Linux中,做出一個有完整功能的、自由的作業系統。 ===命名=== Linux的第一個版本在1991年9月被大學FTP server管理員-{Ari Lemmke}-發佈在網際網路上,最初Torvalds稱這個核心的名稱為 Freax ,意思是自由( free )和奇異( freak )的結合字,並且附上 X 這個常用的字母,以配合所謂的類Unix的系統。但是FTP伺服器管理員嫌原來的命名「Freax」的名稱不好聽,把核心的稱呼改成「Linux」,當時僅有10000行程式碼,仍必須執行於Minix作業系統之上,並且必須使用硬碟開機;隨後在10月份第二個版本(0.02版)發佈,同時這位芬蘭赫爾辛基的大學生在comp.os.minix上發佈一則訊息 Hello everybody out there u" +456,"ootloader)克服這些缺點,具有『動態搜尋核心檔案』的功能,可以讓用户在開機的時候,自行編輯開機設定系統檔案,透過ext2或ext3檔案系統中載入Linux Kernel(GRUB通過不同的文件系統驅動可以識別幾乎所有Linux支援的文件系統,因此可以使用很多文件系統來格式化核心文件所在的磁區,並不侷限於ext文件系統)。 今天由Linus Torvalds带领下,众多开发人员共同参与开发和维护Linux内核。理查德·斯托曼领导的自由软件基金会,继续提供大量支持Linux内核的GNU组件。一些个人和企业开发的第三方的非GNU组件也提供对Linux内核的支持,这些第三方组件包括大量的作品,有内核模块和用户应用程序和库等内容。Linux社区或企业都推出一些重要的Linux发行版,包括Linux内核、GNU组件、非GNU组件,以及其他形式的软件包管理系统软件。 == 系统架构 == 基于Linux的系统是一个模块化的类Unix操作系统。Linux操作系统的大部分设计思想来源于20世纪70年代到80年代的Unix操作系统所建立的基本设计思想。Linux系统使用宏内核,由Linux内核负责" +457,"* 用户界面程序:如命令行Shell或窗口环境。 == Linux發行版 == Linux發行版指的就是通常所說的「Linux作業系統」,它一般是由一些組織、团体、公司或者個人制作并發行的。Linux内核主要作為Linux發行版的一部分而使用。通常來講,一個Linux發行版包括Linux核心,以及將整個軟體安裝到電腦上的一套安裝工具,还有各種GNU軟體,和其他的一些自由軟體,在一些Linux發行版中可能会包含一些專有軟體。發行版為許多不同的目的而製作,包括對不同電腦硬體結構的支援,对普通用户或开发者使用方式的调整,针对實時應用或嵌入式系統的开发等等。目前,超過三百個發行版被積極的開發,最普遍被使用的發行版有大約十多個。較為知名的有Debian、Ubuntu、Fedora、Red Hat Enterprise Linux、Arch Linux和openSUSE等。 一個典型的發行版包括:Linux核心,GNU函式庫和各种系统工具,命令行Shell,圖形界面底层的X Window系統和上层的桌面環境等。桌面环境有如KDE或GNOME等,並包含數千種從辦公套件,編譯器,文字編輯器,小遊戲,兒" +458,"Mozilla Firefox、Apache OpenOffice、Pidgin、VLC、GIMP、Blender;部分流行的专有软件也有相應的Linux版本,如Adobe Flash Player、Adobe Reader、Google Earth、Nero Burning ROM、Opera、Maple、MATLAB、Skype、Maya、SPSS、Google Chrome。 目前仍有較多能在Windows或Mac OS上執行的應用軟體沒有Linux的版本,不過在Linux平台上通常可以找到類似功能的應用軟件。Linux使用的增多也使得Windows開源軟體(Open Source)向Linux移植,比如Filezilla。Linux下也有相當多不能在Windows平台下執行的軟體,主要是依靠X Window系統和其他Windows無法利用的資源,或者是因為穩定性等其他方面的考慮並不準備支援Windows。 另外,相當多的Windows應用程式可以通過Wine和一些基於Wine的項目如CrossOver正常運行和工作。如Microsoft Office、Adobe Photosh" +459,"樣在上网本市場很受歡迎,像ASUS Eee PC和Acer Aspire One,銷售時安裝有訂製的Linux發行版。 ===服务器、主机和超级计算机=== Linux发行版一直被用来作为服务器的操作系统,并且已经在该领域中占据重要地位。根据2006年9月的报告显示,十个最大型的网络托管公司有八个公司基于Linux发行版运行其Web服务器。 Linux发行版是构成LAMP(Linux,Apache,MySQL,Perl / PHP / Python)的重要部分,现在更为常见的是 LNMP(N指Nginx);极低的硬件需求使 LAMP/LNMP 成为一个常见的网站托管平台,在开发者中普遍使用。对应Windows则有XAMPP。 由于其Linux核心对非统一内存访问(NUMA)的原生支持,Linux发行版也经常被作为超级计算机的操作系统。2010年11月公布的超级计算机前500强,有459个(91.8%)运行Linux发行版。曾经是世界上最强大的超级计算机——IBM的红杉(IBM Sequoia),已於2011年交付勞倫斯利福摩爾國家實驗室,並於2012年6月开始运作,也是选择Linux作" +460,"系统Tizen。Jolla Mobile公司成立并推出了由MeeGo发展而来的Sailfish操作系统。 Firefox OS 为Mozilla于2012年推出基於Linux核心的操作系统。 === 嵌入式设备 === Linux的低成本、强大的定制功能以及良好的移植性能,使得Linux在嵌入式系统方面也得到广泛应用。流行的TiVo数字视频录像机还采用了定制的Linux,思科在网络防火墙和路由器也使用了定制的Linux。Korg OASYS、Korg的KRONOS、雅马哈的YAMAHA MOTIF XS/Motif XF音乐工作站、雅马哈的S90XS/S70XS、雅马哈MOX6/MOX8次合成器、雅马哈MOTIF-RACK XS音源模块,以及Roland RD-700GX数码钢琴均运行Linux。Linux也用于舞台灯光控制系统,如WholeHogIII控制台。 === 政府應用 === Linux發行版在各地和各國政府中日益受到歡迎。有消息指出,俄羅斯軍方正在創建自己的Linux發行版,並已有G.H.ost項目作為成果。印度Kerala州甚至已經規定所有州立高中的電腦都必須運行Lin" +461,",比较缺乏對Linux的支援,硬體和應用程式皆只考量微軟作業系統設計的需求。例如:Internet Explorer及ActiveX需求、Microsoft Office相容性、網絡遊戲以及一般用戶都傾向於使用Windows。轉換作業系統的開銷在於,自由開源意識形態與商業用途的衝突、缺乏強而有力的推廣廠商、缺乏對特殊的硬件和應用程序的支援、電腦技術人員不願再花費時間重覆學習、對已有平台的依賴,是制約Linux被採納的主要因素。 然而隨著Linux慢慢開始流行,有些原始設備製造商(OEM)開始在其銷售的電腦上預裝上Linux,Linux的用戶中也有了普通電腦用戶,Linux系統也開始慢慢出現在個人電腦作業系統市場。儘管目前大部分的筆記型電腦搭載Windows系統,目前仍有一些筆電預設搭載Linux系統,如Musicha 音創筆電預設搭載Ubuntu Mate。近年來,Linux下常见的部份软件也开始不斷向Windows移植。有如KDE SC、Cinepaint正在進行向Windows的移植。 很多開源軟體的定量研究聚焦在市場佔有率(雖然多數不算作市場中的商品)和可靠性,包括數不清的Lin" +462,"子一台筆記型電腦這一項目正在催生新的更為龐大的Linux用戶群,計劃將包括發展中國家的幾億學童、他們的家庭和社區。在2007年,已經有六個國家訂購了至少每個國家一百萬台以上免費發放給學生。Google、Red Hat和eBay是該項目的主要支持者。 ==版权,商标和命名== Linux的註冊商標是Linus Torvalds所有的。這是由於在1996年,一個名字叫做William R. Della Croce的律師開始向各個Linux發佈商發信,聲明他擁有Linux商標的所有權,並且要求各個發佈商支付版稅,這些發行商集體進行上訴,要求將該註冊商標重新分配給Linus Torvalds。Linus Torvalds一再聲明Linux是自由且免費的,他本人可以賣掉,但Linux絕不能賣。 “GNU/Linux”此名稱是GNU计划的支持者与开发者,特别是其创立者理查德·斯托曼對於Linux作業系統的主張。由于此类操作系统使用了众多GNU程序,包含Bash(Shell程式)、函式庫、編譯器等等作為Linux核心上的系統套件,理查德·斯托曼认为应该将该操作系统称为「GNU/Linux」或「GNU" +463,"U計劃軟體之外,很多Linux還採用了X视窗系统。 == 評價 == === 正面 === * 開放原始碼的Linux可以讓知識延續下去,新興的軟體公司可以從開放原始碼上快速、低價的建立專業能力,豐富市場的競爭,防止獨霸軟體巨獸的存在。 * 個人使用很少有版權問題,絕大多數都是免費使用,幾乎無所謂盜版問題。 * 新的Linux發行版大多數軟體都有伺服器的服務,只要點選就可以自動下載、安裝經過認證的軟體,不需要到市面購買、安裝。 * Linux學習的投資有效時間較長。舊版軟體、系統都還是存在,有原始碼可以衍生、分支,維護週期普遍比Windows長很多。就算被放棄,還是可以憑藉原始碼衍生。新的軟體更新發展多樣化,容易養成使用者習慣掌握原理,而不是養成操作習慣。 * 強大的Shell及脚本支援,容易組合出符合需求的環境或創造自動程序。 * 預設安全設定相對於目前主流的Windows作業系統安全很多。Windows作業系統為了非專業使用者降低了預設安全性的設定,導致系統容易受到木馬、病毒的侵害。盜版的Windows更糟糕,可能隨盜版作業系統捆绑木馬、惡意程式,部分預設超級使用者(Adminis" +464,"he Linux文档项目 * Linux25 週年:專訪 Linux 之父 Linus Torvalds Category:计算平台 Category:自由作業系統 Category:LAMP" +465,"GNU计划(),又譯為革奴计划,是一個自由軟體集體協作计划,1983年9月27日由理查德·斯托曼在麻省理工學院公开发起。它的目標是创建一套完全自由的操作系统,稱為GNU。理查德·斯托曼最早在net.unix-wizards新闻组上公布该消息,并附带一份《GNU宣言》等解释为何发起该计划的文章,其中一个理由就是要“重现当年软件界合作互助的团结精神”。 GNU是“GNU is Not Unix”的递归缩写。為避免与单词gnu(非洲牛羚,发音与「new」相同)混淆,斯托曼宣布GNU发音应为「Guh-NOO」(),与「canoe」发音相似。其中,Emacs就是由这个计划孵化而出。 UNIX是一种广泛使用的商业操作系统的名称。由于GNU将要实现UNIX系统的接口标准,因此GNU计划可以分别开发不同的操作系统。GNU计划采用了部分当时已经可自由使用的软件,例如TeX排版系统和X Window视窗系统等。不过GNU计划也开发了大批其他的自由软件,这些软件也被移植到其他操作系统平台上,例如Microsoft Windows、BSD家族、Solaris及Mac OS。 为保证GNU软件可以自由地“使用、" +466,"与其他GNU软件结合,完全自由的操作系统正式诞生。许多程序员参与了Linux的开发与修改,也經常將Linux當成開發GNU計劃軟體的平台。该操作系统往往被称为“GNU/Linux”或简称Linux。但Linux本身不屬於GNU計劃的一部份,GNU计划自己的内核Hurd依然在开发中,目前已经发布Beta版本。 许多UNIX系统上也安装了GNU软件,因为GNU软件的质量比之前UNIX的软件还要好。GNU工具还被广泛地移植到Windows和Mac OS上。 GNU工程十几年以来已经成为一个对软件开发主要的影响力量,创造了无数的重要的工具,例如:强健的编译器,有力的文本编辑器,甚至一个全功能的操作系统。这个工程是从1984年麻省理工学院的程序员理查德·斯托曼的想法得来的,他想要建立一个自由的、和UNIX类似的操作环境。从那时开始,许多程序员聚集起来开始开发一个自由的、高质量、易理解的软件。 == 宣言 == == 发言人 == 以下是GNU计划的正式发言人: *Robert J. Chassell *Loïc Dachary * *Georg Greve *Federico Heinz *Br" +467,"自由軟件運動(或,簡稱FSM或FOSSM)是一個推廣用戶有使用、複製、研究、修改和分發軟件等權利的社會運動理查德·斯托曼对于自由软件运动本质的看法 2008在emacs-devel的邮件列表.。接近和相關的運動包括開放源碼運動及自由軟件的開放源碼運動。這運動跟1970年代的黑客文化有淵源,而理查德·斯托曼是該運動的主要發起人以及精神領袖。 自由軟件運動人士認為自由軟件的精神應當貫徹到所有軟件:他們認為禁止計算機用戶行使這種自由是不道德的行為。理察·馬修·斯托曼認為販賣不附帶源代碼的二進制軟件是不道德的,因為這樣阻止了軟件用戶學習以及幫助其他人的權利。然而目前還沒有如何實現自由軟件運動最終目標的共識。有些人認為應當使用法律手段強制軟件供應商提供源代碼;有些則認為應當通過抵制專有軟件來達到目的。還有一些人則認為時間將證明,自由軟件最終在質量上要比專有軟件略勝一籌,並會在自由市場上取得勝利。 部分自由軟件運動人士也認為其他受專利權保護的產品也應當自由化。還有一些人士則認為有部分資源不應當被自由更改,例如GNU通用公共協議證書,小說等。 ==理念== 這個運動的理念在於電腦的使用不應該阻礙人際" +468,"l West解釋了許多自由原始碼運動的理念。如果假設科技不僅對社會變遷造成影響,並從科技進步主導社會變遷的角度來看待,禁止特定人員使用這些科技是道德的嗎?因此—縱使不能造成立刻的改變—自由軟體運動還是希望喚醒人們這個運動所能造成的影響,因為能造成對現實社會的影響。舉例來說:與沒有電腦時相比,電腦給了我們更多自由,但這些科技媒介究竟是帶來自由、或僅賦予少數人特權?這樣的道德爭論如何取的一個折衷的辦法對自由軟體運動是個難題。 自由軟體基金會堅信所有的軟體需要說明文件,特別是周到的工程師應做到能更新說明書以反映他們對軟體的調整,但他們認為對修改其他著作的自由本身是相對次要的。在自由軟體運動中基金會特別強調提供此類文件的目標。自由軟體運動的成員也倡議具實際用途的軟體應該自由的。 == 知名自由軟件 == * GNU Hurd * Mastodon * Linux * PHP * MySQL * Notepad++ * Firefox * Thunderbird == 行動 == === 編寫及推廣自由软件 === 自由軟件運動的核心工作專注於軟件開發。自由軟件運動也反對專有軟件,拒絕安裝不給予" +469,"注於政府機構和政府資助的項目中所使用的自由軟件。 委內瑞拉政府在2006年1月實施了保证軟件自由的法律。法令第3,390條,授權所有政府機構可以將完成超過兩年的軟件,編列為自由軟件。 國會議員和雅克·羅吉克·阿克曼在引進祕魯法案1609“公共管理自由軟件”的過程中,出力極大。而這一事件立刻引起微軟公司祕魯分部的注意,而其總經理隨後寫了一封信給埃德加·戴維斯·維拉紐瓦博士。維拉紐瓦博士的反應受到了全世界的關注,此後對於自由軟件的論證,更是被視為經典。 在美國,也已經有了在州政府於州級對促進使用自由軟件通過立法的努力。 == 發展中採取措施 == 。 一種成立於2004年,發起於2006年的Web服務。用於監視在自由軟件社區的軟件開發活動,對項目和編程語言的發展和普及提供詳細的軟體度量和定量分析。 。監視很多FOSS軟件倉庫和分析各種FOSS軟件許可證的普及。 == 参考文献 == == 外部連結 == * 自由軟體的定義 * Free Software Movement intro by FSF * 自由軟體的故事 == 參見 == * 理查德·斯托曼 * 自由軟件 * 自由軟體鑄造場" +470,"-{T|zh-hans:材料科学; zh-hant:材料科學; zh-sg:物质科学}- 材料科學(),涉及物質的性質及其在各個科學和工程學領域的整合應用,是一個研究材料的製備或加工工藝、材料的微觀結構與材料宏觀性能三者之間的相互關係的跨領域學科。涉及的理論包括固體物理學、材料化學、應用物理和應用化學,以及化學工程、機械工程、電機工程、電子工程、土木工程和建築工程。與機械結合則衍生出機械材料,與電子結合則衍生出電子材料,與土木建築結合則衍生出結構材料,與生物學結合則衍生出生物材料等等。隨著近年來媒體將注意力大量集中在纳米科學上,材料科學在科學與工程學領域越來越廣為人知。它也是鑑識科學和破壞分析中的一個重要組成部分,以後者為例,它是分析各種飛航意外的關鍵。今日許多科技上的問題受限於材料能夠容許的極限,也因此,在此領域的突破在未來科技具有指標性的影響。材料科學有著廣泛的應用前景。 == 簡史 == 在各時代上材料的選擇往往決定了該時代的發展,像是石器時代、青銅器時代、鐵器時代和工業革命就是明顯的例子。材料科學是最古老的應用科學及工程學之一,起初被引導來自陶瓷的加工和冶金學的延伸。現代材料科" +471,"料分类U.S. Department of Energy, Pacific Northwest National Laboratory, Materials Science and Technology Teachers Handbook, vol 2, pp.19-20, 2008 ** 金屬材料 ** 陶瓷與玻璃材料 ** 高分子材料 ** 複合材料 **半導體材料 * 按化學狀態分類 ** 金屬材料 ** 無極非金屬材料 *** 陶瓷材料 ** 有機材料 *** 高分子材料 * 按物理性質分類 ** 高強度材料 ** 耐高温材料 ** 超硬材料 ** 導電材料 ** 絕緣材料 ** 磁性材料 ** 透光材料 ** 半導體材料 * 按狀態分類 ** 單晶材料 ** 多晶質材料 ** 非晶態材料 ** 准晶態材料 * 按物理效應分類 ** 鐵電材料 ** 壓電材料 ** 熱電材料 ** 光電材料 ** 電光材料 ** 磁光材料 ** 聲光材料 ** 電磁材料 ** 雷射材料 * 按用途分類 ** 土木材料 ** 建築材料 ** 結構材料 ** 研磨材料 ** 耐火材料 ** 耐酸材料" +472,"诺贝爾奖(,,),是根据瑞典化学家阿尔弗雷德·诺贝尔遗嘱於1901年開始每年頒發的5個奖项,包括:物理、化学、生理学或医学、文学、和平。1968年,瑞典中央银行設立了瑞典中央銀行紀念阿爾弗雷德·諾貝爾經濟學獎,通称“诺贝尔经济学奖”。诺贝尔奖普遍被认为是所颁奖领域内最有声望的奖项Shalev, p. 8。 == 由来 == 阿佛烈·諾貝爾(瑞典語:Alfred Nobel,1833年10月22日-1896年12月10日)是一位瑞典化學家,同時也是發明家。19世紀末期,歐洲大陸每天有三宗爆炸案,但諾貝爾持續改進炸藥使得炸彈更有破壞力,吸引軍火商的青睞,他的改良矽藻土炸藥取得了众多的科研成果,也成功設立許多工廠生產,大量的軍火被運送到戰場上,靠軍工製造累積了巨大財富。 诺贝尔終生未婚,亦無子嗣。在其逝世前,亲兄弟也早一步去世。由於諾貝爾終生主張和平主義,也因此他對於自己改良的炸藥作為破壞及戰爭的用途始終感到痛心。在即将辞世之际,诺贝尔立下了遗嘱:“请将我的财产变做基金,每年用这个基金的利息作为奖金,奖励那些在前一年为人类做出卓越贡献的人。”根据他的这个遗嘱,从1901年开始,具有国际性的诺" +473,"」。 諾貝爾物理學獎得主列表 化学獎 瑞典皇家科学院负责颁发,始於1901年,每年12月10日举行颁奖典礼。表彰「在化學領域作出最重要發現或發明的人」。 諾貝爾化學獎得主列表 生理学或医学獎 瑞典卡罗琳医学院负责颁发,始於1901年,每年12月10日举行颁奖典礼。表彰「在生理學或醫學領域作出最重要發現或發明的人」。 諾貝爾生理學或醫學獎得主列表 文学獎 瑞典学院负责颁发,始於1901年,每年12月10日举行颁奖典礼。表彰「在文學領域創作出具有理想傾向之最佳作品的人」。 諾貝爾文學獎得主列表 和平獎 挪威议会的诺贝尔委员会负责颁发,始於1901年,每年12月10日举行颁奖典礼。表彰「為促進民族國家團結友好、取消或裁減軍備以及為的組織和宣傳盡到最大努力或作出最大貢獻的人」。 諾貝爾和平獎得主列表 經濟學獎 瑞典皇家科学院负责颁发,始於1968年,由瑞典銀行出資增設,每年12月10日举行颁奖典礼。原稱「瑞典銀行紀念諾貝爾經濟學獎」,通稱「諾貝爾經濟學獎」或「諾貝爾紀念獎」。表彰“在经济学领域作出杰出贡献的人”。 此後,諾貝爾基金會不再增設任何獎項。 諾貝爾經濟學獎得主列表 == 統計數據 =" +474,"(1.28%) 0 (0%) 首5種獎項总计 833 48 (5.76%) 12 (1.44%) 全部6種獎項總计 911 49 (5.37%) 12 (1.31%) === 世界各国诺贝尔奖人数=== 根據獲獎時國籍與出生地,截至2016年10月14日,各國各獎項得主人數的世界前5強: * 诺贝尔物理学奖 # 93 # 26 # 23 # 13 ; 13 # 11 * 诺贝尔化学奖Selon la liste des lauréats sur le site des Nobel # 69 # 27 # 26 # 8 # 7 ; 7 * 诺贝尔生理学或医学奖Selon la liste des lauréats sur le site des Nobel # 93 # 29 # 20 # 13 # 7 * 诺贝尔文学奖 # 15 # 12 # 10 # 8 ; 8 # 6 ; 6 * 诺贝尔和平奖 # 23 # 13 # 11 # 8 ; 8 # 5 * 瑞典銀行經濟學獎 # 45 # 7 # 4 # 3 ; 3; 3 # 2 ; 2 ; 2 === 世界各大学诺贝尔奖人数 === 下" +475,"14 23 5 3 12 康奈尔大学 61 50 23 12 15 5 4 2 13 柏林洪堡大學 57 49 14 23 12 1 4 3 14 巴黎大学 51 35 15 10*** 10 4 6 7 15 哥廷根大学 45 43 19 16 8 0 1 1 16 慕尼黑大学 43 42 14 19 9 0 1 1** 17 约翰斯·霍普金斯大学 39 30 4 8 18 5 1 3 17 哥本哈根大学 39 34 19 7 8 3 2 1** 19 纽约大学 38 20 3 5 12 14 2 2 19 洛克菲勒大學 38 38 1 11 26 0 0 0 21 宾夕法尼亚大学 36 25 4 10 11 11 0 0 22 伦敦大学学院 34 31 5 7 19 2 1 0 23 苏黎世联邦理工学院 32 32 11 17 4 0 0 0 24 伊利诺伊大学厄巴纳-香槟分校 30 27 11 5 11 3 0 0 24 明尼苏达大学 30 15 7 4 4 12 2 1 26 加州大學聖地牙哥分校 27 24 5 9 10 3 0 1** 26 海德堡大学 27 24 11 8" +476,"立超导BCS理论 * 英国科学家弗雷德里克·桑格 **1958年化學獎:测定胰岛素分子的结构 **1980年化學獎:核酸DNA序列的确定方法 * 美国科学家卡尔·巴里·沙普利斯 **2001年化學獎:对手性催化氧化反应的研究 **2022年化學獎:在点击化学和生物正交化学方面的发展做出的贡献 組織: * 國際組織紅十字國際委員會 ** 1917年和平獎:竭盡全力保護第一次世界大戰各方戰俘的權利,其中還包括讓戰俘與家人建立聯繫的權利。 ** 1944年和平獎:表彰該組織在第二次世界大戰期間的大量人道主義工作。 ** 1963年和平獎:表彰該組織100年來在保障人權上所做的努力。(和紅十字會聯盟共同獲獎) * 聯合國難民事務高級專員辦事處 ** 1954年和平獎 ** 1981年和平獎 === 家族 === ;居-{里}-一家 : 玛丽·居里,1903年物理学奖、1911年化学奖。 : 丈夫皮埃尔·居里,1903年物理学奖。 : 大女儿伊雷娜·约里奥-居里,1935年化学奖。 :: 丈夫弗雷德里克·约里奥-居里,1935年化学奖。 : 小女儿艾芙·居里著有《居里夫人传》,她本人不是科学家也" +477,"學獎和2022年生理学或医学獎分別是蘇恩·伯格斯特龍與斯万特·佩博。 ;夫妻都獲獎 : 玛丽·居里與皮埃尔·居里,1903年物理学奖。 : 伊伦·约里奥-居里與弗雷德里克·约里奥-居里,1935年化学奖。 : 格蒂·特蕾莎·科里與卡尔·斐迪南·科里,1947年生理學或醫學獎。 : 贡纳尔·默达尔,1974年經濟學獎,與阿尔瓦·默达尔,1982年和平獎。 : 迈-布里特·莫泽與爱德华·莫泽,2014年生理學或醫學獎。 : 阿巴希·巴納吉與艾絲特·杜芙若,2019年經濟學獎。 === 年齡、群體 === ;最年轻、最年长的得主 : 马拉拉·优素福扎伊为最年轻的诺贝尔奖得主。2014年(与凯拉西·萨塔亚提一同)获得诺贝尔和平奖时年仅17岁。 : 威廉·劳伦斯·布拉格为诺贝尔奖三项科学奖项中最年轻的得主。1915年(与其父威廉·亨利·布拉格一同)获得诺贝尔物理学奖时年仅25岁。 :约翰·B·古迪纳夫获2019年諾貝爾化學獎时已有97岁,为最年長的诺貝尔奖得主。 ;参会者成为诺贝尔奖得主最多的会议 :1927年10月召开的第五次索尔维会议。参加会议的29人中有17人获得或后来获得诺贝尔奖。 ==" +478,"= 人们对诺贝尔奖的争议点,除了在各獎得主必须是三人以下的限制外,還有不颁给已过世的人,另外还有诺贝尔奖不设立重要的数学奖。 1919年的诺贝尔物理学奖得主约翰尼斯·斯塔克曾为自身利益为希特勒效劳,诺贝尔基金会无权使获奖者改变自己的看法。 诺贝尔奖的规矩:得主必须是三人以下,也产生过争议,该奖项只是表彰在科学领域中有重大突破的个人和小团队的。不过也有例外的是诺贝尔奖中的和平奖,分别在1917年,1944年,1963年颁发给了红十字国际委员会组织,2012年颁发给了歐洲聯盟组织。2013年则颁发给了禁止化学武器组织,2015年又頒發給突尼西亞全國對話四方集團。 关于诺贝尔基金会不设立数学奖,其中有一个流传较广的都市傳說是诺贝尔的妻子与一位名叫雷富勒(Mittag-Leffler)的数学家有外遇,所以诺贝尔本人遺囑中不设置诺贝尔数学奖。不过这个说法显然不实,因为诺贝尔根本没有结婚,較可能的因素為當時代,數學僅為尚未形成一有改變人類生活可能的顯學。 隨著数学及計算機领域的發展,數學界的阿贝尔奖及计算机界的图灵奖被称为該領域的“诺贝尔奖”。 在得獎人選方面,在眾多的爭議獎項中,歷年來文學獎與和" +479,"thumb|upright=1.5|法国奥勒湖附近山坡上的几株鸢尾花 植物学(英语:Botany)是一门研究植物形态解剖、生长发育、生理生态、系统进化、分类以及与人类的关系的综合性科学,是生物学的分支学科。传统上的植物学也包括真菌和藻类的研究,如今这两类研究被细分为了真菌学与藻类学,但国际植物学大会仍将它们包括在讨论范围内。如今,植物学家研究了大约410,000种陆生植物,其中391,000属于维管植物(其中包含369,000种被子植物),剩余的约20,000种为苔藓植物。 植物学起源于史前时代,人类在寻找药草的过程中逐渐鉴别出一些植物物种。而后人类也试图人工繁育特定植物,以供食用、提取毒素或医治疾病。这些努力也成为了人类科学研究的起始。欧洲中世纪僧院里的常会种植一些具备药用价值的植物。这些园林后续逐渐演变为大学附属的植物园,其为植物研究提供了便利。最早出现的植物园是位于意大利的帕多瓦植物园。早期对植物的分类与描述为后续植物分类学奠定了基础,并促使卡尔·林奈提出了二名法,如今二名法已被广泛使用于生物命名。19和20世纪出现了许多新的植物研究技术,例如光学显微镜、、电子显微镜、染色体倍性" +480,"英国生物学家约翰·雷确立了现代植物分类的基本原理。17世纪出现了各式各样的显微镜,开创了植物解剖学的研究,随后植物生理学和植物胚胎学也得到进一步发展,到19世纪中期,植物学各分支学科已基本形成。近代中国的植物学主要从西方引入,首部译本是数学家李善兰与外人于19世纪中叶合译的《植物学》,该书所译的细胞、心皮、子房、胎座、胚、胚乳等名词沿用至今。中国近代植物分类学的奠基人是胡先骕,他编写了中国首部植物学教材《高等植物学》,发现并命名了水杉。 ===早期植物学=== 植物学起源于人类对草药的研究与使用。人类早期文明有许多关于植物分类和描述的著作。例如可追溯至1100年前的古印度著作,古埃及的阿維斯陀語文本,中国的植物学早期著作可追溯到公元前221年。秦汉时期的《神农本草经》是中国最早的药用植物志。 现代植物学源于古希腊,创始人为亚里士多德的学生泰奧弗拉斯托斯,他发明并描述了许多现代植物学的原理。泰奧弗拉斯托斯的两部主要著作《植物史》和《植物本原》是欧洲古代和中世纪植物学的重要基础。公元1世纪希腊医师迪奥科里斯的著作《植物志》是一部共5卷的药草百科全书,对其后植物学亦有广泛影响。中世纪伊斯兰世" +481,"665年,博学家罗伯特·胡克凭借显微镜在软木切片中发现了并命名了细胞,不久后亦在活体植物组织中发现了细胞。 ===早期现代植物学=== 现代植物学始于对植物的分类研究。18世纪,植物分类系统开始使用对植物进行快速分类。在应用该方法时,科学家从预设的多种性状中选择与待分类植物最符合的性状,由此将其向下逐步归类为不同分类单元(例如科、属和种)。该方法的选择与顺序可以设计为只以分类为目的,或者也可以使其更加符合单道检索表(或称同步钥,)的自然或。在18世纪,得益于殖民地扩张,世界各地的新奇植物被采集回欧洲,这位植物学家的研究工作提供了诸多便利,他们无需到访世界各地便可研究许多异域植物。1753年,卡尔·林奈发表了植物分类学著作《植物种志》,该作品是现代植物学二名法的基础。根据林奈提出的植物命名方法,标准的植物学名应当由两个部分构成,第一部分是属名,第二部分标示属内的物种。为方便鉴定,林奈在其《性别系统》()中依据植物的生殖器官不同,将它们归为24个群组。例如第24组隐花植物(Cryptogamia)包括所有生殖器官为隐藏形态的苔藓、地衣、蕨类、藻类和真菌。 随着植物解剖学和植物形态学的发展和" +482,"。(1898–1997)在植物解剖学方面的工作为现代植物学奠定了重要基础,其半个世纪前的作品《植物解剖学与种子植物的解剖》()至今仍被用作植物结构生物学教材。 丹麦植物学家尤金纽斯·瓦尔明于19世纪晚期创建了植物生态学,他提出植物以群落方式存在。瓦尔明的导师和继任者对此提出了至今仍在使用的劳恩凯尔植物生活型分类系统。美国植物学家亨利·钱德勒·考尔斯、英国植物学家和美国植物生态学家发展了植物群落演替的概念,例如温带阔叶混交林的演替。克莱门茨还提出了顶级群落概念,它是对应环境所能支持的复杂程度最高的植物群。坦斯利将顶极群落的概念从生态学引入至生物学。19世纪德国植物学家卡尔·路德维希·韦尔登诺分析了种子散布与分布,植物种群,地质历史的影响之间的联系,并开创了植物地理学。俄国植物学家尼古拉·瓦维洛夫(1887–1943)在前人阿方斯·比拉姆·德康多尔的研究基础之上发展了生物地理学、起源中心和经济作物演化史的概念。 自1960年代以来,一些植物生理学原理逐渐被人们理解,例如蒸腾作用(水分在植物体内的移动),叶面水分蒸发速率与温度的依赖关系,水蒸气或二氧化碳透过气孔的分子扩散过程。借助这些研究成" +483,"连续的统一体,并强调这些结构的动态性。现代植物系统分类学主要关注植物间的。现代分子系统发生学通常忽视植物形态特征,而主要以植物的基因序列为数据来源。1998年,借助对开花植物的基因序列分析,被子植物种系发生学组发表了被子植物的系统发生学成果,其中回答了许多有关被子植物与其它物种之间的关系的问题。DNA条形码技术是当前植物学研究的活跃主题,该研究理论上可用于鉴定植物物种和商业品种。 近30多年来,分子生物学和近代技术科学,以及数学、物理学、化学的新概念和新技术被引入到植物学领域,植物学在微观和宏观层面的研究均取得了突破,无论在研究的深度还是广度上都达到了一个全新的水平。当今相当多的新知识都从研究模式植物而来,如拟南芥。这种十字花科的杂草是最早完成基因组测序的植物之一。此外水稻也是较早完成基因组测序的植物,因为水稻基因组相对较小,同时國際水稻基因定序工程()也把它定为重要的谷物/草/单子叶植物模型。另一禾本科植物二穗短柄草也是一个有助理解遗传学,细胞学和分子生物学的实验模型。科学家对小麦、 玉米、 大麦、 黑麦、御谷、大豆等主粮都进行了基因组测序。其中部分植物的基因组测序较为困难,因为它们" +484,"、地钱、角苔和藓类)。高等植物皆为多细胞真核生物,其共同祖先可进行光合作用。它们的生活史以单倍体、二倍体交替。处于单倍体阶段时称为配子体,在某个阶段会发展为二倍体,此时称为孢子体。种子植物的配子体由母体孢子体培育。过去植物学家也研究现代植物标准定义以外的生物,现在这些研究已被细分为其它学科,例如细菌(现在被细分为细菌学)、真菌(现为真菌学)——包括与地衣共生的真菌()、非绿藻(藻类学)、病毒(病毒学)。虽然上述领域已不再是现代植物学的研究范围,但植物学家仍关注这些物种,并且真菌和原生生物也仍包含在植物学课程内。 根据内共生学说,地球上最早出现的光合放氧生物蓝菌通过内共生演化为了植物真核祖先细胞内的叶绿体。由此产生的光合植物加快了大气氧含量的升高,彻底改变了远古地球的大气组成,并在其后20多亿年里维持大气中充足的氧含量。古植物学是研究远古时期遗留的植物化石记录的学科,科学家也从这些记录中探索。 21世纪关于植物的主要问题是其作出基础生产者,在全球范围的生命原材料循环中扮演的角色。这些原材料包括能量、碳、氧气、氮和水。此外,人们也关注植物管理与环境问题之间的关联,例如、自然保护、粮食安全、" +485,"应则用于合成特定物质,例如用于固定植株的木质素和纤维素,或是生成一些次级产物,如树脂和芳香味化合物。 通过纸色谱法看到的不同种类。 叶黄素 叶绿素a 叶绿素b 植物和其它光合真核生物细胞内有独特的细胞器——叶绿体,其被认为是从蓝菌与植物祖先内共生演化而来。叶绿体和蓝菌内都含蓝绿色素叶绿素。叶绿素(主要是叶绿素a和叶绿素b)可吸收光谱中的蓝-紫与橙-红部分,并反射绿光,因此在人类看来呈绿色。植物在红蓝光中吸收的能量由叶绿体转化为富含能量的碳化合物,该转化过程还需要用到二氧化碳和水作为原料。这个过程称为碳固定,氧气是该反应的副产物。 叶绿素从光照中捕获的能量最初以电子形态存在,而后形成电化学梯度。捕获的电子用于制造ATP与NADPH分子,这些分子用于储存与转运能量。能量随后用在卡尔文循环的暗反应中,从RuBisCO酶生产出三碳糖甘油醛3-磷酸(G3P)。甘油醛3-磷酸是光合作用的最初产物,是合成葡萄糖及几乎所有其它有机分子的原材料。部分葡萄糖被转化为淀粉储存在叶绿体中。淀粉是大多数陆生植物及藻类的能量储存介质,但菊科植物使用菊粉(属于果糖)作为能量储存介质。部分葡萄糖被转化为蔗糖,以更方便" +486,"碱,这是一种生物碱,对人畜有神经毒性。而另一些化学产物,人们可能会被其特殊风味吸引,例如辣薄荷、精油、柠檬油等,从而将它们作为香料或调味品。医学上也将由罌粟生产的鸦片用于麻醉镇痛。许多医学或娱乐药物,例如四氢大麻酚(大麻的主要成分)、咖啡因、吗啡、尼古丁等,都直接源自植物。还有些产品则是天然植物的简单衍生物。例如,止痛药阿司匹林(也即乙酰水杨酸)最初是从柳树树皮分离,许多阿片类止痛药(例如海洛因)都是来自罂粟产物吗啡的化学修改。人们常用的兴奋剂也源于植物,例如来自咖啡的咖啡因、茶、以可可豆制作的巧克力、来自烟草的尼古丁。人类酿造的酒精饮料一般来自糖分丰富的植物,例如啤酒源自大麦,清酒源自水稻、红酒源自葡萄。美洲原住民在其数千年历史中发现了许多具备药用价值的植物,制药产业有时会参考民族植物学的相关记录,将其作为发现新药物的一种方法。 植物也被用于制造颜料和染料,例如红酒中的红色源自花色素苷,牛仔裤的靛蓝色来自黄木犀草和菘藍共同制成的吲哚酚。艺术家常用的藤黄和茜草紅分别来自热带金丝桃科植物的树脂以及染色茜草的根。 来自植物的常见原材料还有糖、淀粉、棉花、麻布、火麻、部分绳子、木材、塑合板、" +487,"影响这些环境因素。例如,植物可影响阳光反照率、改变地表径流、稳固矿质土壤、增加土壤有机物含量、影响当地气候。植物会同环境中其他生物竞争资源,也和多种(如群体、种群和群集)中的同类交互。具备相似、优势植物、生物与非生物因子、气候条件、地理环境的区域可被归为一类生物群系,例如冻原和热带雨林。 植食性动物以植物为食,而植物可凭借各种方法。有些植物也演化出了寄生或肉食行为。植物与其它物种可形成互利共生关系,例如某些植物可与菌根、根瘤菌互换食物,可获得蚂蚁的保护,蜜蜂、蝙蝠等动物可帮助传播花粉,人类和部分动物可作为帮助植物散播种子。 === 植物、气候和环境变化 === 人们可根据植物对气候和其它环境变化的响应理解这些变化对生态环境功能与生产力的影响。例如,植物物候学可作为理解、气候变化的生物学影响的窗口。孢粉学对数百万年前花粉化石沉积物的分析可用于重建古代气候。根据对古生代陆生植物体型、叶片气孔密度以及叶片形状的分析,科学家可估算当时大气二氧化碳浓度。臭氧层空洞可将植物暴露于高水平紫外线辐射,从而减缓其生长速率。此外,对植物群集、系统分类、生物分类的研究也有助于深入认识植被变化、栖息地破坏、物" +488,"的染色体,不过错误的细胞分裂也可能使染色体加倍。这类错误可发生在胚胎生长早期,导致同源多倍体或部分同源多倍体生物;或发生在普通细胞分裂时,产生一些多倍体(核内倍数性)细胞;或在配子形成时。两种不同植物物种的杂交种可能是异源多倍体()。同源多倍体和异源多倍体常是可育的,但可能无法与其父代种群杂交,因为两者染色体数目不同。这些植物与父代种群有生殖隔离,但仍生活在同一区域,最终或许可形成。一些不育的多倍体仍可通过营养繁殖或种子的无融合生殖产生完全一致的后代。硬粒小麦是可育的异源四倍体,而普通小麦是可育的六倍体。商业种植的香蕉是不育的、无种子的三倍体。西洋蒲公英是三倍体,可借无融合生殖产生可育的种子。 与其它真核生物一样,由内共生而来的细胞器,例如线粒体和叶绿体,它们的基因遗传不遵循孟德尔定律。在裸子植物中,叶绿体基因遗传自雄性父代,而在被子植物中则是遗传自雌性父代。 === 分子遗传学 === 科学家从模式生物(例如拟南芥)的研究中获取了许多重要的知识。拟南芥的基因组(或称遗传信息)包含1.35亿个DNA碱基对,属于被子植物中最小的基因组之一。2000年,拟南芥的基因组测序完成,是首个完成基" +489,"植物根部的自然载体。如今,对Ti质体的基因修改已成为制造转基因作物的主要手段。 === 表观遗传学 === 表观遗传学是指无法透过DNA序列变化得到解释的可遗传基因功能变化,它可导致生物体性状改变。例如DNA甲基化导致的基因表达(或不表达)就属于表观遗传学变化。依附于DNA缄默区的抑制蛋白也可控制基因表达与否。表观遗传记号可能在植物的程序性发展阶段被加入或移除,并造成了诸如叶片与花瓣的区分。这些记号可能是暂时的,也可能延续至细胞分裂结束,从而伴随整个细胞生命周期。有些表观遗传变化是可遗传的,而另一些则在繁殖细胞阶段被重置。 真核生物借助表观遗传变化控制细胞分化。形态发生时期,全能干细胞转变为胚胎中的多种富潜能,并最终分化为不同细胞。植物胚胎从受精卵开始,并持续分裂分化为薄壁组织、、韧皮部筛管、植物表皮的保卫细胞等不同组织。这种分化过程源于部分基因的表观遗传激活以及另一些基因的抑制。 与动物不同的是,许多分化后的植物细胞(尤其是薄壁细胞)仍保留有全能性,因此仍具备发展为新植株的潜能。不具备全能性的植物细胞包括高度木质化的细胞,成熟后死亡的厚壁组织和木质部,以及缺少细胞核的韧皮部筛管。虽然" +490,"的维管。 無維管束陆生植物是不含木质部和韧皮部的有胚植物,其下包含苔藓植物门、地钱门和角苔门。带有木质部和韧皮部的蕨类维管植物在志留纪晚期至泥盆纪早期分化为不同谱系,其中古老的石松门存活至今。泥盆纪末期,部分植物群例如石松门、前裸子植物门、演化出了“大孢子”——这些植物产生两种大小的孢子,较大的称为,较小的称为微孢子。这些植物的减数配子体由孢子囊内的大孢子发育而来,称为内生孢子。种子由覆盖了一或两层珠被的内生大孢子组成。孢子体最初在种子内部发育,在发芽时释放至外部。已知最早的种子植物可追溯至泥盆纪法门期。随着种子的演化,种子植物开始分化为不同群体,包括已灭绝的种子蕨门,以及存活至今的裸子植物门和被子植物门。裸子植物的种子裸露,不被子房包裹,如今代表性的裸子植物包括松柏、苏铁、银杏和买麻藤。被子植物的种子由雌花器或子房包裹。现代分子遗传学研究显示,被子植物似乎是裸子植物的姐妹群。 ==植物生理学== 植物生理学的研究范畴包括一切与生命相关的植物内部物理化学活动。植物代谢所需的所有物质都源于空气、土壤与水体。植物利用光合作用捕获来自太阳光的能量,随后被供给至细胞呼吸,这些能量构成了所有生命" +491,"世纪末,科学家提出假设认为植物可产生激素调控自身生长。达尔文设计实验检测了植物芽尖和根部的向光性及向重力性,他得出结论“很难否认植物培根尖端……行为就如低级动物的大脑……可作出不同运动”。就在同一时期,德国科学家发现了生长素对植物生长的调控作用。最早发现的生长素是吲哚-3-乙酸(IAA),其可促进植物细胞生长,并最终在50年后被分离。生长素是造成植物向光性和向重力性的原因。1939年,科学家发现植物营养组织可在含IAA的培养基中维持,1947年观察到可通过控制IAA浓度诱使营养组织生根发芽,这是植物生物技术和基因编辑的里程碑。 细胞分裂素也是一类植物激素,顾名思义,它能控制细胞分裂过程。属于细胞分裂素,最初在玉米中发现,属于腺嘌呤的衍生物。玉米素由根部生成,经木质部传输至茎干,促进此处的细胞分裂、芽的发育以及叶绿体变绿。吉贝素属于二萜化合物,由乙酰辅酶A羧化酶经甲羟戊酸途径合成。吉贝素涉及植物发芽和种子解除休眠,并透过控制茎干细胞生长来控制植株高度,控制开花。脫落酸(ABA)存在于除地钱外的所有陆生植物中,由叶绿体或其它质体从类胡萝卜素合成。该激素抑制细胞分裂,促进种子成熟和休眠,并促" +492,"通常没有叶绿素。非维管植物例如地钱、角苔、苔藓,它们没有用于穿透土壤的维管根,但也进行光合作用。地钱的孢子体阶段不具光合作用能力,但角苔和苔藓的孢子体阶段可以依靠光合作用满足部分能量需求。 植物的地上与地下部分相互依存,地下的根系需要依靠地上光合作用获取能量,而地上部分需要依靠根系吸收水分与矿物质。各部分细胞可分化为对方的组织,或生成不定根或不定枝,例如块茎、块根和。靠近地面的根系,例如在柳树中所见到的,可发展出独立的地上部分,并最终形成全新植株。地上或地下部分缺失时,另一部分通常可以再生长。因此仅凭单片叶子(例如非洲堇属),甚至单个细胞(可分化为愈伤组织)都可能繁育成完整植株。在维管植物中,木质部和韧皮部作为传输组织,负责在地上与地下部分之间运输物质。根部有时可用于储存营养物质,例如糖(糖用甜菜)或淀粉(胡萝卜)。 茎的功能主要是为叶和繁殖器官提供支撑,但在肉质植物中也可储存水分(仙人掌)或营养物质(土豆)。茎有时也可用于营养繁殖,例如草莓的匍匐茎繁殖,或是繁殖。叶负责收集光照,并将其用于光合作用。种子植物中的裸子植物,例如松柏、苏铁、银杏、买麻藤,可产生裸露的种子。而被子植物可开花" +493,"以二名法命名。《国际藻类、真菌和植物命名法规》规范了植物学的科学命名,法规由国际植物学大会负责管理。 依据生物分类学,植物界属于真核生物域,各生物依次向下归类,直至物种层级。层级由高到低分别为:界、门、纲、目、科、属、种。植物的拉丁学名由其属和属下的种名组成,由此生成独一无二的名称。例如鸢尾的学名为Iris tectorum,其中Iris为其属名,表示该物种属于鸢尾属,而tectorum是其种加词。属名首字母需大写,种加词小写,整个学名需以斜体表示,如果无法斜体,则需添加下划线。 生物群的演化史与遗传关系称为系统发生,对此的研究称为系统发生学。该学科的基础方法是利用生物间的遗传相似性探究其关系。例如叶仙人掌属枝叶繁茂,与其它叶片呈针尖状的典型仙人掌(例如金琥属)的外观非常不同。然而叶仙人掌属和金琥属都有由(高度特化的垫状结构)产生的尖刺,这表明这两个属存在演化上的亲缘关系。 因植物相似性而作出的亲缘关系判断必须十分仔细,因为趋同演化也使不同植物物种独立演化出相似特征。部分大戟屬物种有和仙人掌相似的无叶球茎,这是因适应水栖环境而发生的形态演化,假若仔细观察就可发现,这些物种的花和仙人掌的" +494,"究有助于植物学家更好的理解植物的演化过程。除了对模式植物的研究以及日渐常用的遗传证据外,分类学家也在讨论和尝试更好的将植物放入分类单元。其它科技,例如计算机和电子显微镜的发展极大促进了植物学研究的精细程度,并可在短时间内分析大量数据。 == 符号 == 植物学中曾应用一些符号,其中部分已经过时,例如林奈曾用行星符号表示木质、草本和多年生植物,而韦尔登诺曾用(土星符号)代表无性植物,用(水星符号)代表雌雄同体植物。以下是植物学中仍在使用的符号: : ♀ 雌性 : ♂ 雄性 : ⚥ 雌雄同体 : ⚲ 植物无性生殖 : ◊ 未知性别 : ☉ 一年生植物 : ⚇ 两年生植物 : ♾ 多年生植物 : ☠ 有毒植物 : 🛈 更多信息 : × 杂交 : + 嫁接杂交 == 参见 == * * * * * * * * * * 开花植物列表 * * == 注释 == == 参考文献 == === 脚注 === === 来源 === * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *" +495,"生命是一种特征,物质存在的一种活跃形式。目前对于生命的定义在学术界还无共识,较流行的定义是一类维持体内平衡、具有生命周期和稳定的物质和能量代谢现象、能对刺激做反应、能进行自我复制和繁殖、进化的半开放物质系统。由细胞组成,能够成长、适应环境。其他定义有时包括非细胞生命形式,如病毒和类病毒。The American Heritage Dictionary of the English Language, 4th edition, published by Houghton Mifflin Company, via Answers.com : * The property or quality that distinguishes living organisms from dead organisms and inanimate matter, manifested in functions such as metabolism, growth, reproduction, and response to stimuli or adaptation to the environment o" +496,"性的过程。地球上的生命最早出现在42.8亿年前,在44.1亿年前海洋形成后不久,在45.4亿年前地球形成后不久。Schopf, JW, Kudryavtsev, AB, Czaja, AD, and Tripathi, AB. (2007). Evidence of Archean life: Stromatolites and microfossils. Precambrian Research 158:141-155.Schopf, JW (2006). Fossil evidence of Archaean life. Philos Trans R Soc Lond B Biol Sci 29;361(1470) 869-85. 當時是始太古代,冥古宙熔化的地殼已經固化。已知最早的生命形式是细菌的微化石。研究人员普遍认为,目前地球上的生命来自于RNA世界 * * * : There is now strong evidence indicating that an RNA World did indeed exist before DNA- and protein-based l" +497,"展現以下全部現象: # 恆定性:能够調節體內環境以維持身體處於一個相对恆定的狀態,例如恒温动物能發汗来降低过热的體溫,也能靠发抖来产生额外的热量以保持体温。 # 組織性:由一個或以上的生物基本單位──細胞所組成。 # 新陳代謝:能够轉換非生物為細胞成分(組成代謝)以及分解有機物(分解代謝)来获取和转化能量。生物體需要能量来維持體內平衡及产生其他生命现象。 # 生長:使組成代谢的速率高於分解代谢的速率来让细胞体积增大,并在细胞分裂后使细胞成长。一個生長中的有機體增加其细胞的数量和体积,而不止是将得到的物質积存起来。某些物种的个体可以长得很巨大,例如蓝鲸。 # 適應:對環境变化作出反應的能力,与生物当前的身体构造、生活习性及遺傳有关。這種能力對生存是很重要的。生物可以通过進化适应环境。 # 感應:反應可以以很多方式進行,从單細胞变形虫被触碰时的收縮到高等生物在不同情况下的複雜反射。最常见的反应是運動,例如植物的葉片轉向太陽以及動物追捕其獵物。 # 繁殖:能够產生新的個體。包括只需一個親本的無性生殖和需要至少兩個親本的有性生殖。 大部分科學家稱這樣的現象為生命的表現方式。通常必須具備全部七個" +498,"ous agent)或一種多主体系统(multi-agent system),並最少完成一次熱力學循環。 == 起源 == 儘管不能準確地找到確實時間,但有證據表現地球上的生命已存在了大約37億年。 雖然沒有標準表示生命起源的模型,但現時最為公認的科學模型建立於一個或更多包括下面的發現之上,可以粗略地列出有以下假設: # 模擬真實的史前生物環境以製造形成生命的基本細小分子。這已由米勒-尤里實驗以及Sidney W. Fox的工作所證明。 # 磷脂自發地形成脂双分子层,而脂雙層是細胞膜的基本結構。 # 製造隨機核糖核酸分子的過程可能製造出核酶,而可以在非常特殊的環境下製造更多核糖核酸。 很多不同的假說认为早期地球上的簡單有機分子能够轉变為原始細胞并进行新陳代謝。很多模型可分為「先有基因」或「先有新陳代謝」兩類,但最近流行的混合模型並不属于任何一類http://www.journals.royalsoc.ac.uk/openurl.asp?genre=article&id=doi:10.1098/rsif.2005.0045。現時所推測的生命歷史还有很多疑点,生命的起源對科學家而言仍是一個" +499,"* 外星生命 * 细胞生物 * 非细胞生物 * 細胞自動機 * 嗜極生物 - 生存於「極端」環境(例如熱液噴口)的生物 * 界 * 生命的起源 * 蓋亞假說 * 分类学 - 描述,分類以及命名生物的科學 * 種系發生學 - 研究物種間的進化關係 * 生命遊戲 - 簡單透過數學上模仿生態系統的動態細胞自動機 * 生物分类学 * 生命的意義 * 生活品質 == 参考文献 == ==延伸閱讀== == 外部链接 == * Wikispecies - 维基网上生命字典(英) Category:主要话题条目" +500,"()是一種可通过浏览器访问并由用户协同编辑其内容的网站。沃德·坎宁安於1995年开发了最初的wiki。他将wiki定义为“一种允许一群用户用简单的描述来创建和连接一组网页的社会计算系统”。 有些人認爲,wiki系統屬於一種人類知識的網路系統,讓人們可以在web的基礎上對wiki文本進行瀏覽、創建和更改,而且這種創建、更改及發佈的成本遠比HTML文本小。與此同時,wiki系統還支持那些面向社群的協同寫作,爲其提供必要的幫助。最後wiki的寫作者自然構成一個社群,wiki系統爲這個社群提供簡單的交流工具。與其它超文本系統相比,wiki有使用簡便且開放的特點,有助於在一個社群內共享某個領域的知識。 == 詞源 == wiki 取自夏威夷的Wiki Wiki公車,源自夏威夷語「wiki」,本是「快速」之意。wiki的中文翻譯有維客、圍紀、快紀、共筆和維基等等,其中「維基」一詞是中文維基百科人特別爲維基百科而創,屬於维基媒体的专用术语。隨著「維基」一詞能見度增加,常被泛用爲wiki的主要音譯名。 == 歷史 == wiki軟體由软件设计模式社群開發,用來書寫與討論模式語言。沃德·坎宁安於1995" +501,"重要特徵。 坎宁安認為原來的HyperCard程式十分有用,但創造卡片與卡片之間的連結卻很困難。於是他不用HyperCard程式原本的創造連結功能,而改用「隨選搜尋」的方式自己增添了一個新的連結功能。使用者只要將連結輸入卡片上的一個特殊欄位,而這個欄位每一行都有一個按鈕。按下按鈕時如果卡片已經存在,按鈕就會帶使用者去那張卡片,否則就發出嗶聲,而繼續壓著按鈕不放,程式就會為使用者產生一張卡片。 坎宁安向他的朋友展示了這個程式和他自己寫的人事卡片,往往會有人指出卡片之中的內容不太對,他們就可當場利用HyperCard初始的功能修正內容,並利用坎宁安加入的新功能補充連結。 坎宁安後來在別處又寫了這樣的功能,而且這次他還增加了多使用者寫作功能。新功能之一是程式會在每一次任何一張卡片被更改時,自動在「最近更改」卡片上增加一個連往被更改卡片的連結。坎宁安自己常常看「最近更改」卡片,而且還會注意到空白的說明欄位會讓他想要描述一下更改的摘要Wiki History ,last edited February 18, 2007。 == 特征 == 奥德·坎宁安和波·路夫(Bo Leuf)在《Wiki之道" +502,"接到一个给定页面的页面。 == 认可与安全 == === 控制更改 === wiki的基本設計理念是,與其避免人們犯錯,倒不如讓人們更方便地修正錯誤。因此,wiki固然相當開放,但它有一個有助檢驗最近新增頁面正確性的功能。幾乎每一個wiki網站都有的最突出的功能,就是「最近修訂」頁面——一個列出最近修訂的特殊頁面,或是一個在特定時間範圍內所做修改的列表。一些wiki可以對此清單作出過濾,篩去小修改或利用自動腳本所做之修改(所謂「機械人」)。 大部分wiki網站的頁面編修紀錄頁都擁有以下功能:可查看過去的修訂版本,亦可在任何兩個修訂版本之間進行差異對比。編輯者可以利用修訂歷史瀏覽並且恢復此條目的前一版本;顯示差異功能則能讓編輯者更容易決定是否有必要做此更改。一個普通的wiki使用者可以在「最近修訂」頁面瀏覽差異、查閱歷史、甚至恢復到先前的版本。這個過程基本上是很流暢的,具體細節則要看用的是哪款wiki軟體。 為了避免人們做出差劣的編輯,有些wiki引擎可以對內容編輯權限進行各種程度的限制,以確保一篇或一組條目的品質。當有人修改某個條目時,願意維護該頁面的使用者(們)會收到通知,讓他/她得" +503,"的wiki。企业的wiki有可能只允许内部员工修改。 == 参考文献 == == 参閲 == * 維基百科 * wiki引擎 * 內容管理系統 * 網誌 * 維基 * 维基农场 * 群件 * 知識管理 * 知識共享 * MediaWiki * 社会性软件 * Web 2.0 * wiki软件比较 == 外部連結 == ;英文 * WikiIndex ,wiki技术指南 * Exploring with Wiki ,Bill Verners对沃德·坎宁安的专访 * WikiMatrix ,wiki软件及hosts的比较 * WikiPapers ,发布wiki相关出版物的wiki站点 * WikiTeam,由志愿者维护的wiki存档 * Murphy, Paula (April 2006). Topsy-turvy World of Wiki. 加利福尼亚大学. * WikiWikiWeb * Portland Pattern Repository ;中文 * 網絡新潮文化:Wiki不完全手冊 * 企業 wiki 軟體大探索(繁體中文) ;Wiki站 * 台灣棒球維基館 * 中文Fand" +504,"电脑程序设计(),或稱程式設計(),是给程序解决出特定问题的过程,軟體開發過程中的重要步驟。程序设计方法往往以某种程序设计语言为工具,给出这种语言下的程序。程序设计过程一般包括分析、设计、编碼、测试、除错等不同阶段。 == 发展历程 == 在计算机技术发展的早期,軟體開發主要就是程序设计。但随着技术的发展,软件系统越来越复杂,逐渐分化出许多专用的软件系统,如操作系统、数据库系统、应用服务器,而且这些专用的软件系统愈来愈成为普遍的系統環境的一部分。这种情况下軟體開發的内容越来越丰富,不再只是纯粹的程序设计,还包括数据库设计、用户界面设计、通信协议设计和复杂的系统配置过程。 空間方面,在早期,由于机器资源比较昂贵,如何縮小儲存空間往往是设计关心的首要重點;而随着硬件技术的飞速发展,電腦上資料儲存媒體的價格降低,空間不再是考慮的第一要點,一些較耗時的運算也漸漸發展出以空間換取時間的模式。 時間方面,在早期,如何加強程式效率、縮短程式執行時間是程式設計師的共同目標;而在硬體效能進步、效率差距縮小,软件规模與複雜度卻日益增加的現在,程序的结构、可维护性、重複使用性、彈性等因素更顯得重要。在多人合" +505,"计算机程序()是指一组指示电子计算机或其他具有訊息处理能力的電子装置每一步动作的指令,通常用某种程序设计语言编写,运行于某种目标体-{系}-结构上。打个比方,一个程序就像一个用汉语(程序设计语言)写下的红烧肉菜谱(程序源代码),用于指导懂汉语的人(体系结构)来做这个菜。 计算机程序是软件的一个组成部分,它还包括文档和其他无形组成部分。通常,以英文文本为基础的计算机程序要经过編譯和連結而成为一种人们不易看清而计算机可解读的一連串數字的格式,然后放入运行。这种程序也叫作编译语言。未经编译就可运行的程序,通常称之为脚本程序(script)或解释型语言。 == 程序的运行 == 为了一个程序运行,计算机加载程序代码,可能还要加载数据,从而初始化成一个开始状态,然后调用某种启动机制。在最低层上,这些是由一个載入器开始的。 在大多数计算机中,操作系统例如Windows等,加载并且执行很多程序。在这种情况下,一个计算机程序是指一个单独的可执行的映射,而不是当前在这个计算机上运行的全部程序。 程序里的指令都是基于机器语言。程序通常由一个计算机程序设计语言编写,然后由该语言的编译器(或者,有时由汇编程序" +506,"数据可以是一个有待执行的程序(参见脚本编程语言),程序可以编写成去编写其它的程序;所有这些例子都使程序和数据的比较成为一种视角的选择。有人甚至断言程序和数据没有区别。 编写一个程序去生成另外一个程序的过程被称之为元编程()。它可以被应用于让程序根据给定数据生成代码。单单一个程序可能不足以表示给定数据的所有方面。让一个程序去分析这个数据并生成新的程序去处理数据所有的方面可能会容易一些。Lisp就是一例支持这种编程模式的程序语言。 在神经网络裡储存的权重是一种数据。正是这些权重数据,跟网路的拓扑结构一起,定义了网络的行为。人们通常很难界定这些数据到底表示什么或者它们是否可以由程序来代替。这个例子以及跟人工智能相关的其它一些问题进一步考验程序和数据的区别。 == 算法 == 算法指解决某个问题的严格方法,通常还需辅以某种程度上的运行性能分析。算法可以是纯理论的,也可以由一个计算机程序实现。理论算法通常根据复杂性分为不同类别;实现的算法通常经过剖析()以测试其性能。请注意虽然一个算法在理论上有效可行,但是一个糟糕的算法仍会浪费宝贵的计算机资源。(更详细信息,参见算法信息论()) == 开发 =" +507,"* 操作系统 * 图灵机 * 系统需求 * 巨集 == 参考文献 == # Eric Baum What is Thought MIT Press 2004年 ISBN 0-262-02548-5 # - Chapter Two: The Mind is a Computer Program == 外部链接 == * 计算机程序的定义@ Webopedia Jisuanjichengxu Jisuanjichengxu" +508,"中國歷史,如果從中國的文字史首次成体系的甲骨文出现的商朝中期(前1300年算起)算起約有3,300年;从考古学上具有广域王权的都城定位的二里头文化遗址(前1920年)算起約有3,700年;从西周文献中的夏朝(前2070年算起)算起约有4,100年;从西周文献中傳說中的尧(前2350年算起)算起约有4400年;從孔子所言三皇五帝的傳說時代算起約有4,700年(前2698年算起)或者5300年(前3300年算起);从已发现的最早城市良渚文化遗址算起約有5,300年(也有意见认为良渚文化仍属史前时期);從盤古、女媧、有巢氏等不確定的神話時代算起「約有五千年」(這也是傳統民間認知上的長度);从考古学上具有酋邦定位的西辽河流域牛河梁遗址(古国时代开端)算起有5800年;从第一座城市高庙文化彭头山遗迹算起有6800年历史;从最早的文字雏形贾湖契刻符号开始算起有8600年历史;從新石器時代的磁山文化算起約有1万年;從舊石器時代的北京猿人和藍田猿人時期算起約有68–100多萬年的历史。“蓝田人”年代的古地磁学研究 《古脊椎动物学报》 1978年04期 中国的傳說有伏羲做八卦,而近代在湖广高庙文化遗址则" +509,"態觀察,中國古代人口主要由自由人構成,私有制、商業活動發達。周朝時商業主要由封建領主階層控制的官商貿易和庶人的自由貿易構成。秦漢以後實行中央集權,人口由士、農、工、商等構成,其中以從事農業的自由民為主體,是一個君權官僚制下的以土地爲主要生產資本的較為自由的商業經濟社會,一些重要的行業譬如油鹽米等由官僚和商人垄断。除了農業,手工業以及商業貿易也有很大的發展。早在漢朝絲綢之路的開通,促進東亞與中亞至歐洲的陸上交通時,國際貿易早已起步;隋唐時大運河的開通促進南北貿易;唐朝的盛世及外交的開放、交通的建設,更使各國文化、物資得以交流;唐朝時出現類似匯兌証券的飛錢,宋代時出現紙幣;元代時更因為全面開通商旅的關卡使得與中亞的商業交流十分繁榮;明清时實行海禁,清末則受到西方國家的影響,海上絲路的國際貿易發展迅速。20世紀初中華民國實行私有制和资本主义制度,普遍被认为带有官僚资本主义色彩;而中华人民共和国成立初期,起先為公有制的計劃經濟社會,1978年起治國方向調整為改革開放,逐步朝向私有制、資本制下的市場經濟社會之轉型,該形態被稱為在宏觀調控下的中国特色社會主義市場經濟,同時工業化及都市化發展迅速,數" +510,"更揭示了古代社會的生活方式、技術水平和文化特徵,為我們理解古代人類演化提供了寶貴的信息。 ===新石器时代=== 仙人洞、吊桶环遗址的考古结果表明,中国在1.8万年前就已经会制作陶罐。陶罐的出现标志着中国率先进入新石器时代。仙人洞、吊桶环遗址还发现了半野生半驯化的人工稻米,这些考古学证据说明中国是世界上最早栽培水稻的地区。当今世界上最早的驯化水稻则发现与湖南南部湘江源头的道县玉蟾岩遗址,时间约为公元前1.3万年。 新石器時代早期的代表性文化是裴李崗文化,這一時期屬於母系氏族社會,社會尚無明顯分層。緊接著是以仰韶文化為代表的新石器時代中期,這一時期,社會性質從母系氏族社會過渡到父系氏族社會,社會貧富分化逐漸加深,同時生產力水平有顯著提高。而以龍山文化(4900年至4100年前)為代表的新石器時代晚期,被发现在長江、黃河流域有數個城址群,如中原城址群、江漢城址群、海岱城址群等。這或許表明在龍山時代,社會的組織形態發生較大的轉變。 根據現在的考古學研究,中國的新石器時代呈現多元並立的情形:約7,000年到5,000年前在河南省、河北省南部、甘肅省南部和山西省南部出現的仰韶文化便具備使用紅陶、" +511,"·文部》中写“華,榮也”。《周书·武成》:“夏,大也。故大國曰夏。華夏謂中國也。”古人認為中國處於世界的中心,因此稱該地為「中華」(中央之國)。 ===外來說=== ==== 中亞起源説 ==== 中亞起源説與出埃及説一樣支持中國文明與古埃及文明是來自同一源頭,但不認為中國文明是古埃及文明的一個分支,而是推斷中國文明和古埃及文明同樣是來自一個更遠古的中亞文明,該文明在公元前1600年左右才進入中國,中國史家對夏朝的描述實則是對這個更遠古的中亞文明的歷史記錄。這個假説同時指出古北歐文明同樣是起源於中亞,與中國文明同源。 1921年,瑞典考古学家安特生发现了仰韶文化遗址,并在当地发掘出许多彩陶。在比较了当地彩陶和中亚阿什哈巴德附近发现的安诺文化彩陶、乌克兰的特里波列文化彩陶,以及甘肃一带发现的彩陶之后,安特生在《中华远古之文化》一文中提出,从中亚开始,彩陶的成品年代逐渐接近当代,说明中国陶器的制作技术是从中亚传入的,因此中国文化亦有可能是从中亚传入的。但是在1931年,中国学者梁思永发现了河南安陽的后岗三叠层,证明了仰韶文化、龙山文化和商朝文化一脉相承,未受外界根本性的影响,安特生也承认了" +512,"出來的傳說,黃帝原系炎帝部落的一個分支的領袖,強大之後在阪泉之戰中擊敗炎帝,成為新部落聯盟首領,之後又與東南方的蚩尤部落發生衝突,在涿鹿之戰中徹底擊敗對手,樹立自己的霸主地位。传说中指南车就是由黄帝所发明﹕“大驾指南车,起黄帝与蚩尤战于涿鹿之野。蚩尤作大雾,兵士皆迷,于是作指南车,以示四方,遂擒蚩尤,而即帝位。”,此后中国历代都记载磁石的应用。後來黃帝的孫子顓頊和玄孫帝嚳繼續擔任部落聯盟的首領。帝嚳的兒子堯繼位,他是一名賢君,創立禪讓制,傳位給舜。在舜時期,洪水氾濫,鯀採用堵塞的方法,結果洪水更厲害了,鯀被處決,他的兒子禹採用疏導的方法,成功治理洪水,因此被推舉為首領。 禹的兒子啟破壞禪讓制方式,禹生前本想把權位傳給益,禹死后,益没有得到权位,反而在民众的拥护下,启得到了权位。之后益率领着东夷联盟讨伐启。经过几年的斗争后,启确立了他在部族联盟中的首领地位。启都郊外的有扈氏仗义起兵,率领部族联盟向启都讨伐,与启军大战于甘。 启死后,其子太康继承后位。太康不理政事,在位期间,夏部族权威削弱,东夷族太昊与少昊部落趁机西进。东夷族有位善射的首领羿。羿率军从东夷属地鉏迁至夏后氏的属地穷石,与当地" +513,"夏由此复国,后世称“少康中兴”。通过太康失国至少康中兴的这段叙史可以看到夏后氏平定中原方国部落(尤其是东夷部落)的历程。少康之子杼继承后位。他了解东夷对夏室的不满,为了巩固在东方的势力,他把都城从原东迁至老丘。他重视发展武器和制造兵甲。文献中常常有“杼作甲”、“杼作矛”的说法。他还派人讨伐东南沿海地区的东夷(今山东南部、安徽东部、江苏一带)。夏朝的版图在杼的统治下扩张到了东海(今黄海)之滨。杼在位期间是夏朝最昌盛的时期。夏人对杼格外尊重,为杼举行过“报祭”。 杼之子槐在位时,东夷与夏人开始和平共处。其中畎夷、于夷、方夷、黄夷、白夷、赤夷、玄夷、风夷、阳夷,居于淮河、泗水流域的九个部落(即九夷)通常向夏后纳贡祝贺。槐逝后,其子芒继位。芒逝后,其子泄继位。其子不降继位。他曾多次率兵征讨西方的九苑。不降年迈时,内禅于其弟扃。扃逝,其子廑继位。廑继位后不久便病死,其堂兄不降之子孔甲继位。他改变夏礼中祭祀祖宗的传统,开始祭祀天帝。不少方国部落开始对夏室不满,但夏朝与东夷的关系仍然友好。 夏后氏自孔甲开始日趋衰落。孔甲逝,其子皋继位。皋逝,其子发继位。这段期间,方国部落与夏室的关系恶化,氏族内部的" +514,"小甲是太庚之子,旁系先王。小甲时期,商王朝第一次衰落。小甲在位十七年崩。根据传世文献记载,雍己继位为王。雍己之后,太戊有中丁、外壬、河亶甲三子相继为王。中丁夺位遭到了王朝内部其他统治势力的反对。元年,诸侯侵犯京畿,中丁被迫放弃经营了一百多年的都城亳,东迁于嚣,由此保住了王位。东土边境的蓝夷部落趁机作乱,中丁伐之。中丁在位九年或十一年崩,其弟外壬继位,都于嚣。其王位是平稳传承还是争夺而来的,由于记载的缺乏,无法明了,但外壬即位元年,邳人、侁人叛乱,这可能说明其王位是争夺而来的。外壬在位五年或十年崩,其弟河亶甲夺位称王,迁都于相。夺权徒都导致商王朝政权内部分裂,国力剧减,诸侯侵犯,商王朝第三次衰落。河亶甲连年用兵讨伐叛离诸侯。征蓝夷,又征班方,侁人联盟班方,河亶甲联合彭伯、韦伯克邳、降班方,侁人臣服。河亶甲为旁系先王,在位九年崩。中丁之子,祖乙即位。即位之初迁都于邢,其王位可能是争夺而来的。祖乙作为太戊的嫡长孙,可能得到了王朝内部旧贵族势力的支持,立旧臣巫咸之子巫贤为相,管理政事。如此政权逐渐稳定,修筑新都。命高圉为邠侯,巩固对西土边疆的统治。商王朝二次复兴。祖乙受殷人的尊崇程度与汤不相上" +515,"都于殷(今河南安阳),并固定下来,从此商朝也称殷朝。盘庚迁殷以后,商朝社会经济进一步发展。直至武丁即位以后,商四处讨伐,征服了周围许多小国,扩大了领土,商朝达到全盛时期。武丁死后,商朝逐渐衰落。商代最后一个王帝辛(商纣王)对开发中国东南,对淮河流域和长江流域的经济文化交流有很大促进;但由于连年发动战争和大规模建造琼宫瑶台,大大耗费社会的人力与资财,促使社会矛盾激化,自西伯周文王始圖滅商。文王次子周武王承襲乃父之志,联合羌、髳、卢等部落,共同伐纣,一直打到朝歌(今河南鹤壁市淇县)。人们不愿为荒淫无度的纣王卖命,纷纷倒戈,牧野之战后,纣王自焚,商朝从此灭亡。周王朝建立。 ==周朝== 周朝是中國歷史上繼商朝之後的王朝,也是最後一個完全施行封建制度(分封制)的世襲王朝,可分為西周(約前1100年-前771年)與東周(前770年-前256年)兩個時期。西周從周武王滅殷商建國並定都镐京(宗周)起,至周幽王亡國止,是中華文明的全盛時期之一。該時期的物質文明及精神文明皆深刻地影響着後世(2007年):《西周史與西周文明》〈引言〉,第1頁。。東周都城為洛邑(今河南洛陽),其時代又可分為春秋時期(前77" +516,",四十六年之间,天下安宁,刑错四十余年不用,史称“成康之治”。成為以後歷代就中國治世的典範。 周康王死后,子瑕继位,是为周昭王。周昭王十六年,昭王亲征荆楚,直至江汉地区。南征共3年,周昭王还师渡汉水时溺死,全军覆没。其子满继位,是为周穆王,继位时年已五十。穆王好大喜功,仍想向四方发展。又好游行,以致朝政松弛。东方徐国率九夷侵周。穆王南征,联楚平乱,得胜。经过昭穆时代之后,周朝開始实力大减。这一时期,西北戎狄渐兴。周懿王时,戎狄交侵,暴虐中国。周人深为所苦。 周厉王时期,连年战乱,百姓苦不堪言。同时,厉王以荣夷公为卿士,垄断社会财富及资源。为压制国人不满,周厉王命卫巫监视,有谤王者即杀。结果人人自危,终于酿成国人暴动。前841年,厉王出奔到彘(今山西霍县)。朝中由召穆公虎、周定公两大臣行政,号为共和(一说由诸侯共伯和摄行政事)。共和十四年(前828年),厉王死于彘,太子静即位,是为周宣王,在位四十六年。宣王励精图治,政通人和,诸侯来朝得宗周。宣王晚年,周王朝重新出现衰象。宣王干涉鲁国君位承继,以武力强立鲁孝公,引起诸侯不快。三十六年,征伐条戎、奔戎,惨败。三十九年,与西戎别支姜氏之戎战于" +517,"春秋战国时期。 由于周王室放弃了关中地区,实力大不如前,不仅无力控制诸侯,还要仰仗诸侯们的供给,早期主要是郑、卫国和晋国,到了战国时期则是魏国和韩国。周室的地位一落千丈,仅留有“天下共主”的虚名。春秋时期,周王室还有一点号召力,到了战国时期,连一点号召力都没有了。 周室东迁,关中故地被犬戎等部落所占据。当周幽王危难之时,秦襄公从秦起兵勤王,奋勇作战,杀退了犬戎,但是没能救出周幽王。但是秦襄公的英勇和忠诚得到了周平王的肯定,于是作为对秦人勤王之功的褒奖,周平王把岐周故地封给了秦襄公,把秦由周王室的附庸升格为诸侯,秦正式成为诸侯国。经过秦襄公、秦文公父子两代人的征战,基本上收复了岐周之地。东周开始后,郑国的郑武公、郑庄公父子把持了王朝的大政。他们利用出任王朝卿士的有利条件,常常借王命大肆扩张,使郑国成为春秋初年一个强国,号称小霸。由于郑庄公公朝中的势力太大,而且常常忙于他在郑国的私事,很少入朝听政,即使入朝,也仅是装装样子,或者干自己的私活。这样引起了周平王的不满。于是周平王逐渐起用虢公以分化郑庄公的权力,这引起郑庄公的强烈不满。而周平王在很多事情又必须依赖郑庄公,因此不得不向郑庄公解释," +518,"一說是齊、晉、楚、吳、越),合稱春秋五霸。 第一個稱霸的國君齐桓公任用管仲,在國內施行改革,使當時階級矛盾十分激烈的齊國安定下來,並逐漸強盛,許多诸侯都朝拜齊國。齊國率九個盟國之師攻伐威脅周朝的楚國,楚成王被迫將和。齊桓公死後,他的五個兒子爭做國君,齊失其霸業。宋襄公曾經試圖稱霸,後為楚軍所敗。 晉國公子重耳在一場內亂後登上君位,是為晋文公。晉文公實行改革,使晉國的勢力發展到中原,與南方大國楚國產生摩擦。後楚國出兵圍攻宋國首都帝丘,晋文公聯合秦、齊、宋三國之師大破楚軍於城濮,成為中原霸主。與晉國為盟的秦國國君秦穆公在晉文公死後起了稱霸的野心,與晉文公之子晉襄公屢次發生戰爭,最後以晉國的勝利告終,於是往西發展,把西邊的一些戎國滅掉,使秦穆公成為西戎的霸主。 楚成王的孫子楚庄王即位後,再次與晉國爭霸,楚國圍攻宋、鄭,晉國幾次派兵來救,都大敗而歸。許多中原國家背晉向楚。楚庄王成為中原的霸主。莊王死後,楚經靈王、平王兩代漸弱。東南方的吳國闔閭趁勢興兵伐楚,並大敗楚軍,攻佔楚國國都壽春,後平王之子昭王聯盟秦國攻吳,吳兵退出楚境。定都會稽(今浙江紹興)的越國趁吳軍遠征時攻擊吳國,大敗吳軍,吳王闔閭" +519,")、尉繚(著有《尉缭子》)等等。 ====戰國時代==== 前546年左右,黃河流域的爭霸基本結束,晉、楚兩國平分霸權。 前403年,晉國被三個門閥巨卿瓜分,成為韓、趙、魏三個諸侯國,史稱「三家分晉」。再加上姜姓的齐国被田氏取代,人稱田齊。 韓、趙、魏、齊、秦、楚及燕,並稱战国七雄,戰國時期正式開始。大部分马克思主义史學家將戰國開始劃為封建社會,然而大部分西方及臺灣學者卻又將之劃為封建社會的崩潰,這是定義不同所致。 戰國初年,各國開始變法運動,其中,以李悝變法的魏國,在魏文侯時首先興起,北伐韓、趙、中山,東伐齊、宋,南伐楚,西伐秦,成為強國。齊國在齊威王時興起,兩次攻伐魏國獲勝,並長期任為霸主。南邊的楚國經吴起變法後強盛,滅掉陳、蔡、越,擴展疆土。趙國經過赵武灵王胡服騎射後訓練成一部強大的軍隊,與秦、齊、楚爭先。韓國國君韓昭侯任用法家大師申不害變法,逐漸興起。 前356年,秦國的秦孝公採用法家的商鞅變法開始後,秦國國力大大增強,使其他六國恐懼,名士苏秦游遍六國,說服六國國君聯合合縱抗秦,秦國卻用张仪為相,又使六國屈從于秦,破壞合縱。宋、魯等小國到戰國後期為齊楚所滅。合縱破壞後,齊國作為" +520,"),从此中国有了皇帝的称号,并且发起一系列的改革以巩固帝国。而后南征百越、北伐匈奴。疆域东起辽东,西至临洮,北抵长城,南达象郡(今越南北部)。虽然秦朝外表十分强盛,但由于秦始皇集权、过度发展、严重劳役百姓,所以秦朝之统治不免带有苛急、暴虐之特点,让天下百姓饱受苛政之苦而想要起义。秦始皇最后留下的,是一个外强中干的帝国。秦朝为了巩固辽阔但各地文化不一的帝国,在政治、军事、经济、交通方面,都推行政策与改革。政治制度是以皇帝制度、三公九卿制与郡县制为基础的中央集权制。君主专制的皇帝握有最高权力,丞相仅仅是辅佐,而法律基本延续秦国法律。为了降低各地区差异,秦始皇推行书同文(以小篆为文字)、车同轨、统一货币(秦半两)与度量衡。军事上,兴建长城以巩固北方,毁六国长城与城敦等防御工事,没收天下百姓武器,铸成十二金人,先后五次东巡以安定关东各地区。交通方面,兴建驰道与灵渠等交通设施以便军队前往各地平乱,也有利各地区运输物资。经济方面,秦朝延续商鞅变法的政策。推行重农抑商,鼓励农民增产粮食,甚至有机会获爵位。工商业方面实行盐铁专卖,但不完全禁止商业,也鼓励如经营畜牧业的乌氏倮与丹砂的巴地寡妇清等商人。政" +521,"定武关,入关中。此时赵高担任丞相,因畏惧刘邦,命阎乐杀秦二世,改立子婴为秦王。同时,刘邦军于蓝田击溃秦军,驻军霸上。秦王子婴杀掉赵高-{}-后于轵道向刘邦投降,秦朝亡。刘邦入主关中后,废除严苛的秦法,与百姓约法三章,受秦人拥戴。前206到前202年刘邦与项羽之间爆发了争夺天下的战争,史称“楚汉之争”,最后刘邦取胜。 === 漢朝 === 汉朝是中國第一個在文治武功與國際聲望達到極為輝煌的時代,也是中國歷史分野上被視為上古時期轉折到中古時期的第一個朝代,具有承先啟後的重要關鍵地位及代表性。由於中間歷經王莽篡漢的短暫中斷過程,所以又分為西汉(又稱前漢)及东汉(又稱後漢)。 前202年汉高祖刘邦正式称帝,立国号“汉”,西汉建国。汉高祖刘邦登基后,采用叔孙通的建议,恢复礼法,设三公和九卿,任用萧何为丞相,采取与民休息、清静无为的黄老治术,鼓励生产,轻徭薄赋。称帝之后,异姓诸侯王势大,因此汉初几年对异姓王一一分别翦除,改封刘氏宗亲为王。后刘邦与功臣斬白马立盟誓:“非刘氏而王者,天下共击之”,史称“白马之盟”。汉初政府官员基本上由大大小的功臣充任,从而形成汉初“布衣卿相”的政治格局。此时,由于秦末" +522,"是休养生息的政策。 景帝即位不久,就采纳晁错的建议,削夺诸藩王的封地,引发各封国的担忧,而在汉景帝三年正月(公元前154年)爆发了“七国之乱”,汉景帝因此杀了晁错,以寄达到平息叛乱的目的。但是七国之乱并未因此缓和,景帝任用周亚夫出兵平叛,在三月即告弭平,各诸侯国的势力因此极大削弱。七国之乱以后,景帝开始专心打理朝政,据说景帝十分朴素,仁厚爱民。除了平定七国之乱以外,从来没有大规模用过兵,和匈奴的战争始终控制在一定的规模内,依然对匈奴采取和亲政策。在与匈奴和南越国(南粤)保持和平的同时,也在积蓄国力,透过马复令等措施来积极备战;此一时期史称“文景之治”,是中国成为大一统帝国时代以来,第一次被传统历史学家赞誉的时期。 景帝驾崩后,其子刘彻即位,史称汉武帝。武帝在位期间(在位前141年—前87年),采取一系列改革措施,锐意进取,使得西汉的政治、经济及军事整合起来。在政治上,武帝加强皇权,于公元前140年建立年号“建元”,该年就称之为“建元元年”,始开中国以皇帝年号纪年的滥觞,采纳主父偃的建议,施行推恩令,削弱诸侯藩王的势力,从此,诸侯藩王的势力不再能够对中央朝廷构成威胁;后又以诸侯献上的黄金" +523,"强农业生产,实行和籴法,开凿白渠,并创立均输、平准政策,统购统销,干预物价,加强国家在经济中扮演的角色,此一系列政策使汉朝得以避免步入衰败。 汉武帝去世后,钩弋夫人之子,年仅八岁的刘弗陵即位,是为汉昭帝。汉昭帝登基之初,由上官桀、金日磾和霍光三人共同辅政。但是在元凤元年(前80年),爆发元凤政变,上官桀被杀,霍光从此大权独揽。霍光延续武帝在位晚年的政策,对内休养生息,放宽盐铁专卖制度,提高儒生地位。昭帝死后,武帝孙昌邑王刘贺即位。他行为放纵,密谋排除霍光,但反被霍光所废。之后霍光又迎立刘病已即位,是为汉宣帝。本始元年(前73年),霍光还政于宣帝。地节二年(前68年),霍光去世。此后宣帝将霍氏集团一网打尽。但是,宣帝继续奉行霍光的治国路线,大力整顿吏治,劝民农桑,抑制兼并,降低国家在经济中的角色。这段时期,汉朝不仅内政清明,而且对于匈奴继续保持强势,在汉宣帝神爵二年(前60年)设立西域都护以加强对西域三十六国所处地区的联络与控制。经过昭宣二帝的休养生息,国家经济得以从武帝中期以来面临衰退的困境下的明显恢复,史称“昭宣中兴”。 汉宣帝死后,汉元帝刘奭即位,西汉开始走向衰落。元帝“柔仁好儒”" +524,",新朝被劉玄為首的叛軍推翻,同年2月,绿林军拥立汉宗室刘玄作皇帝,恢复汉的国号,年号更始,史称玄汉,劉玄成為更始帝,然而他的無能腐敗,加上叛軍中的綠林軍和赤眉軍混亂,25年赤眉军立刘盆子为帝,史称赤眉汉。随后击败绿林军,更始帝刘玄向赤眉军投降。之后漢朝宗室劉秀乘虛而入。25年劉秀復辟漢朝,定都雒邑,史稱东汉,而他後被稱為汉光武帝。 一般将王莽新朝与更始帝其列入西汉诸帝,但其法统混乱。 === 東漢 === 汉更始三年(25年)六月,原本服从更始帝的汉朝宗室刘秀在鄗县(今河北高邑东南)之南即皇帝位,是为光武帝,灭刘盆子,仍沿用汉的国号,以这一年为建武元年,定都雒阳,史称东汉。即位后,于37年终于消灭赤眉、隗嚣、公孙述等割据势力,统一全国。 汉光武帝废除王莽时的弊政,建武二年(26年),光武帝下令整顿吏治,设尚书六人分掌国家大事,进一步削弱三公(太尉、司徒、司空)的权力,加强中央集权;同时清查土地、新定税制、振兴农业,使人民生活逐步稳定下来;另外提倡儒术,建立太学,设置五经博士,与尊儒并行的便是表彰气节,因此士大夫争相以名节为目标,为淳美的政教,奠下基础;对匈奴等外部势力实行防御的策略,纵" +525,"家政治日益腐败。窦氏的跋扈引发和帝的不满,不久年仅14岁的和帝就抓捕外戚窦宪,外戚势力开始衰弱,和帝信用宦官,从此东汉的政治沦为外戚和宦官两股势力的争斗。不过,和帝仍然在政事上非常勤奋,不失为英明之主。元兴元年冬十二月,年仅廿七岁的和帝病逝。出生仅百日的少子刘隆即位,是为殇帝。殇帝仅在位8个月就驾崩了。接替即位的是清河王刘庆之子安帝刘祜。他即位早期由太后邓绥临朝理政。邓太后勤俭节约,任用贤良,同时对自己家族的势力有所限制,却也对宦官势力纵容。建光元年(121年),邓太后逝世,安帝亲政,将邓氏家族诛杀殆尽。安帝依赖外戚宋氏和阎氏以及宦官的力量。听信奸臣,肆意无忌,朝政昏庸不堪,东汉快速衰败下去。延光四年三月,汉安帝在南巡途中死在叶城。外戚阎氏秘不发丧,拥立汉章帝之孙济北王刘寿之子刘懿,史称汉少帝,其在位仅200余日就病死。少帝死后,阎氏家族密谋再立傀儡,被中常侍孙程击破,阎氏家族被诛杀。孙程迎立济阴王刘保,是为汉顺帝。在顺帝执政早期,宦官势力膨胀,却引发社会各界的反弹。阳嘉元年(132年),贵人梁妠立为皇后,从此梁氏外戚势力开始崛起,梁妠的兄弟梁冀被任命为大将军。汉安二年(143年)八月" +526,"败。 数十年的政争以及天灾动摇了汉朝的民生,终于在184年爆发由太平道教主张角引发的黄巾之乱,黄巾军以“苍天已死,黄天当立;岁在甲子,天下大吉”号召数十万民众参与,虽然叛乱不久即告平定,但是东汉朝廷经此一役已实力大减。关键是中央朝廷为了尽快平叛,又将军政权力下放给各州的州牧。各地豪强从此开始慢慢拥兵自重,加以其原已具有强大经济实力,最终演变成东汉末年众豪强军阀割据一方、群雄逐鹿的局面。汉灵帝死后,外戚大将军何进和宦官双双火并而亡,终于结束戚宦之争。但董卓掌权后,废少帝刘辩为弘农王,改立陈留王刘协,并且焚毁首都洛阳。董卓被吕布诛杀后,经过几年混战,出现把持朝廷的曹操、位于河北的袁绍、位于淮南的袁术、位于江东的孙策、位于荆州的刘表、位于益州的刘焉等势力。 東漢漢獻帝建安二年(197年)袁術在淮南壽春僭號稱帝,國號仲家,並仿效河南尹設置淮南尹。後來曹操在《讓縣自明本志令》中稱,當時袁術因為畏懼曹操,僅僭越天子之制而不敢稱帝。 袁術稱帝後,接連遭到孫策、呂布、曹操、劉備等勢力的叛盟與打擊。後呂布轉而與袁術結盟,但在建安三年(198年)敗亡。建安四年(199年),袁術勢窮,想將帝號讓給擁有四州的" +527,"篡位,迫汉献帝禅让后自立為帝,史称曹魏(220年-266年),至此东汉灭亡,中国历史正式进入三国时期。次年以益州为主的刘备亦以汉室宗亲的身份称帝,国号续为汉,史称蜀汉(221年-263年)。刘备与孙权在赤壁之战后拓展势力,为了荆州问题双方多次发生纠纷与战争,最后刘备在222年的夷陵之战失败,孙权获得荆州绝大部分。刘备于223年病死,辅佐其子刘禅的诸葛亮于同年再与孙权恢复同盟。据有扬州、荆州及交州等地的孙权先于221年向曹魏称臣,受封为大魏吴王,至229年才称帝,国号吴(229年-280年),史称孙吴或者东吴。蜀汉在诸葛亮(前期)和姜维(后期)的领导下多次北伐曹魏,但成果不大,诸葛亮亦在234年病逝于北伐途中。东吴亦多次北伐曹魏,但几乎全部失败,其中较著名的有陆逊北伐。期间也出現公孫淵的燕國政權(237年-238年),但旋即被司馬懿領兵剿滅,最終只是曇花一現。曹魏朝廷在魏明帝曹叡逝世後被顧命大臣大將軍曹爽和太傅司馬懿把持,其中曹爽獨專朝政排斥司馬懿,致使司馬懿於249年發動高平陵之變。在高平陵事件後,曹爽與其同黨被夷三族,其后在魏国淮南地区出现反对司马氏支持曹氏皇室的叛乱,史称淮南三叛," +528,"始南渡。316年,西晋灭亡,北方从此进入五胡十六国时期。 === 五胡十六國 === 46年之后,东汉朝廷常以招引或强制的方式,将边疆的北方各族内迁,以便监控各族或是增加兵源和劳动力。到了西晋时,中国北部、东部和西部,尤其是并州和关中一带,大量胡族与汉族杂住。这些胡族逐渐成为汉人管辖下的编户,由于他们需要纳税,且时时受汉官欺压或受汉人歧视,因此心生不满,时有举兵之事。八王之乱的爆发,使晋廷失去在地方的影响力,胡族陆续叛变。304年,李特子李雄继立后击败罗尚并称成都王,又于306年称帝,国号“成”,338年改国号为“汉”,史称成汉。匈奴刘渊统领五部匈奴,成都王司马颖结其为外援。304年司马颖遭王浚围攻,遣刘渊回并州发兵支援。他乘机宣布独立,称汉王,自称继承汉朝正统。308年刘渊称帝并迁都至平阳,国号“汉”,后称前赵。304年成汉与汉国的建立,开启了“五胡十六国时期”。范围大致上涵盖华北、蜀地、辽东,最远可达漠北、江淮及西域。在入主中原众多民族,在这个范围内相继建立许多国家,传统史家认为这些外族以匈奴、羯、鲜卑、羌及氐为主,统称五胡,将此时期称为五胡乱华。 西晋灭亡后,华北地区战火纷飞,掠" +529,"失土,致使前秦崩解,引发了北方军事和政治格局的变化。然而,东晋后期又发生朋党相争及桓玄作乱。平民负担沉重,又发生孙恩、卢循之乱。谯纵亦据蜀地自立。最后刘裕崛起,平定诸乱,凭借军事力量夺得帝位,进入了南北朝时期。 经济方面,东晋庄园经济占据的比例比西晋更大。由于农业技术提升等因素,在侨姓世族与吴姓世族的衣冠南渡密集开发下,江南获得全面发展而繁荣兴盛,中国的经济中心也逐渐南移,后来才有大运河的出现。此外,手工业和商业方面也有长足进步。晋朝虽为汉末以来中国文化衰落的时期,但在哲学、文学、艺术、史学、科技等等方面也有新的发展。汉代以前,政治主权完全在华夏族,而其他族则作为被统治者而同化;汉代以后,政治主权不全在华夏族,而他族或以征服华夏族者而同化,边疆民族带来的草原游牧文化也融于中原文化。当时天下大乱,东晋士族文人多不以道义为重,使儒学衰落。一些旷达的士人目击衰乱不甘于隐避,就开始做出放逸的事情来,比如清谈,甚至逐渐成为一种社会风气。与此同时玄学开始广泛传播。玄学与印度东传之佛教交汇,使中国文化的儒释道逐渐合为一体。 === 南北朝 === 由420年刘裕篡东晋建立劉宋开始,至589年隋灭南陈" +530,"营八年之后,发兵灭南陈统一中国。 相对于北朝战争不断、各阶级对立的局势,经济持续成长的南朝比较稳定,出现元嘉之治与永明之治等治世。中原人口自黄巾之乱和永嘉之乱就持续南移,带来劳动力与生产技术。江南的繁荣,使得中国的经济南移,在隋炀帝时建立隋唐大运河。文化方面,乱世为思想自由提供沃土肥壤,提出务实求治和无君论等观点。对文学、艺术、科技等开创出独到见解与理论。玄学、佛教与道教都很兴盛。其中佛教带动石窟的发展,敦煌莫高窟、麦积山石窟、云冈石窟与龙门石窟名扬后世。对外交流也很兴旺,东到日本和朝鲜半岛,西到西域、中亚、西亚(埃兰沙赫尔),南到东南亚与印度。 南北朝初期仍是世族政治,社会阶层分为世族、齐民编户、依附人及奴隶。世族拥有大量不需付税的依附人从事生产与作战,影响朝廷的税收。虽然南朝皇帝仍然需要主流世族的拥护,不过也扶持寒门以平衡政治势力,并且在南梁时萌芽出科举制度。南朝世族因为长期安逸而逐渐衰退,在侯景之乱后彻底崩溃。北朝胡族缺乏政治的经验,所以重用汉人世族,引起双方的文化采借,久之形成文化混合,以北魏孝文帝的汉化运动最盛。混合的过程产生激烈的思想冲突、政治斗争或种族冲突,例如六镇之乱、" +531,"而调动了大量的劳动力,一方面又好大喜功、穷奢极欲、連年征戰,使农民不堪重负纷纷起义,導致隋朝迅速滅亡。隋朝國祚只有三十八年,是繼秦朝後的另一强大而短暂的大一統王朝。 === 唐朝 === ==== 空前盛世 ==== 隋朝在大业年间,使得各地民变不止。李渊见天下大乱,617年在太原起兵。不久占领隋都大兴城。李渊拥立杨侑为帝,于同年五月迫使隋恭帝禅位,建国唐朝,即唐高祖。都城大兴改名为长安。李渊建立唐朝后先后平定瓦冈军李密、西北金城的薛举、薛仁杲、武威的李轨、河东的刘武周、宋金刚、洛阳王世充与河北夏帝窦建德。623年辅公祏率杜伏威余部在丹阳反唐,隔年被唐军俘杀,江南平定。624年消灭高开道、辅公祏后唐朝基本统一中原。628年,柴绍率军消灭依附突厥的梁师都,唐朝统一战争最终结束。 626年李世民发动玄武门之变。李渊禅让帝位,成为太上皇。李世民继位,即唐太宗。唐太宗励精图治、纳谏如流,逐渐恢复唐朝的国力。在内政方面,唐太宗推行均田制与租庸调制,提升农业发展。在职官制度上,改良隋朝的制度,形成三省六部和科举选士制,限制皇权发展与贵族世袭等恶习。对外方面,唐太宗采取积极防御、以战止战的策略,以及" +532,"690年废睿宗,即皇帝位,改国号为周,即武周,定都洛阳,改立李旦为皇嗣。武则天成为中国历史上唯一的女皇帝。在武则天掌权与称帝的期间,国家较贞观时期有更大的发展。武则天晚年,705年退位,史称神龙革命。 712年李隆基即位,即唐玄宗。唐玄宗时期可分为开元与天宝两个部分,其中前三十年开元时期的政治比较清明。唐玄宗提出以武、韦为戒,以贞观为榜样,作为执政的指导思想。他先后任用姚崇、宋璟、卢怀慎、张九龄与韩休等贤臣,并且广纳谏言。例如采纳张九龄的建议,将京官中有能之士外调为都督刺史以训练行政能力,又将有为的都督刺史升为京官。增进中央与地方的沟通、了解和信任。裁减武周中宗时期的员外官等冗官,精简机构以便节省开支与提升行政能力。严格执行法律,抑制权贵,就算是皇亲国戚犯罪,也绳之以法。对于稳定社会秩序产生良好的影响。加强执行均田制,打击土豪。发展农业生产,兴修水利,扩大耕地面积,大大提升农业生产力。对外方面,改善与吐蕃、东突厥、契丹与奚的关系,推行和亲政策。听从姚崇与宋璟的建议,充实边防军务,并且避免与外族发生战争。唐玄宗的这些措施使唐朝在开元年间进入全盛时期,人口大量增长,物产丰富,史称开元之治。" +533,",国政日衰。805年唐德宗去世,太子李诵继位,即唐顺宗。唐代宗遗留下来的问题越来越严重,唐顺宗与唐宪宗都企图解决,其中唐宪宗较为成功,实现了元和中兴。唐朝中央的政治大权大多由皇帝与宰相掌控,但在天宝之后转变成皇帝与内廷宦官的联合,外廷宰相变成政治上的二流角色。在唐宪宗到唐宣宗期间,发生较长的党争,即称牛李党争。 846年唐武宗去世,宦官们发生权力斗争,其叔李忱在宦官马元贽的扶持之下即位,即唐宣宗。由于李党失势,李德裕被贬黜到崖州(今海南琼山),至此长达40年的牛李党争结束。唐宣宗雖是由宦官擁立的君主,但即位以后励精图治,加强皇权、抑制宦官权力,是时唐朝又出现短暂的复兴景象,史称大中之治。然而唐宣宗为人多疑苛察,使得上下莫不粉饰太平;他崇奉道教,一直希望能够通过服用丹药来长生不老。859年唐宣宗因服用丹药过度而去世。唐宣宗晚年,国内已有乱象,他死后不久就爆发寇乱。 唐宣宗去世后,相继为帝的唐懿宗与唐僖宗是著名的无道昏君,使唐朝的国势一直走下坡。政治败坏、社会贫富差距过大,不少叛乱相继发生,唐朝经济命脉的江南地区也被破坏殆尽,彻底动摇了这个政权,也产生李国昌、朱全忠等新藩镇。904年朱全忠" +534,"源的女婿石敬瑭的策动下,河北军立嗣源为帝,反攻洛阳。庄宗被伶人郭从谦所杀,史称兴教门之变,此后,李嗣源继皇帝位,是为后唐明宗。 926年,后唐明宗李嗣源即位后,也有相当治绩,朝政渐为安定。但军人安重诲专权,未能处理好与孟知祥、董璋的关系,两人发生内斗,孟知祥取胜,结果为后来后蜀脱离后唐独立埋下了祸根。明宗晚年病倒在床时,秦王李从荣以为明宗已死,起兵企图攻入皇宫,结果事败被杀。明宗得知秦王被杀,震惊之下驾崩,大臣妃子拥立宋王李从厚,是为后唐闵帝。闵帝即位后,采用削藩政策,引起潞王李从珂的叛乱,叛军攻入京师,而闵帝逃往河北,被姐夫石敬瑭设计除去其手下将士,为李从珂军士擒杀,从珂即帝位,是为后唐末帝。末帝与镇守太原的河东节度使石敬瑭不和,936年末帝下诏把石敬瑭调任,引来石敬瑭的叛乱。末帝发兵攻太原,石敬瑭向契丹借兵,辽太宗耶律德光亲率大军南下,唐军大败,937年,契丹与石敬瑭的大军攻入洛阳,1月11日末帝自焚而死,后唐灭亡。 936年,石敬塘建後晉,石敬塘為了獲契丹的支持,把燕雲十六州割讓給契丹,使之後中原的邊防受到嚴重威脅。后晋建国后一直处于动荡中,石敬瑭割地称儿的做法受到许多人的反对," +535,"950年,郭威發動兵變,奪得帝位,建立後周,郭威是为后周太祖。郭威死后由其养子(本身是其内侄)柴荣继位,是为后周世宗。959年,後周世宗柴荣死後,后周恭帝即位,随后960年趙匡胤發動兵變,奪得帝位,建立宋朝,五代结束。 后北宋渐灭全国藩镇,963年灭荆南,965年灭后蜀,971年灭南汉,975年灭南唐,978年吴越国归降,979年,宋太宗消滅北漢,統一中原,至此五代十國時期完全結束。 == 宋辽夏金并立时期 == === 宋朝 === 宋朝是繼唐朝後又一個長達三百年的皇朝,分為北宋和南宋。960年,後周大將趙匡胤發動兵變建立北宋,是為宋太祖,定都汴京(今河南開封市),又稱東京。 北宋時期中國出現印刷術。當時中國經濟發達,中國海上貿易十分興盛,福建泉州市一帶成為繁華的港口,財政收入超過一億兩白銀,首都開封和杭州人口達到100萬和120萬人口,各國商人雲集,文化也極盛,出現程頤、朱熹等理學家,提倡三從四德。與唐詩並駕齊驅的宋詞,有蘇軾、歐陽修、黃庭堅、李綱、辛棄疾、陳與義、陸游、文天祥等詞文優秀的詞人,出現中國歷史上最著名的女詞人李清照,社會文化發達,出現白蛇傳、梁祝等浪漫愛情傳說。 宋神" +536,"宋對抗,經歷大規模血腥戰爭(如襄樊之戰、釣魚城之戰)。1271年忽必烈建立元朝,定都大都(今北京)。1276年,蒙古攻破臨安,佔領南宋大部分領土。1279年,元軍與南宋崖山海戰,8歲的小皇帝趙昺被陸秀夫背著跳海殉國。崖山海戰以元朝的勝利告終,偏安江南一百五十年的南宋隨之滅亡。 === 遼朝(契丹) === thumb|世界最早的火藥制方 遼由契丹族建立。契丹與秦漢時的東胡、魏晉南北朝的烏桓、鮮卑有著淵源關係,遊牧於遼河流域。安史之亂後,唐朝統治衰微,突厥也被回鶻取代,契丹趁機發展,9世紀後期日益強大。契丹軍事首領耶律阿保機於公元907年推翻了聯盟首領遙輦氏,任契丹部落聯盟首領。 916年,耶律阿保機建立契丹國,稱帝建元,即遼太祖,都臨潢府(今內蒙巴林左旗),成為中原最大威脅。公元944年南下中原,946年陷開封,滅後晉,947年改國號大遼。1005年辽朝与北宋签订澶渊之盟,从此宋辽的和平局面长达一百多年。 遼末政治腐敗,完顏阿骨打建金抗遼。遼天祚帝率兵親征,在護步答岡(今黑龍江五常市以西)大敗,金朝連連告捷,中京大定府、西京大同府、南京(燕京)析津府相繼陷落。公元1125年金太宗俘虜遼" +537,"了女真各部。完顏部首領烏古乃死後,各部落分崩離析,爭鬥不斷,完顏部聯盟長完顏盈歌、國相完顏撒改、盈歌之侄阿骨打重新統一,奠定此後金國基礎。完颜阿骨打繼任聯盟長後,獲悉遼國內部虛弱的消息,於1114年起兵反遼,先後攻下寧江州、出河店、賓州、咸州等地。遼天慶五年(公元1115年)完顏阿骨打稱帝,建國大金,都會寧府(今黑龍江省哈尔滨市阿城区南白城子)。公元1125年滅遼。滅遼後,金國將矛盾指向北宋。宣和七年(公元1125年)十一月金軍南下,十二月陷燕京,次年正月兵臨開封。靖康元年八月再次南下,於靖康二年(公元1127年)四月陷開封,俘虜徽欽二帝北去,北宋亡。南宋建立後,金國加緊統一腳步,但在黃天蕩之戰中受挫,郾城之戰大敗,此後宋金雙方都未有突破,金世宗即位後,30年中不再發動戰爭,女真逐漸腐敗,戰鬥力下降,宣宗時,有所謂貞祐之亂,盜賊滿野。蒙古滅西夏後,矛頭指向金國,於公元1211年大舉攻金,迫近中都燕京(今北京市),金軍雖有「震天雷」、「飛火槍」等新式武器,但當時政治腐敗,將領毫無鬥志,於野狐嶺戰役、澮河堡戰役、縉山戰役中大敗,主力被全殲,被迫求和。 公元1214年,金宣宗為避敵鋒芒,由中" +538,"国号诏》,取《易经》中“大哉乾元”之意,将国号由大蒙古国改为大元,建国元朝,忽必烈即元世祖;1260年设立中书省,1263年设立枢密院,1268年设立御史台等等国家机构;设置大司农并且提倡农业;尊孔崇儒并大力发展儒学等推行汉法的政策。然而为了保留原蒙古制度,最后形成蒙汉两元政治。元世祖虽然于争夺汗位战争获得蒙古大汗的汗位,并且最后成为皇帝,但由于汗位取得不合法与崇尚汉法,使得蒙古宗室不承认忽必烈的汗位,四大汗国有三国不奉忽必烈的命令,蒙古帝国完全解体。最后窝阔台系的海都出兵争夺汗位,造成漠北地区动荡不安,史称海都之乱。 1279年3月,張弘範在崖山海战攻灭南宋,陆秀夫身揹8岁的小皇帝宋幼主赵昺投海而死,南宋亡。元朝统一中国,结束自靖康之乱以来百余多年的分裂局面。元朝周边一些国家或地区(包括高丽、安南、占城、缅甸)臣服,加入元朝的朝贡关系。 === 中期的统治 === 1294年元世祖驾崩,元成宗继位。元成宗主要恪守元世祖时期的成宪,任用其侄海山(答剌麻八剌之子)镇守和林以平定西北海都之乱,并且下令停止征讨日本与安南。在内政方面专力整顿国内政治,减免江南部分赋税。然而,由于元成宗过度赏赐" +539,"木儿率军攻入上都,天顺帝不知所终。隔年和世琜于漠北和林称帝,即元明宗。元文宗放弃帝位,派燕帖木儿迎元明宗继位,并且被立为皇太子。然而燕帖木儿毒死元明宗,复位元文宗,改元天历,史称天历之变。 1333年元文宗去世后,仅七岁的明宗次子懿璘质班为帝,是为元宁宗。但元宁宗仅在位不到两个月即去世,不久后燕帖木儿也去世。元明宗的长子妥欢贴睦尔被文宗皇后卜答失里从静江(广西桂林)召回并立为帝,是为元順帝。元朝长期处于继承权问题、贪污、权臣擅政的恶习,使得政治总是无法上轨。这种不稳定的朝廷如果又遇到天灾人祸的干扰,往往导致国家的覆没。元朝后期,特别是1340年代中后期至1350年代期间,干旱、瘟疫与水灾时常发生,黄河地区水患尤其严重。1351年红巾军的起事,揭开元朝灭亡的序幕。 === 民变与灭亡 === 元廷派兵镇压各地红巾军,丞相脱脱亲自率军南下攻陷徐州芝麻李军,一度压制民变军。然而脱脱在1354年南攻高邮张士诚军之际,被元廷大臣弹劾而功亏一篑。徐寿辉部最后分裂成两湖的陈友谅与四川的明玉珍。两淮郭子兴的部下朱元璋于1356年以南京为根据地开始扩充地盘;1363年与据有两湖的陈友谅作战,最后于鄱阳湖" +540,"起义,攻占濠州(今安徽凤阳)。不久,朱元璋投奔郭子兴,屡立战功,得到郭子兴的器重和信任。之后,朱元璋离开濠州,发展自己的势力。1356年朱元璋率兵占领集庆(今江苏省南京市),改名为应天府,并攻下周围一些军事要地,获得一块立足的基地。经过几年努力,其军事和经济实力迅速壮大。1359年,陳友諒挾持徐壽輝,遷都江州(今江西九江),自立為漢王。次年(1360年)閏五月十六日,其在採石五通廟登基稱帝,國號漢,改年號為大義,建立陳漢政權。1363年,朱元璋通过鄱阳湖水战,陳友諒势力被消灭。1367年朱元璋自称吴王,率军攻下平江(今江苏省苏州市),灭张士诚,同年又消灭割据浙江沿海的方国珍。 1368年正月,朱元璋于南京称帝,即明太祖,年号洪武,明朝建立。同年攻占元大都,元朝势力撤出中原,在全国的统治结束。之后1371年消灭位四川明玉珍势力,1381年消灭据守云南的元朝梁王。最后,于1388年深入漠北进攻北元。 === 前期的巩固 === thumb|150px|山西海会寺雙塔,分别是高約20餘米的宋代樓閣式磚塔,和高約50米明代樓閣式磚塔 明太祖洪武帝即位后一方面减轻农民负担,恢复社会的经济生产,改" +541,"亚、阿拉伯半島和東非地区。其规模空前,扩大明朝对南洋、西洋各国的影响力。文治方面,明成祖修大型类书《永乐大典》。1405年明成祖将北平改名北京,1421年迁都。因为永乐年间天下大治,并且大力开拓海外交流,所以有学者将这段时期称为永乐盛世。但是明成祖同样对异议者大肆杀戮,诸如黄子澄、齐泰等明惠宗旧臣等都被杀。 明成祖驾崩后,其长子明仁宗,年号洪熙。洪熙帝即位時47歲,年龄已经偏高,即位仅一年就驾崩。其统治偏向保守固本,任用“三杨”(杨士奇、杨荣、杨溥)等贤臣辅佐朝政,停止郑和下西洋和对外战争以积蓄民力,鼓励生产,宽行省狱,力行节俭。明仁宗驾崩后长子朱瞻基即位,是为明宣宗,年号宣德。宣德元年,皇叔漢王朱高煦發動叛亂造反,企圖效法父親永樂帝爭奪帝位未果,被宣德帝平定。宣德帝基本继承父亲的路线,实行德政治国,并讓鄭和最后一次下西洋。明宣宗同样热爱美术,有画作传世。但是,其执政期间也并非毫无弊端。由于明宣宗喜好养蟋蟀(古名“促织”),许多官吏因此竞相拍马,被称为“促织天子”。同时,宣德帝打破洪武帝留下的宦官不得干政的规矩,一些太监如王振等人开始干政,为明中葉的太监专权埋下隐患。1435年明宣宗去" +542,"與其八虎集團、麥福、黃錦、陳洪,內閣首輔楊廷和、夏言、嚴嵩、徐階之間,內閣與宦官的政治鬥爭,明朝逐漸中衰,社會矛盾萌發。 1556年陕西省华县发生约8级的大地震,是中国历史上人口稠密地区影响广泛和损失惨重的著名历史地震之一,这次地震导致大范围的山体倒塌,河流改道,人员死伤极为惨重(死亡83万人)。同时也导致明朝国库连续两年亏空,地震引发的自然灾害和瘟疫导致明朝政府税收减少,对明朝的国力和财政状况亦造成不同程度的影响。 隆慶帝和萬曆帝初期,在內閣首輔高拱和張居正及宦官馮保的輔政之下曾一度中興,國勢鼎盛,此時銀錢透過國際貿易流入中國,明朝經濟達到全盛。萬曆年間,日本太閤豐臣秀吉發動朝鮮之役,使明朝投入大量的兵力與軍費去清剿日軍,國力因而消耗甚烈,但使朝鮮重新處於明朝的控制。其後而明神宗因為寵幸郑贵妃而引發國本之爭,開始怠政而不上朝理政,政治日益腐化。 === 衰败与灭亡 === 明光宗由明神宗手上接過天子的寶座,卻在一個月間突然駕崩,由其子天啟帝繼承皇位。天啟帝熹宗喜歡木匠工作,任用宦官魏忠賢來分擔朝廷的業務,魏忠賢與其手下的閹党把持朝政,造成東林黨爭,政治更加腐敗,國勢更衰。 1626年" +543,"1410年黃河氾濫事件。崇祯元年至十四年(1628-1641年)-{zh-hans:里;zh-hant:裡}-,有11年黄河发生决口,史不绝书。 == 清朝 == === 滿人的兴起 === 明朝万历初年,董山的后代觉昌安与其子塔克世偕同明朝辽东总兵李成梁,以建州右卫王杲叛乱为由攻灭王杲与其子阿台,然而觉昌安父子也在混战中死亡。1586年明廷袭封塔克世之子努尔哈赤为指挥使作为补偿。 努尔哈赤以祖、父遗留的十三副遗甲崛起,统一建州女真后陆续并吞女真各部,并与漠南蒙古友好。1616年努尔哈赤在今中国东北地区建国称汗,建立后金,定都赫图阿拉(即兴京,今辽宁新宾)。建州女真国力日盛,1616年努尔哈赤在建立八旗制度后于赫图阿拉称汗立国。两年后他以“七大恨”为由起兵反明。努尔哈赤在1619年的萨尔浒之战中,击败杨镐指挥的明军、朝鲜与叶赫联军;接连占领沈阳、辽阳、抚顺等辽东城市,随后战事集中于辽西地区。努尔哈赤最后于1626年的宁远战役中被袁崇焕的红衣大炮击败,不久逝世。 第八子皇太极历经权力斗争后继位。皇太极即位之后,针对努尔哈赤时期的社会矛盾进行了一系列改革,史称“天聪新政”。还继续完善和扩大" +544,"台湾台南),郑克塽率领大臣降清,至此明郑亡。 清朝平定三藩后进入康雍乾时期,这段时期是清朝发展的高峰时期,称为康雍乾盛世。康熙帝为政宽仁,留心民间疾苦,在他亲政不久后,便宣布停止圈地,放宽垦荒地的免税年限。他还着手整顿吏治,恢复京察、大计等考核制度。受康熙帝的“滋生人丁,永不加赋”政策以及外来农作物的引进等影响,清朝人口大大提升。他先后任用靳辅和于成龙治理黄河与大运河。康熙中期以后,因战乱而遭到严重破坏的手工业逐步得到恢复和发展。为安定社会秩序,他颁行十六条圣谕,要地方人士循循告诫乡民。他又派心腹包衣(即家奴)如曹寅、李煦等人打探地方物价、人民收入与官绅不轨之事,并以密褶奏报。此即密折制度的萌芽,到雍正时期趋于完善。康熙帝重视对汉族士大夫的优遇,他多次举办博学鸿儒科,创建南书房,并且向来华传教士学习西方科学与文化。 1690年至1697年间乌兰布通之战与三征噶尔丹使噶尔丹战死,创立多伦会盟以保护喀尔喀蒙古。青藏地区的和硕特汗国协助黄教的达赖五世,击败红教统一全藏,之后分裂成青海与西藏和硕特。达赖六世时,藏区政事交由第巴(理事大臣)桑结嘉错管理,他联合准噶尔对抗西藏和硕特的拉藏汗,拉藏汗" +545,"的三分之一。江南与广东等地的丝织业与棉织业都很发达,景德镇的瓷器都达到历史高峰。与此同时,银号亦开始在山西出现。乾隆帝鸿讲学术,此时期有许多书籍出版,1773年更下令编纂《四库全书》,与《古今图书集成》成为全世界最庞大的类书,这些都成为盛世的文化标志。 对外方面,乾隆十七年(1752年)冬,达瓦齐袭夺准噶尔汗国汗位,阿睦尔撒纳在随后的内斗中被击败归附清廷。乾隆皇帝乘机于乾隆二十年(1755年)派其为引导,以定北将军班第率军平定准噶尔,攻下伊犁。乾隆帝派兆惠西征,阿睦尔撒纳战败逃亡哈萨克汗国,后因为哈萨克汗归降于乾隆,又逃亡沙俄,阿睦尔撒纳于乾隆二十二年(1757年)病死,天山北路遂告平定。然而在天山南路,回部领袖大小和卓兄弟起兵反清,称大小和卓之乱。乾隆二十三年(1758年)乾隆帝再命兆惠西征,兆惠与富德最终攻灭大小和卓,并让帕米尔高原以西的中亚各国成为藩属国。乾隆末年,尼泊尔的廓尔喀王国两次入侵西藏。1793年清廷派福康安与海兰察领兵入藏,击退廓尔喀入侵,不丹与哲孟雄(今锡金)亦为藩属国,加强驻藏大臣的权力。西南,乾隆初年派张广泗平定贵州苗民之乱,随后清军前往平定大小金川之役。期间缅" +546,"死。台湾天地会领袖林爽文于1787年发动林爽文事件,历时一年多。在乾隆年间,平定大小金川之乱、消灭准噶尔汗国等各威胁,将新疆正式重新纳入中国版图,并且颁布被视为西藏属于中国领土的最有力的证据《钦定藏内善后章程》二十九条,加强中央政府对西藏的管治,影響现代中国的版图。 1796年乾隆皇帝禅位于嘉庆皇帝。乾隆帝虽为太上皇,但依然“训政”至1799年去世掌權64年,嘉慶四年一月三日去世,終年八十九歲。嘉庆帝方得以亲政。然而嘉庆帝未能解决弊端,在乾隆帝驾崩後,清朝继续走向衰退。1820年嘉慶帝駕崩,秉國二十五載,由道光帝繼位。 === 近代的衝擊 === 到了19世紀初,清朝已經走向衰落,在嘉慶年間先後爆發川楚教乱,與此同時海上強國英國、荷蘭與葡萄牙等紛紛開始強制與中國貿易。1787年,英國商人開始向華輸入鴉片,導致中國的國際貿易由巨额順差變為巨額逆差。清廷於1815年頒佈搜查洋船鴉片章程,然而英商無視禁令依然走私大量鴉片,道光皇帝于1838年派林則徐赴廣州禁煙。1839年6月,將237萬多斤鴉片在虎門銷毀,史稱虎門銷煙。英國政府因此於1840年6月發動鴉片戰爭。1842年,英軍進入長江,切斷" +547,"軍、上海小刀會起義、甘肅同治回乱等。 晚清時期,代表大清帝國的正式國旗被稱為黃龍旗,也被稱為黃底藍龍戲紅珠圖。在第一次鴉片戰爭之後,中國開始與西方國家有更多的交往,逐漸開始接觸國旗的概念。自1862年開始,斜幅的黃龍旗開始被用作官方船隻的標誌。李鴻章在同西方列强谈判、签约、通商、互派外交人员等外交活动中,看到西方列国庄严悬挂国旗,而中国却无旗可挂,於是在1888年李鴻章制定了《北洋海軍章程》,章程寫道:「按西洋各國,有國旗、兵船旗、商船旗之別。大致旗式以長方為貴,斜長次之。同治年總理衙門初定中國旗式,斜幅黃色,中畫飛龍,係為僱船、捕盜而用,並未奏明為萬年國旗。今中國兵商各船日益加增,時與各國交接,自應重訂旗式,以崇體制。應將兵船國旗改為長方式,照舊黃色,中畫青色飛龍。軍旗提督用五色長方旗,諸將用三色長方旗,上角飾以錨形。」這標誌著中國歷史上首次在正式文書中出現了 國旗 的概念。1889年天津軍械局完成了設計樣稿和營造法則:旗為羽紗質地,正黃色,用羽紗鑲嵌青色五爪飛龍,龍頭向上,分為大小四號,頭號橫長一丈五尺六寸,寬一丈六寸五分;二號橫長一丈三 尺九寸,寬九尺五寸;三號橫長一丈一尺五寸," +548,"始獨自視事聽政。1877年清軍收復新疆,1881年通過《伊犁條約》清軍收復被沙俄占據多年的伊犁。中法戰爭後清朝還建立當時號稱亞洲第一、世界第八的近代海軍艦隊—北洋水師。1895年中日甲午戰爭的失敗,暴露出清政府的软弱无能。此后,列强纷纷强租土地,意图瓜分中国。1898年,光緒帝在親政後同意康有為、梁啟超等人提出的變法主張,從6月11日到9月21日的被稱為百日維新的103天中進行多項改革,但最終在慈禧太后發動政變後失敗落幕。 1899年,義和團運動爆發,以“扶清滅洋”為宗旨並在慈禧太后包庇下烧杀淫掠北京和默許下開始圍攻外國駐北京使館。同时兵权落入亲族手中险些引发政变。於是,各國以解救駐京使館人員和外使人员被杀的理由攻入北京,史稱八國聯軍。1900年7月俄軍以義和團破壞東清鐵路為由,在海蘭泡和江東六十四屯地對中國人進行大規模屠殺,造成約7000人遇難,海蘭泡慘案和江東六十四屯慘案合稱為庚子俄難。1901年,清政府被迫與各國簽定辛丑条約,賠款4.5億兩白銀,分39年還清(本息合計9.8億兩),后来均全额归还,同時從北京到山海關鐵路沿線由各國派兵駐紮,開北京東交民巷為使館區,中國人不得入內等。" +549,"基人何子淵、丘逢甲等人開風氣之先,排除頑固守舊勢力的禁錮,成功引進西學,推广新式學校。清政府迫於形勢壓力,對教育革新網開一面,於1905年末頒布新學制,廢除科舉制,並在全國範圍內推廣新式學堂。1909年,地方科舉考試停止以後,西學逐漸成為學校教育的主要形式。正是教育方式的這一根本性轉變,為後來風起雲湧的辛亥革命和國家建設培養造就了大批思想進步、銳意創新的社會精英和寶貴人才。1906年,清政府同時筹划立憲運動,於1911年成立為人詬病的皇族內閣,以慶親王奕劻為內閣總理大臣。 1905年中國同盟會成立以後,革命黨開始興起,孫中山和黃興等人在海外積極籌款,組建黨組織,指揮國內的多次革命運動。經過十餘次失敗的革命起事後,與革命黨互不溝通的湖北新軍在武昌起事獲得成功。清廷迫於情勢於11月任命袁世凱為內閣總理大臣,以便討伐革命黨人。袁世凱一面以武力壓迫南方革命,另一方面暗中與革命黨人談判,換取利益,而革命黨人亦認可袁世凱是能領導中國的有力人選,遂行謀定,是為南北議和。1912年1月1日,中華民國在南京宣告成立,孫中山正式就任首任臨時大總統。2月12日,清宣統帝溥儀宣佈退位,清朝正式走進歷史,中國兩" +550,"故又稱北京政府。 袁世凱上台後不久,逼迫國會選他為大總統,之後就解散國會和終止臨時約法,另頒布新約法,以擴大其職權一步步籌備帝制,1913年7月又將蔡鍔召入北京控制,且在一番裝模作樣後使馮國璋和大眾誤以為袁世凱並無稱帝之心,但1916年袁世凱宣稱恢復帝制,建立中華帝國。孫中山組織中華革命黨討伐是為二次革命,同年3月,袁世凯被迫宣布取消帝制,6月在绝望中死去,护国战争结束。袁世凱死後黎元洪繼任總統之位,段祺瑞則成為國務總理,但兩人卻因為意見不一而交惡,不久段祺瑞擅自向德國宣戰,黎元洪將段祺瑞撤職,但段祺瑞卻率軍討黎,黎元洪不得不向張勳求助,1917年6月7日張勳卻率五千名辮子軍攻入北京並扶植溥儀為帝,黎元洪又向段祺瑞求助,段祺瑞便用12天討平復辟,黎元洪也辭職。中華民國進入北洋軍閥各派系(皖系、直系和奉系等)控制中央政府時期,地方政府則由各個軍閥派系割據。 === 第一次世界大戰 === 1917年8月14日,段祺瑞領導的北洋政府和中華民國第一屆國會經過長時間的辯論,最終通過了《對德宣戰案》,中華民國宣布对德意志帝國、奥匈帝國宣战,废除中德、中奥条约,收回天津,汉口德奥租界,並援助協约國" +551,"力爭取無效。隨後爆發了五四運動,抗議巴黎和會上有關山東問題的決議,最後北洋政府代表團6月28日拒絕在《凡爾賽條約》上簽字。直到1921-1922年的華盛頓會議後,日本才將膠州灣地區和膠濟鐵路全部移交給中華民國,並從山東撤出所有軍隊,中日雙方才解決山東問題。 === 外蒙古獨立 === 1921年達木丁·蘇赫巴托爾在苏联红军的帮助下建立“人民革命政权”,1924年蒙古人民革命黨宣布废除君主立憲制,成立蒙古人民共和国,定都庫倫,改城名为乌兰巴托。1937年,在霍尔洛·乔巴山的领导下,外蒙古开展了大清洗,党和国家、政府、軍隊、僧侣当中有两到三万人被杀,占当时外蒙古人口的百分之二到百分之三。1946年外蒙古獨立。1952年中華民國政府在聯合國大會提出「控蘇案」並得到通過意圖阻撓蒙古人民共和國加入聯合國;1961年,蒙古人民共和国成为联合国会员国。苏联解体以後,蒙古国于1992年2月12日大呼拉尔放弃社会主义制度,推行多党制和总统制政治体制,实行议会制。新宪法当日生效,国名改称为“蒙古国”。 === 國民政府時期 === 孫中山之後多次發起護法運動與護國運動,組建廣州軍政府,試圖聯合南方軍閥推翻" +552,"西北辦事處改組為陝甘寧邊區政府。 === 第二次世界大戰 === 1931年9月18日,日本發動九一八事變,出兵佔領東三省,滿洲國于次年成立,日本扶植溥儀成立的第一個傀儡政權。1936年12月12日,西安事變後國共結束十年內戰,第二次国共合作初步形成。1937年7月7日,日本發動盧溝橋事變,中國抗日戰爭全面爆發。12月13日南京淪陷,日军在南京进行大屠殺,超过30万人被杀害。之前國民政府已從南京遷至漢口,後來遷至重慶。而在1940年至1945年間:在日本支那派遣軍扶持下,以汪精衛、周佛海、李士群等為主要成員,在南京建立所謂的“中華民國國民政府”,即“汪精衛政權(日本軍扶植的傀儡政權)”,對淪陷區實行賣國日偽統治。不過,當時已經遷都重慶(戰時首都)的中華民國國民政府,即重慶國民政府,自始至終並不承認“汪偽政權”。 抗戰期間,蔣中正統帥國民革命軍與日軍22次大會戰,國民革命軍陸軍死亡、失蹤321萬人,海軍幾乎全滅。1945年,第二次世界大戰結束後,中國從日本手裡接管台灣及澎湖列島以及其他一些領土,廢除百年不平等條約,而且得以成為联合国安全理事會5個常任理事國之一,號稱“世界五強”之一。然而" +553,",但仍實施於1950年地方公職人員選舉,此舉為養成民眾的民主素養。1975年蔣中正去世後,由蔣經國接掌政權。隨著產業從農業發展到輕重工業、國民教育普及,雖然台灣在清末以及日本統治時期有引入基本的現代化,但在1970年代才進入經濟起飛時代,蔣經國總統所推動的十大建設至今仍被視為促進臺灣經濟起飛的基本工程。1960年代和1970年代左右,臺灣轉向高技術領域,經濟成就與知識方面的突破造就一整代的中產階級,導致1980年代中後期,臺灣的威權社會控制體系開始鬆動。 1986年9月28日,政府遷臺後第一個反對黨民主進步黨成立。1987年7月14日,國民黨政府正式宣告解嚴,終止黨禁、報禁,解除長達39年政府對中華民國社會的嚴密控制,不久蔣經國去世,李登輝接任總統,臺灣邁入民主化的時代。1991年4月30日,中華民國政府宣佈廢止動員戡亂時期臨時條款,使得臺澎金馬單方面解除對大陸的積極軍事對峙(即正式以成文方式宣告放棄反攻大陸政策)。之後,憲法多次增修後,1996年舉行第一次總統公民直選,由中國國民黨籍的原總統李登輝當選連任;2000年,中華民國第二次總統民選,由在野黨民主進步黨所推舉的陳水扁當選,此次" +554,"下階層的勞工和婦女等群眾中。 1950年代开始的土地改革运动将地主的土地没收,从政治和经济上彻底消灭了中国自秦汉以来作为地方政治精英的乡绅阶层,改变了两千多年来“皇权不下县”的局面,国家政权的动员力从此深入村镇基层。这次土地改革是中国共产党在第二次国共内战时期对老解放区土地改革的继续,基本完成了全国范围内的土改,至1953年,除新疆、西藏、青海、川边等少数民族地区,中国大陆大部分地区的土地改革基本完成,3亿多无地或少地的农民无偿分得约7亿亩土地及生产资料,并每年免除地租3000万吨粮食。但是从1953年开始,中共开始实行土地集体所有制,强制实施农产品统购统销政策和农业合作化运动,再次剥夺了农民的土地。学者估计土地改革造成100万-470万人死亡,导致农村精英阶层的消亡。受害者中很多是掌握知识和了解经济规律,以地租为生的地主。与此同时,三反五反运动展开,造成了诸多冤假错案。 中華人民共和國成立初期,支持中華民國的部隊到處與解放军和新建的地方政权對抗。中华人民共和国政府在1950年3月开展全国性的“剿匪行动”,动员超过了一百五十万的军队。随着“剿匪行动”的开展,数年之后,中华民国政府在大陆" +555,"认为中国已进入社会主义初级阶段,社会主义制度(经济方面)在中国基本建立。1953年到1957年中国实施發展國民經濟的第一个五年計劃。培養了大量的技術專家,產業工人,提高了國民收入,並且通過蘇聯和東歐技術的引進提高了解放軍的現代化水平。1954年9月20日,第一部《中华人民共和国宪法》(即五四宪法)在第一届全国人民代表大会第一次会议上通过,共4章106条。 ==== 大量政治化運動 ==== 与此同时,各类政治运动逐渐展开。1955年7月1日,中共中央發出《關于展開鬥爭肅清暗藏的反革命分子的指示》,在中國大陆各省、市、自治區、人民解放軍和中央一級黨的、政府和群眾團體的機關開始肅清一切暗藏的反革命分子。8月25日,中共中央發出《關于徹底肅清暗藏的反革命分子的指示》,毛澤東將各團體中鎮壓的比例定為百分之五。1957年,反右运动展开,上百万知识分子和民主党派人士等遭到批斗和迫害,中国大陆的民主制度遭到严重破坏。1958年,循化事件爆发导致数百人死亡;1959年,爆发藏区骚乱,造成约8万7千人死亡,第十四世达赖喇嘛流亡海外。中國於1950年代末基本消滅血吸蟲病,但是近年又有死灰復燃趨勢。1957" +556,"南驻马店爆发了二十世纪世界最重大技术灾难“河南“75·8”水库溃坝”,造成29个县市被淹,24万人死亡。 1976年,周恩來、朱德、毛澤東先後去世;其後四人幫在懷仁堂事變中被逮捕,國務院總理華國鋒接替毛澤東的領導地位,出任中國共產黨中央委員會主席,成為最高領導人。儘管華國鋒停止文革中的文攻武鬥等混亂局勢,但提出「兩個凡是」的路線,在國家實質上仍然沒有完全脫離文化大革命階段的困頓狀態。 毛泽东在其任内有许多人遭到政治迫害及大清洗,尤其在他晚年,由于严重的政策错误,给中国精神文化领域造成巨大的损失。毛泽东时代历经了建政初期1950-1953年的三大运动“抗美援朝、土地改革运动、镇压反革命运动”、1951-1952年的三反五反运动、1957年的反右运动、1958年-1962年的大跃进、1966-1976年的文化大革命,据各方估计造成4000万-8000万人的死亡,亦有数千万人遭到批斗和迫害。其中,文化大革命因给国家和人民带来巨大的灾难而被严厉批评和谴责,中国传统文化及道德沦亡,法制和宪政被严重破坏,文革还影响了柬埔寨的红色高棉大屠杀。 === 改革开放时期 === ====鄧小平撥亂翻正==" +557,"共第三代中央領導接掌政權,改革開放的步伐一度有所停滯,直到1992年鄧小平南巡之後才得以繼續進行。1997年,中華人民共和國政府從英國接收對香港的主權,其後1999年從葡萄牙接收澳門。 江泽民时期,中国经历了1997年亚洲金融危机,以及百年不遇的1998年洪灾、造成数千人死亡。 2002年中共十六大後,以胡錦濤為總書記的第四代中共中央領導集體上台。中共逐漸放棄「韜光養晦」的外交方針,在外交舞臺上動作頻繁,並強化國際地位,並于2008年末派出護航艦隊進入亞丁灣附近海域,以維護中國在該地區的海上利益。此時中國經濟改革已經有顯著效果,人均生活水平大幅提高,外匯儲備世界第一,被譽為「世界工廠」,成為世界上最有發展潛力的國家之一。 雖然经历了SARS病毒帶來的衝擊,2003年中国經濟成長還是達到近幾年來最高的9.1%。2002年底,南水北调工程开始建设。但是經濟發展依然面對深層的問題,包括東西部發展不平衡、貧富差距拉大等問題。胡温政府提出幾項措施,包括繼續實施西部大開發、振興東北老工業基地,以及關注弱勢群體。加入世界貿易組織後的中國繼續開放其市場,雖然還是受到沒有完全履行入世承諾的指責。不過,多" +558,"的综合性世界博览会,也是首次由发展中国家主办综合性世博会,共有256个国家和地区及国际组织参展。 2007年以来,中国境内的恐怖主义事件明显增多。新疆维吾尔自治区发生多次恐怖性质的袭警、爆炸案件,包括2008年的喀什袭击事件、库车爆炸案和2011年的和田7·18严重暴力恐怖事件、喀什暴力恐怖袭击事件。此外,也发生针对民航班机的恐怖事件,如南航班机三·七爆炸未遂案(2008年)和天津航空7554号班机劫机事件(2012年)。大多恐怖事件发生在中国大型活动(如2008年北京奥运会)举办前期,在国内外造成巨大反响,如2008年昆明公交车爆炸案和在新疆发生的数起事件。2012年,王立军事件、薄熙来事件爆发,受到广泛关注。 缩略图|四川汶川大地震(2008年5月12日) 2008年5月12日,四川省发生汶川大地震,共造成69,227到300,000人死亡,374643人受伤,17923人失踪。是中华人民共和国成立以来破坏力最大的地震,也是唐山大地震后伤亡最惨重的一次。地震造成四川、甘肃、陕西等省的灾区直接经济损失共8451亿元人民币,灾区的卫生、住房、校舍、通讯、交通、治安、地貌、水利、生态、少" +559,"行,是海峡两岸(即中国大陆与台湾)自1949年政治分立以来,双方最高领导人的首次会晤。2018年起,新一轮的中美贸易战开打。2020年5月起,中国和印度爆发边境冲突,造成人员伤亡。 香港主權移交中国后,部分香港人不满意中央政府对香港选举制度的干涉,要求“真”普选,即有“公民提名”的选举。2014发起佔领中环运动(雨傘革命)要求全国人民代表大会撤回决议。佔領行動於2014年12月15日結束,歷時79日。2019年,香港爆发了全球瞩目的“反对逃犯条例修订草案运动”,简称“反送中”,香港警察與示威者雙方的暴力行动使得香港的安全与法治遭到严重破坏,旅游业接近崩溃,香港地区经济严重受挫。 2018年,中国共产党中央委员会提议第十三届全国人民代表大会修改宪法删除国家主席连任不得超过两届的限制。习近平的中共中央总书记和中央军委主席两个掌握实权的职务本身已经没有任期限制,废除国家主席连任限制,意味他可以成为继中国共产党中央委员会主席毛泽东之后的终身领导人。有學者和傳媒認為習近平的做法有如仿效袁世凱「稱帝」自邓小平时代开始实行的“废除干部领导职务终身制”被习近平打破。同时“袁世凯”、“复辟”等词汇被网络" +560,"防疫政策在中共二十大後仍會持續,至少到2023年第十四屆全國人大召開,完成領導班子交接後才有轉變的可能。10月11日,人民日报在第二版刊登署名仲音的评论员文章,强调“动态清零”可持续而且必须坚持。美國之音認為這標志著中共20大结束后中国的防疫政策可能适度放松的幻想彻底破灭。2022年10月中共二十大召開時,習近平在二十大報告中提出堅持動態清零不動搖。在之后的中共二十届一中全会上,习近平再次连任中共中央总书记和中央军委主席。 2022年11月底,多地爆发反对动态清零政策的抗议活动,为1989年以来中国大陆规模最大的抗议运动。12月7日,中国国务院发布疫情防控“新十条”,中国大陆事实上放弃动态清零政策。12月26日,中国国家卫健委宣布将“新冠肺炎”改称为“新冠感染”。 2023年3月10日,習近平在十四屆全國人大一次會議上以全票第三次當選為中華人民共和國主席,是中華人民共和國歷史上第一位任期超過十年的國家主席,任期超越包括毛澤東在內的歷任國家主席。外界认为领导人终身制已经复辟,习近平很有可能终身执政。 在此同時,中國大陸地區也面臨巨大的挑戰,主要表現在全球金融危機、單一依賴外貿出口的經濟發" +561,"調查,僅有2.4%的人認為自己是中國人,有67%的人認為自己是台灣人,認為自己既是台灣人也是中國人則有27.5%。政大民調:台灣人認同感67% 創歷年新高 ,中央社記者林克倫台北3日電,2020-07-04 近來中華人民共和國政府面對中華民國主權相關議題,態度轉趨強硬,稱臺灣是中華人民共和國領土的一部分,作為其一個中國的定義,並以“和平統一”為主軸,“一國兩制”作為處理臺海兩岸議題的框架,意欲將臺灣地區歸中華人民共和國統治;中華民國政府方面,中國國民黨及其他泛藍陣營堅持一個中國及九二共識;民主進步黨以及臺灣團結聯盟強調臺灣本土意識,否定有九二共識的存在。 == 參見 == * 中國歷史年表 * 朝代歌 * 中国历史事件列表 * 诸侯会盟 * 中国君主列表 * 中國公主列表 * 中国皇后及妃嫔列表 * 中國宰相列表 * 中华人民共和国历史年表 * 中華民國時期君主列表 * 汉学 * 中華文明 * 中华文明五千年说 * 劍橋中國史 * 中国古代题材电视剧列表 * 中國總統 * 中國總理 * 華夏文化 * 天下 * 中国行政区划 * 中国北方与南方 * 中国朝代 * 夏商周斷代工程 * 中國" +562,"引用 === === 来源 === # # # # # # # == 相關著作 == * 《二十四史》(正史) * 《國史要義》柳詒-{徵}- * 《國史大綱》錢穆 * 《中華五千年史》張其昀 * 《中國人史綱》柏楊 * 《中國大歷史》黄仁宇 == 外部連結 == * * *中國歷史概述 *中國歷史時間表 * 欧立德:〈传统中国是一个帝国吗?〉. * 许倬云:〈东周到秦汉:国家形态的发展〉. * 王明珂:〈重建中国史观-从中原到边疆:对典范中国史的反思〉. China Category:文明" +563,"州市,简称亳,古称谯,是中华人民共和国安徽省下辖的地级市,位于安徽省西北部。市境西南邻阜阳市,东南接淮南市,东毗蚌埠市、淮北市,北依河南省商丘市,西北达河南省周口市。地处黄淮平原腹地,全境地势平坦。涡河自西北往东南斜贯市境,还有茨淮新河、北淝河、西淝河等河流。全市总面积8,521平方公里,常住总人口約496万人,亳州是国家历史文化名城。2019年,国务院正式将亳州市纳入长三角一体化区域。市人民政府驻谯城区希夷大道588号。 亳州盛产药材、白酒、烟草以及小麦等粮食作物,属温带季风气候,是中国大陆最大的中草药集散地,古井贡酒的产地,素有“中华药都”的美称,而且一年一度的“药交会”更是享誉全国。 == 历史 == 亳州古称亳、谯、谯郡,是中华民族较早的发祥地之一。据有文字记载,亳州建城已有3700年的历史。 夏朝,亳州属豫州,曾是帝喾的国邑。商代曾為國都,西周初年是神农氏后裔的封地,置焦国。春秋时期,焦属陈国。陈于此建焦城,后楚灭陈,筑谯城(今谯城区)。谯夷所辖区域扩大到苦、蒙(今蒙城县)及周边地区。秦时置谯县,属砀郡。汉时先后属豫州、沛国治。建安末年设谯郡。曹魏黄初二年(221年),封谯郡" +564,"境呈东南西北向斜长形,长约150公里,宽约90公里。辖境与黄河决口扇形地相连,属平原地带,地势平坦,仅东部有龙山、石弓山、齐山、狼山、双锁山等10余处石灰岩残丘分布;整个亳州地势西北高而东南低,以1/9000地面自然坡降向东南微倾。西北部谯城区詹楼地势最高,海拔42.5米;东南以利辛县展沟南部最低,海拔22米,相对落差20.5米。由于受河流蜿蜒切割变迁和黄河历次南泛的影响,形成平原中岗、坡、碟形洼地相间分布,具有“大平小不平”的地貌特征。 城中有涡河流经,是淮河支流。 === 气候 === 亳州处于秦岭-淮河天然气候分界线上,位于亚热带季风气候与温带季风气候的过渡地带。季风影响显著,四季分明,雨量适中,光照充足。1月平均气温0.6℃,极端最低气温-20.6℃(1969年2月5日)。7月平均气温27.3℃,极端最高气温42.1℃(1966年7月18日)。年均温14.7℃。年均降水量约790毫米。 == 政治 == === 现任领导 === 亳州市四大机构现任领导人 机构 中国共产党亳州市委员会 亳州市人民代表大会常务委员会 亳州市人民政府 中国人民政治协商会议亳州市委员会 职务 书记 主" +565,"个县。 * 市辖区:谯城区 * 县:涡阳县、蒙城县、利辛县 亳州市行政区划图 亳州市行政区划图 亳州市行政区划图 亳州市行政区划图 亳州市行政区划图 亳州市行政区划图 亳州市行政区划图 亳州市行政区划图 亳州市行政区划图 亳州市行政区划图 区划代码 区划名称 汉语拼音 面积(平方公里) 常住人口(2020年普查) 政府驻地 邮政编码 乡级行政区划 乡级行政区划 乡级行政区划 区划代码 区划名称 汉语拼音 面积(平方公里) 常住人口(2020年普查) 政府驻地 邮政编码 街道办事处 镇 乡 341600 亳州市 8,521.23 4,996,844 谯城区 236800 9 72 7 341602 谯城区 2,262.89 1,537,231 花戏楼街道 236800 3 20 2 341621 涡阳县 2,109.90 1,170,719 城关街道 233600 3 20 341622 蒙城县 2,143.93 1,101,640 城关街道 233500 3 12 2 341623 利辛县 2,004.50 1,187,254 城关镇 236700 20 3 == 人口 == 2022" +566,"比例增加0.04个百分点;各少数民族人口减少400人,下降0.79%,占总人口比例下降0.04个百分点。 == 交通 == * 、过境。 * 京九铁路设亳州站。 * 京港高速铁路商合段设芦庙站(皖豫省界附近)、亳州南站、古城东站。 * 亳州机场(興建中) ==全国重点文物保护单位== == 文化古迹 == ==友好城市== * 干邑 == 名人 == ==特产== 亳白芍:中国地理标志产品。 中藥材是亳州的特色產業,亳州也是中華人民共和國中藥材的主要产地。每年9月的国际(亳州)中医药博览会均在亳州举办。 == 注释 == == 参考文献 == == 外部链接 == * * Category:安徽地级市 皖 皖 Category:中原经济区城市 Category:前16世紀建立的聚居地 Category:2000年建立的行政区划" +567,"计算机网络(),通常也简称網路,是指容許节点分享資源的數碼電信網絡。在電腦網絡,電腦設備會透過節點之間的連接(数据链路)互相交換數據,即收發(接收和發放)網絡資訊。傳輸媒介可分為有線及无线兩類——有線的可用到双绞线、光纖電纜等媒介;無線則可用到Wi-Fi、NFC和Bluetooth等。 用於建立、路由及终止數據傳輸的電腦網絡設備即為網絡節點。節點包括像个人电脑、电话、服务器般的主机及其他網絡硬件(如閘道器及路由器)。它們一般以網路位址作識別碼。當一個設備能夠與另一設備交換資訊時,便可視它們倆已连接成网络,不論它們是否直連。專用通信協定在大多數分層中位於其他更通用的通信協定之上。要維持網絡的可靠性,便需要有一定的網絡管理技能。 電腦網絡為海量应用程序及服務背後的基礎。比如存取互聯網、数字视频、數位音訊;共享列印機;收發電子郵件及即時通訊訊息。電腦網絡可依照传输介质、传输协议、 網絡大小、拓撲、流量控制機制、建立目的等因素區分。世界上最大的電腦網絡為互聯網。 == 歷史== 電腦網絡發展的里程碑包括: * 1950年代後期,美軍開始使用指揮系統——贤者系统,其為早期的電腦網絡。 * 195" +568,"關一個用於電腦分时的試驗性广域网。 * 1969年,ARPANET的首四個節點經已用電路連接,其速度為50kbit/s,在加利福尼亚大学洛杉矶分校、斯坦福大學研究中心、加利福尼亞大學聖塔芭芭拉分校、犹他大学這四個地點之間建立網絡。在分组交换網絡的理論中做出許多傑出貢獻的伦纳德·克莱因罗克協助了ARPANET的研發。1970年代後期,他與其學生針對的理論性研究對當下互聯網的實際運行仍有一定重要性。 * 1972年,使用X.25的商業服務經已投入運作,其於後來成為了TCP/IP網絡的基礎。 * 1973年,法國的網絡為第一個使主機可靠地傳遞數據的網絡。 * 1974年,文頓·瑟夫等人寫出了首個TCP的規格(Internet傳輸控制程序的規範),他們在當中首次把「Internet」視作互聯網絡的簡寫Cerf, Vinton; Dalal, Yogen; Sunshine, Carl (December 1974), , Specification of Internet Transmission Control Protocol。 * 1976年,Datapoint Corporation的" +569,"址、上層協定、存活時間等資訊。 ==网络拓扑== 网络拓扑是网络的幾何形狀分類。除了影響网络的容錯度、管理方式、資訊如何流通外,它還會影響網絡的可靠性和架設成本,比如匯流排拓撲較容易發生單點失敗。一般而言線路愈多愈可靠,但相對地佈線成本亦會提升。 常見的网络拓扑有: * 匯流排拓撲:所有節點共享一個介質,以此連接其他節點。早期的以太网10BASE5及10BASE2會應用此一拓撲。 * 星狀拓撲:所有節點集中連接至一個特殊的裝置,例如交換器、集線器。 * 環狀拓撲:所有節點以形成一個環狀的方式連接,節點間需以順序的方式傳送資訊。應用此一拓撲的有IBM Token Ring、IEEE 802.5 Token Ring。 * 網狀拓撲:所有節點連接至一個以上的節點。 * 樹狀拓撲:所有節點一層一層地以分支形式連接。 * 混合式拓撲:將上述拓撲混合使用。在佈置網絡時,一般會混合多種拓撲。 === 覆盖网络 === 覆盖网络是指建立在其他網絡之上的網絡。覆盖网络内的節點會透過虛擬或邏輯鏈路連接。每個鏈路對應於基礎網絡中一條或多條的路徑。覆蓋網絡的拓撲一般會跟基礎網絡的不同。比如很多對等網路皆屬覆" +570,"用的傳輸媒介。它一般由四對銅線所組成,並可用於傳輸語音及數據。雙線纏繞的目的在於減少串扰及雜音的情況。其速度從2Mbps到40Gbps不等。双绞线可分為兩類—— 遮蔽式雙紐線及無遮蔽式雙紐線。 *光纖是一種玻璃纖維或塑膠。其以光為傳遞的介質。它的好處為速度快、信號難以衰減。其傳輸速度可超過2Gbps;AT&T曾製作一條速度為400Gbps,橫跨12.07008公里的光纖电缆。其可在保有很高的數據傳輸率的情況下,擁有很遠的傳輸距離,因此可用於作為海底電纜的傳輸媒介。光纖大致分為兩種——單模光纖與。單模光纖適合長距離傳輸(數十至一百公里);多模光纖相對較便宜,但其傳輸距離僅限於幾百米的距離,甚至只有幾十米。 === 無線技術 === 利用無線電等電子通信手段也可連接至網絡。 * 陸上微波通訊會以地上發送站來把微波傳送至類似衛星的天線接收器。陸上微波的頻譜在千兆赫以內——因此所有通信限制在無阻礙的情況下才能順利進行。基站最高可分開約40公里。 *通訊衛星通訊亦會透過微波來實現通訊。該些衛星位於太空,一般距離地球地面約36000公里。其可傳送語音、GPS、視像等資訊。 *蜂窝网络利用了好幾種的" +571,"太网MAC地址的長度有6Bytes。前3Bytes為廠商向學會登記而得來。後3Bytes則為廠商自行賦予。 ===中继器及集線器 === 中继器是用於增強訊號的網絡設備。這一設備可使因距離問題而出現衰減的訊號再生,令其能傳送得更遠。對於大多雙紐線而言,若總長度超過100米,便需要安放一台中继器。光纖則要有幾公里的總長度才需安放一台。 擁有多個端口的中继器即為集線器。在OSI模型中,集線器為第一層設備。「5-4-3」規則是指在10Mbps以太網中,網絡不能有超過5個區段的總長度,最多使用4個集線器,最多以3個區段連接電腦。 由於交換器的功能相對較佳,價格亦相近,故上述兩者皆已被交換器所取代。 === 橋接器=== 橋接器連接兩個獨立的网段及過濾之間的流量,它在OSI模型的數據鏈路層中運作。它可分割網絡的碰撞域,但同時仍會進行群播。 分割網絡的碰撞域能增加網絡的效率。 橋接器有三大類: *局域橋接器——直接連接不同的局域網。 *遠程橋接器——連接在不同區域的局域網。 *無線橋接器——連接兩個或多個有線局域網。 === 交換器=== 交換器是一種依據MAC地址,來在端口之間轉發和過濾数据链路" +572,"ARCNET * DECNET * 以太网 * IPSEC * Appletalk * TCP/IP协议栈 ** 传输控制协议 ** 互联网通讯协议 * 令牌环 * IPX * 光纤传输数据接口 * HIPPI * Myrinet * 异步传输模式 * RS-232 * IEEE-488 * USB * IEEE 1394 * X.25 * 帧中继 * 蓝牙 * ZigBee * IEEE 802.11 * 系统网络结构 == 相关的机构与厂商 == * 机构 ** 美国国家标准组织ANSI ** 电子工业联盟EIA ** 电子和电气工程师协会IEEE ** 国际标准化组织ISO * 厂商 ** CISCO ** HP ** 中兴通讯 ** 华为 ** 锐捷 ** IBM ** 北电网络 ** 港湾 ** D-Link **TP-Link == 参见 == * 存储区域网络 * 網際網路 == 参考文献 == === 引用 === === 来源 === ; 书籍 * Andrew S. Tanenbaum. 《计算机网络》(Computer Networks, ISBN 0-13-34" +573,"本表是以現代標準漢語拼音或注音首字母排列的所有语言的列表,收录了目前世界现存语言超過6900种语言(其中汉语可視為「漢語族」或是一种语言,但部分汉语分支仍在列表中)。 ==A== 语言汉译 当地语名称 语系或语族 备注 阿巴札语 Абаза Бызшва 西北高加索语系 阿布哈兹语 Аҧсуа 西北高加索语系 阿昌语 Ngachang 缅彝语支 阿当梅语 adangbɛ 沃尔特-刚果语支 阿迪格语 адыгэбзэ; адыгабзэ 西北高加索语支 阿尔巴尼亚语 Shqip 印欧语系,阿尔巴尼亚语族 北阿尔泰语 тÿндÿк Алтай тили 北突厥语支 南阿尔泰语 тÿштÿк Алтай тили 北突厥语支 阿法尔语(阿法语) Afaraf 库希特语族,东库希特语支 阿非利堪斯语(南非荷兰语、南非语) Afrikaans 西日耳曼语支 阿弗里希利语 Ɛl-Afrihili 人工语言 阿卡德语 akkadû; lišānum akkadītum 东闪米特语支 阿坎语 Twi, Fante 沃尔特-刚果语支 阿拉伯语 العربية 中闪米特语支 阿拉贡语 aragonés 意" +574,"(イタッㇰ) 孤立语言 阿哲语 ꀊꍓꁈ/Azhepo 缅彝语支 埃菲克语 Usem Efịk 沃尔特-刚果语支 埃多姆语 Edomite/אדומית 中闪米特语支 埃桂语 Aequian 奥斯坎-翁布里亚语支 埃及语 𓂋𓏺𓈖𓆎𓅓𓏏𓊖 埃及语族 灭亡语言 埃克丘克语 沃尔特-刚果语支 埃兰语 孤立语言 灭亡语言 埃内茨语 Онаь базаан 萨莫耶德语族,北萨莫耶德语支 埃斯特雷马杜拉语 estremeñu 意大利-西罗曼语支 埃维语(幽语) Èʋegbe 沃尔特-刚果语支 鄂温语 эвэды торэн 通古斯语族 埃翁多语 沃尔特-刚果语支 艾马拉语 aymar aru 艾马拉语系 艾米利亚-罗马涅语 Emiliàn e rumagnòl 意大利-西罗曼语支 艾努语 ئەينۇ 东突厥语支 爱尔兰语 Gaeilge 凯尔特语族 爱沙尼亚语 eesti keel 芬兰-乌戈尔语族,芬兰语支 盎格鲁-诺曼语 意大利-西罗曼语支 灭亡语言 安息语 𐭐𐭄𐭋𐭅𐭀𐭍𐭉𐭂 伊朗语支 灭亡语言 奥地利-巴伐利亚语(巴伐利亚语) Boarisch 西日耳曼语支 奥吉布瓦语 ᐊᓂᔑᓇᐯᒧᐏᐣ (A" +575,"語言 巴什基尔语(巴什噶尔语) башҡорт теле 西突厥语支 巴斯克语 euskara 孤立语言 巴亚语 沃尔特-刚果语支 巴宰語 Pazeh 台灣南島語言 白俄罗斯语 Беларуская мова; biełaruskaja mova 东斯拉夫语支 白马语 喜马拉雅语支 班巴拉语 bamanankan 曼德语族,西曼德语支 邦阿西楠语 马来-波利尼西亚语族,北菲律宾语支 邦板牙语 Kapampangan 马来-波利尼西亚语族,北菲律宾语支 保安语 蒙古语族 保加利亚语 български език 南斯拉夫语支 卑南語 Pinuyumayan 台灣南島語言 贝扎语(贝贾语) بداوية 库希特语族,北库希特语支 本巴语 ichiBemba 沃尔特-刚果语支 崩尼-博嘎尔语(博嘎尔-珞巴语) 藏缅语族,北阿萨姆语支 比科尔语 马来-波利尼西亚语族,中菲律宾语支 比林语 ብሊና 库希特语族,中库希特语支 比尼语 Edo 沃尔特-刚果语支 比什奴普莱利亚-曼尼浦尔语 ইমার ঠার/বিষ্ণুপ্রিয়া মণিপুরী 印度-雅利安语支,东部地区 比斯拉玛语(必斯拉马语" +576,"多尼语) brezhoneg 凯尔特语族 布鲁夏斯基语 بروشسکی 孤立语言 布匿语 中闪米特语支 布農語 Bunun 台灣南島語言 布努语 Bu-Nao 苗语支 布兴话 北孟-高棉语支 布依语 Haausqyaix 台语支 ==C== 语言汉译 当地语名称 语系或语族 备注 仓洛-门巴语(仓洛语) 喜马拉雅语支 查法蒂语(犹太-法语) צרפתית 意大利-西罗曼语支 查莫罗语 Chamoru 马来-波利尼西亚语族,查莫罗语 察合台语(查加泰语) جغتای 东突厥语支 潮州话 潮汕话、潮語 漢藏語系汉语族閩語支闽南语 朝鲜语(韩语) 한국어 (-{韓國語}-); 조선말 (-{朝鮮語}-) 朝鲜语系新罗语族 车臣语 Нохчийн мотт; Noxçiyn mott 东北高加索语支 楚利姆语 西突厥语支 楚瓦什语 чӑваш чӗлхи 突厥语族,保加尔语支 茨瓦纳语(博茨瓦纳语) seTswana 沃尔特-刚果语支 村语 加岱语族,央标语支 错那-门巴语 喜马拉雅语支 ==D== 语言汉译 当地语名称 语系或语族 备注 达尔格瓦语 дарган мез 东北高加索语支 达尔" +577,"ml 侗水语支 荳蘭阿美語 台灣南島語言 独龙语 tɯɹɯŋ 藏缅语族,怒语支 杜亚拉语 沃尔特-刚果语支 多尔干语 Дулҕан 北突厥语支 多格拉语 डोगरी 印度-雅利安语支,北部地区 多格里布语 Tłįchǫ 纳-德内语系,阿萨巴斯卡-埃雅克语族 ==E== 语言汉译 当地语名称 语系或语族 备注 俄语 русский язык 东斯拉夫语支 俄都语 Ơ đu 北孟-高棉语支 峨村布央语 加岱语族,央标语支 埃尔齐亚语 эрзянь кель 乌拉尔语系,莫爾多瓦語支 鄂伦春语 通古斯语族 鄂罗克语 ульта 通古斯语族 鄂温克语 Эвэнки 通古斯语族 北恩德贝勒语 isiNdebele 沃尔特-刚果语支 南恩德贝勒语 Ndébélé 沃尔特-刚果语支 恩敦加语(恩东加语) Owambo 沃尔特-刚果语支 恩济马语 沃尔特-刚果语支 尔龚语 羌语支 尔苏语 羌语支 ==F== 语言汉译 当地语名称 语系或语族 备注 法语 français; langue française 意大利-西罗曼语支 法拉语 Fala 意大利-西罗曼语支 法兰克-普罗旺斯语 arpitan;" +578,"寨语) ភាសាខ្មែរ 东孟-高棉语支 哥伦打洛语 马来-波利尼西亚语族,苏拉威西语支 哥特语(哥德语) 𐌲𐌿𐍄𐌹𐍃𐌺 东日耳曼语支 格列博语 沃尔特-刚果语支 格陵兰语 kalaallisut; kalaallit oqaasii 爱斯基摩-阿留申语系,因纽特语族 格鲁吉亚语(喬治亞語) ქართული ენა 南高加索语族 格曼-僜语 藏缅语族,北阿萨姆语支 贡德语 Gōndi 中南达罗毗荼语族 古吉拉特语 ગુજરાતી 印度-雅利安语支,中部地区 古教会斯拉夫语 ѩзыкъ словѣньскъ 南斯拉夫语支 瓜拉尼语 Avañe'ẽ 图皮语系,图皮-瓜拉尼语族 官话 官话 漢藏語系漢白語族 龜崙語 台灣南島語言 桂柳话 桂林话、柳州话、郴州话、荔浦话 漢藏語系漢語官话,西南官话 贵琼语 羌语支 国际语 interlingua 人工语言 ==H== 语言汉译 当地语名称 语系或语族 备注 哈卡斯语 Хакас тілі 北突厥语支 哈拉吉语 南突厥语支 哈里亚纳语 हरियाणवी 印度-雅利安语支,中部地区 哈尼语 Haqniqdoq 缅彝语支 哈萨克语 Қазақ ті" +579,"系,阿萨巴斯卡-埃雅克语族 华北官话 北方話 漢藏語系漢語官話 徽语 徽州話 漢藏語系漢語族 回鹘语 东突厥语支 回辉语 马来-波利尼西亚语族,马来语支 霍语 蒙达语族 寒溪語 台灣南島語言 猴猴語 Qauqaut 台灣南島語言 西臺語 印歐語系安納托利亞語族 西瓦語 Žlan n Isiwan 亞非語系柏柏爾語族 ==J== 语言汉译 当地语名称 语系或语族 基库尤语(吉库尤语) Gĩkũyũ 沃尔特-刚果语支 基里巴斯语(吉尔伯特语) taetae ni Kiribati 马来-波利尼西亚语族,核心马来-波利尼西亚语支 基隆迪语(克伦地语) kiRundi 沃尔特-刚果语支 基尼阿旺达语(盧安達語) kinyaRwanda 沃尔特-刚果语支 吉大港语 চাটগাঁইয়া বুলি 印度-雅利安语支,东部地区 吉尔吉斯语(柯尔克孜语) кыргыз тили; قىرعىز ٴتىلى 西突厥语支 济州语 제주어 (-{済州語}-); 제주말 (-{済州말}-) 朝鲜语系新罗语族 吉兹语 ግዕዝ 南闪米特语支 加语 Gã 沃尔特-刚果语支 加拉提亚语 凯尔特语族 加勒比印度斯坦语" +580,"tili 西突厥语支 卡累利阿语 karjalan kieli 芬兰-乌戈尔语族,芬兰语支 卡那卡那富語 台灣南島語言 卡纳达语(康纳达语) ಕನ್ನಡ 南达罗毗荼语族 卡努里语 撒哈拉语族,西撒哈拉语支 卡什加语 Qaşqaycə 南突厥语支 卡舒比语 kaszëbsczi jãzëk 西斯拉夫语支 卡威语(古爪哇語) Bhāṣa Kawi 马来-波利尼西亚语族,爪哇语 卡西语 Khasi 北孟-高棉语支 卡约语 马来-波利尼西亚语族,Gayo语支 卡真法语(路易斯安那州法语) français cadien 意大利-西罗曼语支 卡卓语 缅彝语支 凱達格蘭語 台灣南島語言 凯尔特伊比利亚语 凯尔特语族 坎巴语 沃尔特-刚果语支 坎伯兰语 凯尔特语族 康家语 蒙古语族 康沃尔语(康瓦尔语) Kernewek 凯尔特语族 科达古语 ಕೊಡವ ತಕ್ 南达罗毗荼语族 科拉米语 中达罗毗荼语族 科米语(科米-兹梁语) коми кыв; зырян коми кыв 芬兰-乌戈尔语族,彼尔姆语支 科米-彼尔米亚克语 перем коми кыв 芬兰-乌戈尔语族,彼尔姆语支 科普特语 ⲙⲉⲧ" +581,"(库德语) Kurdî 伊朗语支,西部地区 库卢克语 कुरुख़ 北达罗毗荼语族 庫梅克語 Кумык 西突厥语支 库特内语 Ktunaxa 孤立语言 库维语 中南达罗毗荼语族 库伊语 中南达罗毗荼语族 宽亚玛语(贡耶玛语) 沃尔特-刚果语支 ==L== 语言汉译 当地语名称 语系或语族 拉阿魯哇語 Hla'alua 台灣南島語言 列茲金語 лезги чІал (lezgi č’al) 高加索語系北支 拉达克语 ལ་དྭགས་སྐད། 喜马拉雅语支 拉登语 Ladin 意大利-西罗曼语支 拉迪诺语(犹太-西班牙语) ג'ודיאו-איספאנייול 意大利-西罗曼语支 拉丁语 latine; lingua latina 拉丁-法利希语支 拉亨达语 ਲਹਿੰਦੀ 印度-雅利安语支,西北部地区 拉祜语 Lahu 缅彝语支 拉祜西语 缅彝语支 拉基语 加岱语族,仡基语支 拉珈语 侗台语族,拉珈语 拉贾斯坦语 राजस्थानी 印度-雅利安语支,中部地区 拉科塔语 Lakhota 苏语系,大苏语族 拉克语 лакку маз 东北高加索语支 拉帕努伊语(复活节岛语) rapanui;" +582,"拉语 lwaà: 沃尔特-刚果语支 卢萨语 藏缅语族,Kuki-Chin-Naga语支 卢森堡语 Lëtzebuergesch 西日耳曼语支 卢森尼亚语 русин rusin 东斯拉夫语支 卢维语 luwili 安那托利亚语族 卢伊塞诺语 犹他-阿兹特克语系,北犹他-阿兹特克语族 魯凱語 台灣南島語言 吕底亚语 𐤮𐤱𐤠𐤭𐤣𐤸𐤯𐤦𐤳 安那托利亚语族 吕基亚语 Trm̃mili;𐊗𐊕𐊐𐊎𐊆𐊍𐊆 安那托利亚语族 呂勒薩米語 julevsámegiella 乌拉尔语系,芬兰-乌戈尔语族,萨米语支,西萨米语组 伦巴底语 Lombard 意大利-西罗曼语支 伦巴底日耳曼语 Langobardic 东日耳曼语支 罗马尼亚语 română 东罗曼语支 罗曼什语 rumantsch 意大利-西罗曼语支 罗姆语(吉普赛语) rromani ćhib; Romani šib; Romanó 印度-雅利安语支,中部地区 罗托卡特语 东巴布亚语系,北布干维尔语族 逻辑语 lojban 人工语言 洛齐语 siLozi 沃尔特-刚果语支 ==M== 语言汉译 当地语名称 语系或语族 马都拉语 Basa Mathu" +583,"তৈইলোন 藏缅语族,曼尼普尔语 曼西语 маньси; моаньсь 芬兰-乌戈尔语族,乌戈尔语支 芒语 Tiếng Mường 越芒语支 芒戈语 沃尔特-刚果语支 莽语 北孟-高棉语支 貓霧束語 台灣南島語言 毛利语 te reo Māori 马来-波利尼西亚语族,核心马来-波利尼西亚语支 毛南语 侗水语支 梅戈来诺-罗马尼亚语 Vlăheşte 东罗曼语支 门德语 Mɛnde yia 曼德语族,西曼德语支 蒙达里语 蒙达语族 蒙古语 монгол;ᠮᠣᠩᠭᠣᠯ 蒙古语族 孟语 M'Nông 东孟-高棉语支 孟加拉语 বাংলা 印度-雅利安语支,东部地区 米德勒语(迈蒂利语) मैथिली 印度-雅利安语支,东部地区 米克马克语 Míkmaq, Mi'gmaq 阿尔吉克语系,阿尔冈昆语族 米兰德斯语 Lhéngua Mirandesa 意大利-西罗曼语支 米南卡保语 Baso Minangkabau 马来-波利尼西亚语族,马来语支 缅甸语 ျမန္မာစာ 缅彝语支 苗语 Hmoob 苗语支 闽北语 閩北話;建甌話;Guīng-èi-dī 漢藏語系汉语族闽语支,沿山闽语 闽东" +584,"鲁语) Ekakairũ Naoero 马来-波利尼西亚语族,核心马来-波利尼西亚语支 尼奥罗语 Runyoro 沃尔特-刚果语支 尼泊尔语 नेपाली 印度-雅利安语支,东部地区 尼斯语 缅彝语支 尼瓦尔语 Nepal Bhasa;नेपाल भाषा 喜马拉雅语支 尼亚斯语 马来-波利尼西亚语族,苏门答腊语支 尼扬贾语(齐切瓦语) chiCheŵa; chinyanja 沃尔特-刚果语支 尼扬科勒语 沃尔特-刚果语支 尼扬韦齐语 Kinyamwezi 沃尔特-刚果语支 涅吉达尔语 通古斯语族 涅涅茨语 ненэця вада 萨莫耶德语族,北萨莫耶德语支 纽埃语 ko e vagahau Niuē; faka-Niue 马来-波利尼西亚语族,核心马来-波利尼西亚语支 怒苏语 缅彝语支 怒语 藏缅语族,怒语支 女真语  通古斯语族 挪威语,书面挪威语 Norsk bokmål 北日耳曼语支 挪威语,新挪威语 Norsk nynorsk 北日耳曼语支 诺恩语 北日耳曼语支 古诺尔斯语 norskr 北日耳曼语支 诺盖语 ногай тили 西突厥语支 诺维亚语 Novial" +585,"刚果语支 契丹语  蒙古语族 羌语 羌语支 乔克托语 Chahta Anumpa 穆斯科格语系,西穆斯科格语支 切罗基语 ᏣᎳᎩ 易洛魁语系,南易洛魁语族 钦西安语 佩努蒂亚语系,钦西安语族 丘克语 马来-波利尼西亚语族,核心马来-波利尼西亚语支 龟兹语(西吐火罗语) 吐火罗语族 却隅语 羌语支 ==R== 语言汉译 当地语名称 语系或语族 日语(日本語) -{日本語}- (にほんご/にっぽんご) 日本语系 日德兰语 Jysk 北日耳曼语支 柔若语 缅彝语支 瑞典语 Svenska 北日耳曼语支 瑞士德语(阿勒曼尼语) Schwyzerdütsch, Alemannisch 西日耳曼语支 若买语 北孟-高棉语支 ==S== 语言汉译 当地语名称 语系或语族 撒丁语(萨丁尼亚语) sardu 南罗曼语支 撒拉语 Salarça; سالارچا 南突厥语支 撒梅语 缅彝语支 撒尼语 缅彝语支 萨波特克语 奥托-曼格安语系,萨波特克语族 萨马利亚阿拉米语 ארמית, ܐܪܡܝܐ 中闪米特语支 萨米语,北萨米语 sámegiella 乌拉尔语系,芬兰-乌戈尔语族,萨米语支,西萨米语组" +586,"語系漢語族閩語支,沿山閩語 绍尔语 Шор тили 北突厥语支 绍纳语(修纳语) chiShona 沃尔特-刚果语支 畲语 [saŋ44 xaʔ5 uə21](山哈话)或[hɔ22 ne42](活聂) 苗瑶语族,畲语 舍弥来语 孟-高棉语族,亚斯里语支 史拉维语 纳-德内语系,阿萨巴斯卡-埃雅克语族 史兴语 羌语支 世界语 Esperanto 人工语言 水语 Suī 侗水语支 斯科尔特萨米语 nuõrttsääʹmǩiõll 乌拉尔语系,芬兰-乌戈尔语族,萨米语支,东萨米语组 斯科讷语 skånska 北日耳曼语支 斯洛伐克语 slovenčina 西斯拉夫语支 斯洛文尼亚语(斯洛維尼亞語) slovenščina 南斯拉夫语支 斯瓦特语(史瓦濟蘭語) siSwati 沃尔特-刚果语支 斯瓦希里语(斯瓦西里语) kiswahili 沃尔特-刚果语支 斯万语 ლუშნუ ნინ 南高加索语族 四川话 川话、川语、蜀語 汉藏语系汉语官话,西南官话 苏阿迪特语(犹太-普罗旺斯语) שואדית 意大利-西罗曼语支 苏格兰语 Scoats leid; Lallans 西日耳曼语支 苏格兰盖尔" +587,"Tamajeq,ⵜⴰⵎⴰⵌⴰⵆ 柏柏尔语族,塔马舍克语支 塔马塞特语 ⵜⴰⵎⴰⵣⵉⵖⵜ 柏柏尔语族,北柏柏尔语支 塔塔尔语(鞑靼语) татарча; tatarça; تاتارچا 西突厥语支 塔希提语(大溪地语) te reo Tahiti; te reo Māʼohi 马来-波利尼西亚语族,核心马来-波利尼西亚语支 泰卢固语 తెలుగు 中南达罗毗荼语族 泰米尔语(坦米尔语、淡米尔语) தமிழ் 南达罗毗荼语族 泰语 ภาษาไทย 台语支 泰雅语 Atayal 台灣南島語言 汤加语 (莫桑比克) 沃尔特-刚果语支 汤加语 (尼亚萨) chiTonga 沃尔特-刚果语支 汤加语 (泰国) 孟-高棉语族,亚斯里语支 汤加语 (汤加岛)(東加語) faka-Tonga 马来-波利尼西亚语族,核心马来-波利尼西亚语支 汤加语 (赞比亚) 沃尔特-刚果语支 唐米语 喜马拉雅语支 特拉华语 Lënape 阿尔吉克语系,阿尔冈昆语族 特列纳语 阿拉瓦克语系,Maipurean语族 特林吉特语 Lingít 纳-德内语系,特林吉特语 特威语 Twi 沃尔特-刚果语支 滕内语 大西洋语支" +588,"坎语系,瓦肖语 瓦伊语 ꕙꔤ 曼德语族,西曼德语支 佤语 Vāx 北孟-高棉语支 汪达尔语 东日耳曼语支 望加锡语 马来-波利尼西亚语族,苏拉威西语支 威尔士语(威尔斯语、韦尔斯语) Cymraeg 凯尔特语族 威尼斯語 Vèneto 意大利-西罗曼语支 维吾尔语 Uyƣurqə; уйғурчә; ئۇيغۇرچە 东突厥语支 卫拉特语(卡尔梅克语) хальмг келн;ᡆᡕᡅᠷᠠᡑ 蒙古语族 文达语(温达语) tshiVenḓa 沃尔特-刚果语支 翁本杜语 úmbúndú 沃尔特-刚果语支 翁布里亚语 Umbrian 奥斯坎-翁布里亚语支 沃尔西语 奥斯坎-翁布里亚语支 沃拉普克语 Volapük 人工语言 沃洛夫语(渥鲁夫语) Wolof 大西洋语支 乌语 北孟-高棉语支 乌德语 通古斯语族 乌德穆尔特语 удмурт кыл 芬兰-乌戈尔语族,彼尔姆语支 乌尔都语 اردو 印度-雅利安语支,中部地区 乌尔奇语 通古斯语族 乌加里特语 𐎜𐎂𐎀𐎗𐎛𐎚 中闪米特语支 乌克兰语 українська мова 东斯拉夫语支 乌鲁姆语 Урум 突厥语族,乌鲁姆语 乌撒纳苏语 缅彝" +589,"通古斯语族 锡达莫语 Sidámo 'Afó 库希特语族,东库希特语支 夏尔巴语 喜马拉雅语支 夏威夷语 ‘Ōlelo Hawai‘i 马来-波利尼西亚语族,核心马来-波利尼西亚语支 夏延语 Tsêhést 阿尔吉克语系,阿尔冈昆语族 仙岛语 缅彝语支 襄樊话 襄樊话 漢藏語系汉语官话,西南官话 乡话 瓦乡话 汉语 湘语 湖南话 漢藏語系漢語族 象雄语 𑲆𑱵𑲆𑲲𑱵 喜马拉雅语支 信德语(辛德语) سنڌي، سندھی, सिन्धी 印度-雅利安语支,西北部地区 匈牙利语(马扎尔语) magyar nyelv 芬兰-乌戈尔语族,乌戈尔语支 古叙利亚语 ܣܘܪܝܝܐ 中闪米特语支 巽他语 basa Sunda;ᮘᮞ ᮞᮥᮔ᮪ᮓ 马来-波利尼西亚语族,巽他语支 ==Y== 语言汉译 当地语名称 语系或语族 备注 牙纳桑语 ненэця вада 萨莫耶德语族,北萨莫耶德语支 雅库特语 Саха тыла 北突厥语支 雅浦语 马来-波利尼西亚语族,核心马来-波利尼西亚语支 亚东语 喜马拉雅语支 亚美尼亚语 Հայերեն լեզու 印欧语系,亚美尼亚语族 亚齐语 Basa Acèh 马来" +590,"印度尼西亚语(印尼语) Bahasa Indonesia 马来-波利尼西亚语族,马来语支 印古什语 гӀалгӀай мотт 东北高加索语支 英语 English 西日耳曼语支 攸乐基诺语 缅彝语支 尤比克语 twaχəbza 西北高加索语支 犹太-阿拉伯语 中闪米特语支 犹太-波斯语 伊朗语支,西部地区 犹太-格鲁吉亚语 קיברולי 南高加索语族 犹太-克里米亚鞑靼语 кърымчах тыльы 西突厥语支 犹太-意大利语 giudeo-italiano 意大利-西罗曼语支 源语 Tiếng Nguồn 越芒语支 约鲁巴语 Yorùbá 沃尔特-刚果语支 粤语 粤语;广府话;广东话;白话 漢藏語系漢語族 越南语 Tiếng Việt (㗂越); Việt Ngữ (-{越語}-) 越芒语支 云南话 云南话 漢藏語系汉语官话,西南官话 ==Z== 语言汉译 当地语名称 语系或语族 备注 载瓦语 Atsi 缅彝语支 赞德语 Pazande 沃尔特-刚果语支 藏语 བོད་ཡིག 喜马拉雅语支 扎巴语 羌语支 哲纳加语 Tuḍḍungiyya 柏柏尔语族,哲纳加语 中原官话 郑州话" +591,"上海市,简称沪,别称申,是中华人民共和国直轄市,中國的经济、金融、贸易、航运、商業、上游製造和對外開放中心,上海也是一座港口城市,是中國人口第二多的城市,是中國最發達的城市之一,也是一座全球城市。上海位於中国东部海岸线的中心,长江三角洲最东部,东临东海,南濒杭州湾,西接江苏、浙江两省,北端的崇明岛处于长江入海口中,与周围的江苏、浙江、安徽三省多个城市共同构成了长江三角洲城市群。 最早在新石器時代,上海地區已經有先民聚居。春秋時代,上海属吳國,戰國時代則属楚國。晋代,上海的人口聚集起來,初步发展为區域性渔港、盐产地和商贸集镇。唐代到元代,上海所属地区归华亭县、松江府管辖。1292年上海縣始建縣。明清两朝時的上海有棉纺织业。1843年,《南京条约》签订,正式开埠,上海作为指定的五大通商口岸之一,设立租界。此时的上海還是苏松太道的驻地。上海开埠後不少江蘇、浙江、廣東、安徽、山東等省份及外国移民前往上海,上海开始吸收江南地区附近传统的吴越文化与各地移民带入的多样文化,融合開埠後的西方近现代文化,逐渐形成了上海本地的海派文化。 自20世纪以来, 上海已成为中国最重要的经济中心及亚洲最大的城市之一" +592,",但在吴地常指河川的小支流。“上”一词,在江左地区的河流名称中,又常指靠近源头一面。宋朝经济发达,催生了酒业的发展,政府在上海浦附近设置酒务以征酒税,称为上海务,上海一名由此传播。 上海地名的来源有多种说法。一说上海因黄浦江而得名。一说因“地居海之上洋”而得名。另一种则以“海外商舶在此登岸”而名为上海。《晉書》和《梁书》就曾多次提到過“沪渎”。上海别称“申”,则因相传上海西部地区曾为楚国公子春申君的封地而得名。 == 历史 == === 早期历史 === 約7000年前,上海西部已形成陆地,东部地区则为近2000年陆续形成。上海境内最早的人类文明遗迹可追溯至6000年前的新石器时代,有马家浜、-{崧}-泽、良渚、马桥文化的遗迹。公元4至5世纪时的晋朝,松江和滨海一带即有渔民聚集,逐渐发展为一个渔港和商贸集镇。唐天宝十载(751年),设立华亭县,隸屬蘇州,范围北到今天的虹口一带,南到海边,东到下沙(今浦东新区航头镇北部),此为松江建城之始。 宋時商販雲集,今上海境内已有上海市(集市)这一称呼,又名華亭海。咸淳三年(1267年),嘉興府华亭县于上海浦(松江的一条支流)西岸设置市镇,定名为上" +593,"境为上海之后的持续繁荣创造了条件。 === 中华民国时期 === File:Shanghai Nanking Road 1930s.jpeg|1930年代的南京路 File:1932 Hochi Map of Shanghai, China - Geographicus - Shanghai-hochi-1932.jpg|1932年的上海市 File:Shanghai Bund seen from the French Concession.jpg|1934年从法租界看上海外滩 1911年11月3日,上海光复。自1912年中华民国成立后,由于保守势力与革命党互相争夺中央及地方权力,全国局势较为混乱,上海华界也时常处于无序状态。与此同时,租界保持了相对的稳定与和平,在经济、人口等方面进一步加快了发展,由1914年开始至1936年成为上海租界高度繁荣的一个阶段。 1927年以前,租界以外的闸北和南市属江苏省上海县管辖。由于租界的政治特殊性,20世纪初的一些影响中国的重大政治事件亦在上海发生。1919年因巴黎和会,上海工人、学生及各界人士举行全市总罢工、罢课、罢市,该年6月5日起,五四运动的" +594,"1937年8月13日,淞沪会战爆发,全面抗战开始,中日于上海激战三个月,上海沦陷。战后,原先繁华的闸北、南市市面受到严重破坏,华界基本沦为贫民区,这对上海今后几十年城市格局产生重大影响。上海公共租界的原為日本界區的虹口地區即公共租界的北半部沦为日军防区,而上海法租界和苏州河以南的由英國、美國和意大利王國主導和防守的上海公共租界因为是外國控制的情况,日本不能強佔因為只會和英、美、意發生戰爭,则产生了长达四年多、畸形繁荣的孤岛时期。至1941年太平洋战争爆发后,日本正式對英國和美國開戰,日军亦在12月8日早晨占领租界,日本的盟友意大利的界區得以倖存至1943年,自1943年起,所有的租界不复存在。1945年抗战胜利,上海由国民政府接管。 1946年,第二次国共内战爆发,上海经济陷入崩溃,物价飞涨。1948年政府颁行《经济紧急处分令》试图扭转局面,但以失败告终。1949年5月上海战役后,解放军在5月27日控制上海全境,成立中国人民解放军上海市军事管制委员会和上海市人民政府。此间,许多与民国政府及海外机构有关的人员、财产和机构随同中华民国政府撤离上海。 === 中华人民共和国时期 === 19" +595,"12-22.。 1956年以前,中國中央政府认为上海地处国防前线,不宜作为国家重点建设地区,所以第一个五年计划的前三年,国家实施的156项重点工程没有一個安排在上海。1956年,中國中央政府调整沿海地区的发展战略,表示要利用和发展沿海的工业基礎。同年陈云來到上海,表示上海要发展。之後上海对原有工业进行调整和经济改组並进行设备更新和技术改造、改建、扩建和新建一批企业。1956年上海工业总产值比1955年增长34.3%。同一时期,上海向中國各地輸送设备、人才、技术。一五期间,上海向长春汽车制造厂等8家单位提供了3000多种设备、14万多公里电线、4万平方米钢窗。此時上海动员支援内地的劳动者有150多万人,其中,工程技術人员4万多人,技术工人13.7万人,为各地培养艺徒13万人。1962年,上海已是中国最大工业中心,共有220个生产门类,占全国256个的86%。1964年,由於中國中央政府實施三线建设,上海承担了大三线建设的304个搬迁项目,並將411个工厂、9.2万职工、2.6万台机器设备搬迁到四川、贵州、江西等13个省、自治区。上海还承担了小三线建设的55个项目,將64个工厂、2.7万职" +596,"了其作为中国经济、金融及贸易中心的地位。现上海市政府致力于将上海建设成國際金融中心与国际航运中心,其规划已于2009年3月获国务院批准。但2006~2008年,时任中共中央政治局委员、上海市委书记陈良宇被指控涉嫌上海社保基金案,并遭开除党籍公职并判处有期徒刑18年,是为21世纪初上海政坛的大变故。 2010年5月1日至10月31日,中國2010年上海世界博覽會召开。该世博会主题为“城市,让生活更美好()”。2010年11月15日,静安区胶州路728号一幢28层的公寓大楼发生一起重大火灾,共造成58人死亡,71人受伤。事发后,市民自发举行哀悼。 2013年8月,国务院正式批准设立中国(上海)自由贸易试验区。该试验区成立时,以上海外高桥保税区为核心,辅之以机场保税区和洋山港临港新城,成为中国经济新的试验田,实行政府职能转变、金融制度、贸易服务、外商投资和税收政策等多项改革措施,并将大力推动上海市转口、离岸业务的发展。该项目立项的过程中曾遭遇阻力,但得到中共中央总书记习近平及国务院总理李克强的支持,该项目最终成功启动。2013年9月29日,上海自由贸易区正式挂牌成立。 2014年12月31日" +597,"日至4月1日、4月1日至4月5日先后封閉浦東和浦西,3月30日进一步宣布全域静态管理即“封城”,随后发生的诸多事件以及防疫政策、防疫手段引发广泛争议。6月1日,開始恢复住宅小区出入、机动车通行和公共交通。由于清零政策仍需继续实施,因此进入公共场所、乘坐公共交通需持72小时内核酸检测阴性证明。直至12月新十条实施后,相关政策与限制才得以解除。 == 地理 == === 地形 === 上海位于北纬30°40′-31°53′,东经120°52′-122°12′,处长江三角洲冲积平原前缘,东濒东海,北界长江,南临杭州湾,西与江苏省和浙江省接壤,是中国海岸线的中心位置。截至2013年末,全市面积6340.5平方公里,南北最长处约120公里,东西最宽处约100公里。其中市区面积为2648.6平方公里。上海市全境为冲积平原,仅西南部有部分火山岩丘。海拔平均高度在4米左右,地势平坦,略呈东高西低,山脉少而低小。东部沿海由长江不断携带入海的泥沙沉积而成,崇明岛、浦东等地的面积依旧在增长中,西部有佘山、天马山等,但高度都在100米以下。上海市最高点是位于杭州湾内的大金山岛,海拔103.4米。 === 环境" +598,"。曾经污染严重的苏州河,自1990年代以后进行了三个阶段的大规模治污工程,污染现已基本消除,河流生态亦得到恢复。对于地下水的过度开采,则采用自来水回灌的手段达到基本平衡,但是地面依旧以每年6毫米的速度沉降。 据《2023上海市生态环境公报》记载:淀山湖处于轻度富营养化状态,综合营养状态指数略有上升;根据中华人民共和国国家标准《地表水环境质量标准》,黃浦江监测断面水質为Ⅱ至Ⅲ類,苏州河监测断面水质均为Ⅲ类;根据《海水水质标准》,近岸海域监测点位中有72.1%劣于第四类海水水质。此外,根据2023年《中国生态环境状况公报》,2023年上海市环境空气质量指数优良天数为320天,重度污染天数1天,无严重污染天数。 === 气候 === 上海的气候类型属于亚热带季风气候夏季高温多雨,冬季温和少雨,四季分明,日照充分,雨量充沛。气候温和湿润,年平均气温17.2 °C。春季(3月15日-5月22日)和秋季(10月2日-11月30日)较短,冬季(12月1日-次年3月14日)居中,夏季(5月23日-10月1日)较长。有春雨、梅雨、秋雨三个雨期,因而5月至9月间为上海的汛期,降水量达全年的60%左右。每年" +599,"海市人大为上海市最高地方国家权力机关,与中共上海市委、市政府、市政协并称市级“四大班子”,其负责人都是省部级正职干部,但市委书记通常由国家级副职的中共中央政治局委员兼任。基于以党领政的实际,中共上海市委对全市各部门有实际上的领导权,市委书记享有比市长更高的地位,而市人大与市政协则拥有较小权力。 上海市人民政府驻黄浦区人民大道200号,现任市委书记為中央政治局委员陈吉宁,市人大常委会主任黄莉新,市长為龚正,市政协主席为胡文容。 === 行政区划 === 上海市现辖16个市辖区,下辖107个街道、106个镇、2个乡,细分为4588个居委会(社区)和1558个村委会。 上海市人民政府于1949年5月成立时,上海市总辖区面积仅为现在的1/10。市域大致位于今日的外环线以内,面积663.5平方公里。1958年,江苏省10个县划归上海市,基本形成当今的辖区范围。1988年至2016年,各郊县撤县建区。1988年,吴淞区和宝山县合并设立宝山区,成为全国第一个撤县设区的县。2000年,浦东新区人民政府正式成立,成为中国第一个副省级区。2009年5月,原浦东新区和南汇区的行政区域合并,成为新的浦东新区。" +600,"注:本图不包括佘山岛、大小金山岛及所有飞地、准飞地。 备注:本图不包括佘山岛、大小金山岛及所有飞地、准飞地。 区划代码 区划名称 汉语拼音 面积(平方公里) 政府驻地 邮政编码 行政区划 行政区划 行政区划 行政区划 行政区划 区划代码 区划名称 汉语拼音 面积(平方公里) 政府驻地 邮政编码 乡级 乡级 乡级 村级 村级 区划代码 区划名称 汉语拼音 面积(平方公里) 政府驻地 邮政编码 街道 镇 乡 社区 行政村 310000 上海市 6340.5 黄浦区 200001 107 106 2 4588 1558 310101 黄浦区 20.46 外滩街道 200001 10 172 310104 徐汇区 54.76 徐家汇街道 200030 12 1 306 310105 长宁区 38.30 江苏路街道 200050 9 1 185 310106 静安区 37.37 江宁路街道 200040 13 1 264 1 310107 普陀区 54.83 真如镇街道 200333 8 2 265 7 310109 虹口区 23.48 嘉兴路街道 200086 8 200 310110 杨浦区" +601,"上海开始逐步承办众多重要国际会议和大型赛事,逐渐加强了与世界的交流。 另外作为曾经租界的存在,很多国家在沪开设领事机构,大多为总领事馆。上海与国外城市缔结友好关系的历史,始于1970年代。至2017年底,与上海建立友好关系的地区已有85个。 === 上海合作组织 === 1996年4月26日,中国、俄罗斯、哈萨克斯坦、吉尔吉斯斯坦、塔吉克斯坦五国元首在上海举行首次会晤。自此,“上海五国”会晤机制正式建立。2001年6月14日至15日,“上海五国”元首在上海举行第六次会晤,乌兹别克斯坦以完全平等的身份加入“上海五国”。2001年6月15日,6国的元首举行首次会晤并签署《上海合作组织成立宣言》,上海合作组织正式成立。 2017年6月9日,在上海合作组织阿斯塔纳峰会上,印度和巴基斯坦正式加入上海合作组织。这是上海合作组织2001年成立以来首次扩大。 === 對口援建 === 1949年中华人民共和国成立以来,上海市对中国大陆其他地区在经济、文化、科技、教育等方面予以了大量支援(不包括上海上缴中央财政后的财政转移支付),对当时尚属于农业国家的中国的工业现代化,以及中国其他省份的经济发展和民生保" +602,",上海的宏觀稅負為15%左右,2017年達到45%左右。至21世紀10年代末,上海上繳的財政收入占中央比重將近10%,2021年,上海净上缴中央税收达到9294亿元,排名全國第二,生产总值约占中国的4%,2017年全市生产总值为30,130亿元,全世界排名第11位,其中第三产业占比超过60%。上海生产总值的一半是由非公有制经济企业贡献的。上海最重要的产业依次为商贸流通业、金融业、信息产业、房地产业、成套设备制造业、汽车制造业,此六大支柱产业约占生产总值的一半。人均生产总值达到达到$135,305元人民币,按國際匯率可兑换成大约$20,447美元,截至2021年,上海拥有7万多家外资企业、800多家跨国公司总部或地区总部以及500多家外资研发中心。2019年,上海占中国实际利用外商直接投资总额约13.8%。截至2022年,上海港集装箱吞吐量连续12年全球第一。2019年上海居民全年人均可支配收入为69442元人民币。 上海市自改革開放以來經濟數據变化 年份 1978 1980 1983 1986 1990 1993 1996 1998 2000 2003 2006 2008 2010 2" +603,"3.00 02.22% 2018 26,890.08 12.96% 28,981.11 16.55% 65,458.33 20.33% 2017 23,804.00 03.81% 24,866.00 04.03% 54,399.00 27.29% 2016 24,747.00 18.13% 25,910.00 20.51% 42,735.00 14.22% 2015 20,949.00 24.79% 21,501.00 30.98% 37,415.00 11.97% 2014 16,787.00 02.24% 16,415.00 01.38% 33,415.00 15.55% 2013 16,420.00 16.77% 16,192.00 16.74% 28,917.00 02.56% 2012 14,061.37 03.71% 13,869.88 02.24% 28,195.63 06.82% 2011 14,603.24 00.96% 13,565.83 05.07% 26,396.13 05.79% 2010 14,464.00 12.65% 14,290.00 15.58% 2" +604,"=== 缩略图|上海证券交易所是中国三所证券交易所之一 1949年以前,上海是中国的商业金融中心。当时上海汇集着号称“四行两局一库”的中央银行、中国银行、交通银行、中国农民银行、中央信托局、邮政储金汇业局和中央合作金库的国家资本金融机构,以及数量众多的外资、私有银行、钱庄和信托公司。当时,总部设在上海的国内银行占银行同业公会注册会员的81%。经过国民政府黄金十年的发展,至抗日战争爆发前,上海的各类私有银行、钱庄与信托已经达到了105家,在华外资银行共32家。与外滩平行的江西路,则因坐落大批金融机构,如金城、盐业、浙江兴业等银行,所收存款额占全国存款总额的三分之一以上,而被誉为“东方华尔街”。 上海金融市场曾非常发达、金融工具品类繁多。除了证券市场以外,还拥有同业拆借、贴现等货币市场,内汇、外汇市场和标金(黄金)市场与大条银(白银)市场。在资本市场方面,上海当时是仅次于纽约和伦敦的全球第三大股票市场和第三大债券市场。上海当时既是全国资金调剂中心,也是远东国际交易活跃的金融中枢:1936年,上海的黄金市场全年成交数实际达到3116万条,折合美元1100余万元。这一数字超过日本的东京和大阪之" +605,"国东方航空集团公司、中国商用飞机有限责任公司、中国宝武钢铁集团、中國銀聯、上海浦东发展银行、中国太平洋保险、中银国际证券有限责任公司、国泰君安证券股份有限公司、海通证券股份有限公司、普华永道中天会计师事务所、德勤华永会计师事务所、安永(中国)咨询有限公司、毕马威企业咨询(中国)有限公司、上海电气(集团)总公司、上海汽车工业(集团)总公司、上海国际港务集团股份有限公司、上海实业(集团)有限公司、上海久事公司。此外,英特尔、葛兰素史克、联合利华、辉瑞、强生、拜耳、汉高、陶氏化学、飞利浦、欧莱雅、博世、通用汽车、可口可乐、埃森哲、麦肯锡、贝恩、波士顿咨询公司等一大批跨国公司和花旗、汇丰、安联、美国国际集团、渣打、法国巴黎银行、法国兴业银行、澳新银行、星展银行、大华银行、恒生银行、东亚银行等17家外资金融公司中国总部设在上海。。 ====商业==== 上海是中国近现代以来的商业中心。上海自建城以来,由于处于富庶的江南平原地区,加之自身传统自然经济手工业的发达。因而早在宋明时期,便已经是有名的商埠。元朝政府在上海设市舶司,使得上海的商业和海外贸易得到了极大发展。明代,松江府成为全国纺织手工业和棉" +606,"证进行购买。1983年起到1993年,票证逐步退出上海的商业,市场开始实行敞开供应。从1990年代中叶起,超市、卖场、便利店等现代化商业模式开始进入上海。传统的杂货店和百货商店开始逐步消失或转变功能。 到1990年代,上海拥有三条主要商业街区和几大商业中心,分别为南京东路、淮海中路、四川北路、豫园、徐家汇等。后经过20年的再度高速发展,至2010年末,上海的商业中心已经扩展到十个,分别为:南京东路商业中心、南京西路商业中心、淮海中路商业中心、四川北路商业中心、徐家汇商业中心、张杨路商业中心、豫园商城商业中心、五角场商业中心、新客站不夜城商业中心、中山公园商业中心。商业中心也从市区的市中心位置逐步扩展至市区的次中心区域,辐射范围不断扩大。根据上海市政府2010年出台的商业报告显示,中山公园与五角场是在原八商业中心的基础上新增的两个商业中心。 === 第二產業 === 缩略图|沪东中华造船厂为巴基斯坦海军建造的F-22P型护卫舰 轻工业是上海发展最早也是最为成熟的工业部门。丝绸与纺织行业是自明代就已经颇具规模。上海开埠后,由于倚靠的江南地区的富庶,加上轻工业投入相对较少,资金回笼快,中外资" +607,"。张江高科汇集了大量的高端制造业。 目前,在交通工业方面,上海大众、上海通用成为中小型乘用车生产的重要基地,申沃客车则是重要的大型车生产基地,而上海阿尔斯通交通设备有限公司则承担了轨道交通方面的生产。而在航空工业领域,上海是中国航空工业的研发基地,是大型国产客机的生产商中国商用飞机有限责任公司的总部基地。此外,江南造船厂和沪东中华造船厂领衔的造船工业也是上海的一大支柱产业,年造船能力超过千万吨。而在冶金和石油化工方面,上海宝钢和上海石化也居于国内行业的领先地位,宝钢集团更是在全球钢铁企业中位列第三。 === 第一產業 === 上海由于属于亚热带季风气候,一年四季分明。全年平均气温15.5℃,自有气象记录以来,最高气温为40.6℃,最低气温为-12.1℃。常年11月中旬初霜,次年4月上旬终霜,年均无霜期225天。年均日照时达1934.4小时,太阳辐射总量4670兆焦耳/平方米,其中光合有效辐射占一半。年均有效积温2630℃·日。雨量充沛,年雨日130天,平均年雨量1145.4毫米。光、热、水基本同季,农业气候条件比较优越。自古代以来,上海一直为鱼米之乡,水稻、渔业、蔬菜的产量都相对较高。" +608,"上海市实现农业总产值325.37亿元。其中,种植业174.56亿元;林业9.71亿元;牧业67.65亿元;渔业61.55亿元;农林牧渔服务业11.9亿元。上海域外市属农场实现农业总产值18.81亿元。全年全市粮食播种面积168.51千公顷;粮食产量114.15万吨;水产品产量27.59万吨。至2013年底,上海市有1069家企业、4797个产品获得农产品质量认证。其中,绿色食品生产企业151家,绿色食品215个;无公害农产品生产企业911家,无公害农产品4557个;上海累计建成标准化畜禽养殖场320家,标准化水产养殖场217家;累计建成设施粮田面积86.5千公顷,蔬菜标准园107家。至年末,全市有农业产业化龙头企业288家,农民专业合作社3200家。 == 人口 == 上海市各区(县)人口数据 区划名称 常住人口(2020年11月) 常住人口(2020年11月) 常住人口(2020年11月) 户籍人口(2010年末) 区划名称 总计 比重(%) 每平方公里人口密度 户籍人口(2010年末) 上海市 24870895 100 3630.50 14123202 黄浦区 662030 2.7" +609,"77.71万名常住外来人口以及1450.43万名常住户籍人口,是中華人民共和國非戶籍常住人口最多的城市。目前,上海市常住人口中少数民族共有11.8万人,其中回族约有7万余人。除常住人口以外,持居住证的外来人口为103万人。另外有16万外国人常年居住于上海。 2020年末,根據第七次全国人口普查主要数据公布如下:上海市常住人口为 24870895 人,同第六次全国人口普 查的 23019196 人相比,十年共增加 1851699 人,增长 8.0%。 平均每年增加 185170 人,年平均增长率为 0.8%。 在人口结构方面,上海正面临严重的-{zh-hans:老龄化; zh-hant:老齡化; zh-tw:高齡化;}-问题。现今户籍设在上海的人口其平均期望寿命为83.66岁,其中男性和女性分别为81.27岁与女性86.14岁,而65岁及以上人口則有232.98万人,占总人口的10.1%。至2019年百歲老人數量為2657人。预计到了2030年時60岁以上的老年人口将超过500万人。 上海市的生育率处于极低水平。根据人口普查和抽查,2000年以来,上海市总和生育率处于0.67和0.74之" +610,"籍人口自然增长率自1993年后一直为负增长,但上海每年以引进人才的方式给予2万余人户籍,户籍人口依旧保持了连年增加。上海曾长期对户籍进行严格控制,但近年来已有所放宽。另外随着流动人口的剧烈增加,上海市也正面临种种人口问题,首先上海市的土地、水源、环境、交通等资源承受沉重压力,且大部分犯罪为流动人员所为,对上海的治安造成了影响。数据显示,截止2012年上海中心城区人口密度是东京的2.4倍、伦敦的3.5倍、巴黎的4.8倍,而以2012年为基点,上海人口或许在3年内超过城市承载量。 === 语言 === 普通话为中华人民共和国的通用语言,在学校教学及正式场合使用。而市民在日常生活中常使用本地方言。上海话简称沪语,語言學上屬於吴语,一般意义上是指近代以來上海租界範圍和周邊使用的一种现代吴语,由於上海是遠東的國際大都市,所以上海話由各地吴语的方言和英語融合而成。上海话也和旗袍、洋房、弄堂等一样,成为了上海风情的代表性符号。 如今上海话的使用范围面临多方面的压缩。伴随外地人的大量湧入,本地户籍人口比例逐渐减小,加之政府有意在公共場所限制上海話的使用,使得普通话越来越成为公共场合语言。由于國家禁止校" +611,"始建于三国时期的龙华寺历史最为悠久。而1949年时,上海尚有道观236座,如今仅存27座,最著名的非正一道的城隍庙莫属,每年都会举行盛大的庙会活动,周边也已是沪上知名的商业区域,各种小吃餐厅、古玩商铺和小商品市场鳞次栉比,成为重要的旅游景点,另一著名道观为全真道的海上白云观。伊斯兰教最早随宋代在上海镇经商的大食商人进入上海,现有7坊清真寺,信徒集中于回族、维吾尔族等10个少数民族。 天主教和基督教新教则为近代由传教士传入。明万历三十六年,来自意大利的天主教传教士开始在沪传道。1949年以后,由于罗马教廷和中国政府长期不和,天主教在沪发展的较为艰难。在上海现存的107座天主教堂中,地处上海西郊佘山的圣母大殿,气势宏伟,为远东最大的天主教堂,也是重要的圣母朝圣地。而基督教新教在上海出现得最晚,是在开埠的1843年由英国传教士传入的。上海的基督教堂多达169处,有相当一部分成为了上海市文物保护单位,且大多位于市中心,因而也被游客视为热门的旅游景点。 File:Chenghuangmiao2.jpg|上海城隍廟 File:Longhuasi.JPG|上海龍華寺 File:Community C" +612,"重新活跃,目前全市共有运营汽车达5万辆,出租车-{A|zh-hans:扬招点; zh-hant:招呼站}-也陆续设立。此外轮渡也曾为重要的交通方式,用以连接黄浦江两岸,以及服务于崇明岛和上海市区之间,但随着跨黄浦江和长江的隧道与桥梁陆续建成,轮渡对于客运的重要性已大不如前,取而代之的是黄浦江上行驶的众多观光游船。 === 公路 === 上海市区内由南北高架(南北向)、延安路高架(东西向)和内环高架(环线)组成全长55公里形状如“申”字的主干道,并仅供汽车通行。由内环线向外扩散,依次为中环线、外环线、绕城高速公路。另外还尝试以地下通道的方式来缓解路面交通的阻塞。 由于河流纵贯,上海建有诸多桥梁和隧道,横贯市区的苏州河上方就有外白渡桥等数十座桥梁,黄浦江上的第一座大桥是1975年建成的松浦大桥,而下游的首座大桥迟至1991年才建成,截至2012年,黄浦江上的桥梁和隧道数量已超过20。2003年,卢浦大桥建成,曾是世界上主拱桥最长的拱桥。2009年,上海长江隧桥建成,结束了崇明岛只能依靠船舶往来浦江两岸的历史。 上海市区通过十余条省级高速公路连接市郊或邻近城市,此外还有多条国家级高速公路和国" +613,"是1876年建成的吴淞铁路,同时也是中国最早运营的铁路。至1909年沪宁铁路、沪杭铁路均已通车,形成了现有进出上海的铁路干线。 上海现有京沪(北京-上海)和沪昆(上海-昆明)两条既有铁路干线,以及金山铁路、浦东铁路等市内运营的线路。高速铁路方面,沪宁(上海-南京)/沪汉蓉铁路(上海-武汉-成都,其中上海至南京段与沪宁高铁并线)、京沪高铁(北京-上海)和沪昆高铁(上海-昆明)线路已相继通车。 === 海运 === 上海港自开埠后迅速成为中国的最大港口,码头集中于黄浦江沿岸,后由于黄浦江水深已不符合大型集装箱船的要求,上海于2005年在东海的岛屿上建成洋山深水港,通过32公里长的跨海大桥与大陆相连及并使用专业的海港油库为来往船舶提供加油。该港的口岸、港政、航政归上海市,但所在的嵊泗县行政上属浙江省。到2008年,上海港拥有各类码头1200余个,万吨级以上生产性泊位137个,码头线总长115公里,年吞吐量58170万吨,最大靠泊能力15万吨级。至2010年,上海港已成为世界最大的集装箱港。 除货运外,上海港每月定期发出客运航班2258次,每年有国际邮轮58班次。2019年11月28日,上海市" +614,"85工程院校。2012年10月,中国第一所中美合作的国际化大学上海纽约大学在浦东陆家嘴成立。2013年9月,上海市与中国科学院共同建立了小规模、国际化的研究型大学上海科技大学。 基础教育方面,上海適齡幼兒、小学、初中学生的净入学率接近100%,高中阶段教育入学率为98%,普通高等学校在校人数超过百万,毛录取率约为70%,在2009年与2012年国际学生能力评估计划中,上海学生的数学、科学、阅读能力在参与测试的国家和地区中全部名列第一。 全市现有100多所科研机构,10万科研人员,及100多所专业技术培训机构。在上海产生了遗传学家谈家桢、显微外科专家陈中伟、张涤生、肝胆外科专家吴孟超、血液学专家王振义、陈竺等著名科学家。 上海的公立科研和设计机构有: * 中国科学院上海光学精密机械研究所 * 中国科学院上海硅酸盐研究所 * 中国科学院生物化学与细胞生物学研究所 * 中国科学院上海天文台 * 中国科学院上海微系统与信息技术研究所 * 中国科学院上海应用物理研究所 * 中国科学院上海药物研究所 * 中国科学院上海有机化学研究所 * 中国科学院上海技术物理研究所 * 中国科学院植物生理生态研究" +615,"契机,上海市政部门对城市景观、道路、居民住宅等均进行了大规模的整修。上海历史上遗留下了大面积的棚户区,在1990年代的对其中365万平方米的危棚简屋进行了改造,但是曹家村、光复路一代的棚户区由于成本问题始终未能动工,与周边高楼行成鲜明对比的城中村景观留存至今。 上海开埠以前,园林绿化全部都为私家园林,最为著名的有豫园、露香园、古猗园、秋霞圃、醉白池等等;开埠后,公共园林开始出现,1868年诞生的公共花园为第一座公园。但1928年前,中国人不得进入工部局和公董局兴建的公园。目前,上海全市除上海动物园、上海野生动物园等个别公园外,已经取消公园门票。自2000年以后,为了净化城市空气,美化环境,上海开始在城市中心和郊区兴建一批开放绿地。到2008年,上海全市绿地总面积34256公顷,公共绿地14777公顷,城市绿化覆盖率为38%。全市林地总面积到近10万公顷,森林覆盖率11.6%。 为解决自1990年代开始大规模市政建设对资金的大量需求,上海市政府部分官员主导了对上海的投融资体制的大幅改革,先利用政府手中的土地资源,引入土地批租的融资方式。期间,上海通过土地批租共筹措资金1000多亿元。19" +616,"人感染和31人死亡,疫情过后上海市政府总结经验并逐步完善了突发传染病疫情的防控预警机制以及应急处理预案。2003年中国大陆SARS事件期间,得益于先前甲肝大爆发取得到经验和防控体系,上海市的防疫工作得力使得上海未发生大规模疫情,因而获得世界卫生组织在内的各界肯定和好评。2013年长三角地区发生了H7N9流感疫情,上海卫生部门反应迅速,及时扑杀相关市场内的禽鸟以切断传染源,很快就基本消除了疫情。2022年,上海因奧密克戎變異株疫情而封城。 截至2016年,上海户籍人口的人均期望寿命已超过83岁,位列中国大陆第一位,专业公共卫生机构达112所。上海居民三大健康指标(期望寿命、孕妇死亡率和新生儿死亡率)均保持在发达国家水平。 === 社会保障 === 从1990年代以来,上海开始建立并逐步完善了现代化都市型的社会保障体系。目前,上海个人和单位住房公积金缴存比例各为5%至7%;补充住房公积金缴存比例各为0%至5%。单位职工各项社会保险费缴费比例之和为45.5%。上海市现行月最低工资标准是2690元/;城镇居民和农民最低生活保障金为每月1595元。城镇医疗保险的最高支付封顶线为39万元,超过部分" +617,"新报》创刊。1872年,近代影响力最大的报纸之一的《申报》创刊。至1936年,《申报》《新闻报》《时报》《时事新报》《大公报》等五大报纸,每日销售总数即达到50万份。至1937年淞沪会战开始后,《申报》等由于不接受日军的新闻审查而停刊。1941年上海完全沦陷后,上海报业基本停运。直到1945年才恢复,至1949年5月,上海全市有各类报纸70余种,为全国报业中心。此外,民國時期长期发行的著名杂志有《东方杂志》、《科学》、《中华教育界》、《教育与职业》、《生活》、《世界知识》、《现代评论》等。 1949年中国人民解放军接管上海以后,由于《申报》因属于中国国民党党产而被没收停刊,上海报业也开始转向全面国有,到了1960年代中期以后,全市只有《解放日报》和《文汇报》两种。1979年起,上海大批报纸开始复刊。 目前上海主要的两大报业集团,解放日报和文汇新民两大报业集团,合并为上海报业集团。《解放日报》和《文汇报》等保留了党报性质,在一些企事业单位实行强制订阅。而较为贴近平民的报纸有《新民晚报》、《新闻晨报》等,以《新民晚报》的发行量为最大。 ==== 广播电视 ==== 1923年1月23日在上" +618,"台全部并入上海文广新闻传媒集团(今上海广播电视台),几乎控制了上海市全部的广播电视节目播出。各电视台也进行明确分工,原本竞争的格局不复存在。 === 体育 === 参见:上海体育场馆 上海开埠后,近代体育传入上海,后通过新式学校的教学活动在华人学生中开始,促进了上海体育水平的提高。1936年在柏林举办的第11届奥林匹克运动会上,中国代表团的三分之一为上海运动员。 1949年以后,在有较高群众基础的等领域,培养了篮球运动员姚明、乒乓球运动员王励勤、游泳冠军乐靖宜、跳水运动员吴敏霞、田径运动员刘翔、中国象棋冠军胡荣华,围棋冠军常昊,以及徐根宝、范志毅、孙雯、谢晖等著名足球教练员和运动员。1990年代开始,上海开始尝试商业化体育运转模式,并于1993年成立了国内首家职业足球俱乐部——上海申花足球俱乐部。1990年代起,各类健身器材开始在社区内出现,并逐步将学校场地对公众开放,到2008年,全市开放各类学校体育场地已有1100多处。 在1903年,中国最早的相对专业的足球队在上海出现。目前上海在足球、篮球、排球、乒乓球和棒球上有专业的职业俱乐部及代表队。中超的上海申花队、上海海港队;中国男子篮" +619,",成為在亞洲唯一的,等級最高的網球盛典。 队伍 联盟 运动 主场 成立年份 上海申花 中国足球超级联赛(CSL) 足球 上海体育场 1951年 上海大鲨鱼 中国篮球职业联赛(CBA) 篮球 源深体育中心 1996年 上海海港 中国足球超级联赛(CSL) 足球 上汽浦东足球场 2005年 上海龙 守望先锋联赛(OWL) 守望先锋 -- 2017年 == 文化 == 上海的文化被称为“海派文化”。海派文化的主体,根植于江南地区传统的吴越文化,并且融入了开埠以后来自欧美各国的西方文化,而逐步形成的一种不同于中国其他地区的独特文化。而海派的来源,有说法指于清末民初的海上画派有关。海上画派的前源是以董其昌为代表的松江画派。后随着上海商业的繁荣,越来越多的人开始以卖画为生,海上画派之名渐广为人知。 === 博览文化 === 1860年代,法国人在上海创立中国第一间博物馆以展出长江流域的动植物标本,现在全市已有博物馆百余座。位于人民广场中心地带的上海博物馆有100多万件馆藏文物,为上海最大的博物馆。另外自1985年开始举办的上海车展也颇有影响力。除上海历史博物馆、上海科技馆、上海市城市建設檔案館等大" +620,"为其代表,宁波风格料理则可以在宁波汤团店品尝到。 而西方饮食文化随同上海开埠进入这座城市,1853年开业的老德记西餐馆为沪上首家西餐厅。最初西餐一般以应对外国顾客为主,1910年德大西餐馆开业,成为面向各界民众的西餐馆。十月革命后,由于大批苏俄侨民涌入,俄式西餐一度成为大众西餐的典范,其中罗宋汤颇受平民喜爱。目前,上海经营各国菜肴的餐馆为数众多,风格来自世界各地。而西方糕点随同西餐传入后由于其价格较之正式西餐相对低廉,受到普通民众的喜爱。 上海民众日常饮食的主食以米饭为主,偶尔以馄饨、面条等作为调节。传统家庭早饭以隔夜的剩余米饭加水制成泡饭,并辅以酱菜、咸蛋和腐乳等。如在小摊提供的早餐则以豆浆、油条、大饼、粢饭最为常见,因而被沪上民众称为“四大金刚”。 === 文学 === 魏晋时期,华亭县人陆机、陆云曾为当时一代文豪。而自1919年后,由于租界独特的政治制度,吸引了众多名人,上海很快成为当时新文化运动基地,聚集了左联、新感觉派、鸳鸯蝴蝶派文学流派,鲁迅、郭沫若、茅盾、巴金、叶圣陶、郁达夫、王造时、丁玲、张爱玲等名家也在上海从事创作,并产生了《子夜》等以上海为背景的著名作品。 1980" +621,"、江苏、浙江一带逐渐出现了独角戏,多由一人以吴语方言。在1920年代末期,通过吸收话剧艺术和传统说笑艺术,在上海产生了滑稽戏,主要使用上海话和其他吴语方言。1912年开业的大世界游乐场后逐渐成为重要的演出场地。 而随着苏州的四大昆班、徽班的皮黄、京剧艺术等陆续进入上海。评弹也籍着语言与地理位置的优势,从苏州扩展到上海,并随着商业电台的普及,评弹艺术迅速发展,到1930年代,当时每天的评弹节目多达103档。1920年代以后,上海并逐渐形成了以周信芳、盖叫天为首的海派京剧风格,并吸引了梅兰芳等京剧大家常住上海。同一时期,浙江嵊县(今浙江嵊州)小歌班也在上海舞台上开始发扬越剧这一新剧种。而上海本地的传统唱腔在融合了现代戏剧形式后,形成了上海特有的沪剧。 至2012年,上海主要的传统剧团有上海京剧院、上海昆剧团、上海越剧院、上海沪剧院等,昆剧团经常赴其他国家和地区进行演出,京剧交流活动也时常举办,同时借助于电视媒体,出现了《越女争锋》等选秀活动。上海滑稽剧团、上海人民滑稽剧团和上海青年滑稽剧团等也开始借助于电视进行演出,尤其《老娘舅》节目自1995年起连演12年,成为上海电视上最长寿的电视节目" +622,"剧《大哥》,带有怪诞色彩的黑色喜剧《秀才与刽子手》等等。众多充满海派风情的话剧作品纷纷在包括上海大剧院、东方艺术中心、上海人民大舞台、艺海剧院、上海话剧艺术中心、兰心大戏院等在内的上海各大表演中心上演,其中亦有不少精彩的精品走出国门,在各国巡回演出,获得好评无数。 === 音乐 === 上海的早期音乐以传统的江南丝竹为主,上海开埠后,西洋音乐传入。1878年,上海管乐协会成立管乐队,即今上海交响乐团的前身。进入1930年代,上海流行音乐进入繁荣时期。《夜上海》、《苏州河畔》、《夜来香》、《何日君再来》、《玫瑰玫瑰我爱你》、《如果没有你》、《恭喜恭喜》、《明月千里寄相思》等,均创作并流行于这一时期。除流行音乐以外,鉴于当时局势,上海的大部分音乐人也兼创作爱国歌曲,出现了《毕业歌》、《大刀进行曲》等作品。现在用作中国国歌的《义勇军进行曲》也是在由知名音乐家聂耳创作于上海。 1949年以后,上海的流行音乐以及电影原声音乐为红色歌曲所取代,少有新作品诞生,这一现象一直持续到“文化大革命”结束。至1978年,“上海之春”音乐舞蹈节恢复举行,即后来的“上海之春”国际音乐节。随着音乐教育的恢复,上海" +623,"迅速发展,阮玲玉、胡蝶、周璇、赵丹、上官云珠、黎明晖、王人美等成了当时中国家喻户晓的明星。大量私营电影则1950年代被整关闭。上海的电影业界先主要由国有的上海电影制片厂、上海美术电影制片厂、上海电影译制厂等构成,并于2003年组建成上海电影集团。1949年后诸多著名革命题材电影如《南征北战》《红色娘子军》《鸡毛信》等为上海电影制片厂所拍摄,而成立于1957年上海美术电影制片厂是中国规模最大的美术电影制片基地,创作了《小蝌蚪找妈妈》《大闹天宫》《哪吒闹海》《天书奇谭》《三个和尚》《宝莲灯》等经典作品。 随着全球化的发展和海内外的电影产业进一步崛起,越来越多的中外电影包括《-{zh-cn:变形金刚2; zh-tw:變形金剛:復仇之戰; zh-hk:變形金剛:狂派再起; zh-sg:變形金剛:復仇之戰;}-》、《太平轮》、《碟中谍3》、《小时代》、《-{zh-cn:谍海风云; zh-tw:上海; zh-hk:諜海風雲;}-》、《神奇四侠2》、《如果·爱》、《面纱》、《七月与安生》、《黄金时代》、《十月围城》和《007:大破天幕杀机》等等作品纷纷前往上海取景拍摄。 上海國際電影節(英語:Sha" +624,"文化节日,主要进行国际旅游推广活动。上海国际艺术节期间会举行多场画展、音乐会、艺术展,并聘请各国艺术家登台献艺。每年8月中旬会在上海展览中心举行上海书展,而上海国际音乐烟花节通常在世纪公园举行。此外,自2001年起,上海时装周于每年的10月份在上海市區舉辦。 === 建筑 === 上海的古代建筑,目前基本遗留的以明代建筑为主,而且大部分为民居和私家园林。除官署、学堂、寺庙外鲜有其他类型的建筑。民居大多为江南风格,砖木立帖式平房,在上海,除老城厢以外,在各郊区的乡镇中仍能见到较多的此类建筑。上海的私家园林目前保存较为完整的为豫园,而古代居民区保存较为完整的有闵行七宝镇、青浦朱家角镇、浦东新场镇等。 上海开埠以后,随着租界建立和欧美文化大举进入,一些西式别墅和洋房开始兴建,上海也开始出现各种不同风格的建筑,因而被誉为“万国建筑博览会”,其中尤以外滩地区的金融建筑最为著名,诸如中式的中国银行原总行大楼,如折衷主义风格的江海关,新古典主义风格的汇丰银行上海分行大楼等,采用美国装饰艺术运动摩天楼风格的沙逊大厦等,现大量作为上海市优秀历史建筑予以保护。因为人口大量增加,这个阶段也产生了极富上海特色" +625,"0111).jpg|外滩汇中饭店大楼(左)、沙逊大厦(中)和中国银行大楼(右) File:20090427 5361 Shanghai Oriental Pearl Tower.jpg|东方明珠广播电视塔 File:Shanghai skyscrapers 5166285.jpg|金茂大厦(左)、上海环球金融中心(中)和上海中心大厦(右) File:Soho Building (188097355).jpeg|凌空SOHO == 旅游 == 上海在租界时期遗留了众多欧式建筑,而1990年代起又出现了诸多现代化建筑,同时,周边郊区分布着几处具有江南风格的水乡古镇,均成为了旅游景观。上海于1986年被国务院定为国家历史文化名城。丰富的旅游资源吸引了各国游客,到2012年,接待入境的国际旅游者达到800万人次。 上海的东方明珠广播电视塔、上海野生动物园、上海科技馆3处属中国最高的5A级旅游景点,另有20处4A级景区,佘山、东平、共青、海湾等4处国家森林公园,以及沙逊大厦、外白渡桥等617处上海市优秀历史建筑。 上海也是一個夜景都市。浦西的外滩夜景以及浦东陆家嘴的摩天高楼夜景相隔黄浦江而分布," +626,"上海野生動物園 **中国馆 **上海迪士尼度假区 **新场镇 == 人物 == == 上海主题条目索引 == ;地理水文 * 上海行政区划 * 上海地理 * 长江口 ** 黄浦江 *** 苏州河 * 崇明岛 * 天馬山 ;历史政治 * 上海历史 **上海历史年表 * 上海市人民政府 ** 上海市市长列表 * 中国共产党上海市委员会 * 上海友好城市列表 * 上海領事機構列表 ;经济社会 * 上海经济 * 上海交通 ** 上海机场 ** 上海轨道交通 * 上海人口 ** 上海人 ** 上海外来人口问题 ;文化教育 * 海派文化 * 上海城市精神 * 上海教育 * 上海话 * 本帮菜 * 上海建筑 * 上海世界博覽會 == 注释 == == 参考文献 == == 延伸阅读 == * Marie-Claire Bergere(白吉爾)著,王菊 等 譯. 《上海史:走向現代之路》(上海:上海社會科學院出版社,2005) * 梁元生. 《晚清上海:一個城市的歷史記憶》(香港:中文大學出版社,2009) * 梁元生 著,陳同 譯. 《上海道台研究——轉變社會中之聯繫人物,1843–1890》(上海" +627,"消解 * 连玲玲. 从零售革命到消费革命:以近代上海百货公司为中心 * 李培德. 上海城市的历史记忆和文化认同——以1949年前的上海为讨论中心 == 外部链接 == ;概览 * 中国上海网:上海概览 * 上海市地方志办公室 ;政府 * 上海市人民政府门户网站 * 上海人大 * 上海政协 * 上海市人民政府新闻办公室 ;旅游 * 上海市旅游局网站 * * ;媒体 * 东方网 * 新民网 * 上海观察 * 青年报 * 上海广播电视台 ; 地图及卫星照片 * 谷歌地图 * Category:中華人民共和國直轄市 沪 Category:東海沿海城市 Category:长江三角洲城市群 沪 Category:中国沿海开放城市 Category:国家园林城市 Category:国家中心城市 沪 Category:10世紀建立的聚居地 Category:中国港湾城市 Category:华东 Category:江南" +628,"歷史學家,又稱歷史家、史學家、史家,是指以撰写历史著作为职业或对历史学的创立、发展与应用付出努力的知识分子。 ==概念== 历史学家包括历史记录的编撰者和史料的研究者。人們研究歷史必須倚靠前人所留下的記錄。歷史學家會研究過去所發生的事件和這些事件記錄的真確性,並將他們的研究記錄下來。歷史學家的研究對象可以是某人的經歷,某城市、某地或某國家的發展。根據他們不同的研究對象,歷史可有不同的分類,例如:個人歷史,是有關某人過去發生的事做研究;地方歷史,是有關某城市或某地曾發生事件的研究;世界歷史,是有關世界各地過去事件的研究。 ==古中國歷史== 在古代中国,历史学的传统主要是以历史编撰的形式创立和发展的,在西漢历史编撰者司马迁之后,唐代的刘知幾开创了另外一种门类的工作,即对历史编撰这项工作本身的研究(史学)。在宋代,开始了以往的历史记录为材料的针对“历史”的研究活动。在清代,也有史學家劉鶚研究商代甲骨文。 ==古希臘/羅馬歷史== 而在古希臘/羅馬,歷史學家向來被視為文學家的一種,撰寫歷史的首務是將人物或事件寫的生動,甚至某些「修飾」是被認為理所當然的。 ==歷史分析== 歷史分析的過程比較" +629,"開放源代码()是一种在电脑程序出品前和开发中提供其内部程式碼的做法。 开放源代码在互聯網上獲得廣泛使用,參加者需要大量更新電腦原始碼。開放原始碼使得生產模組、通訊管道、互動社群獲得改善The complexity of such communication relates to Brooks' law, and it is also described by Eric S. Raymond, Brooks predicts that as your number of programmers N rises, work performed scales as N but complexity and vulnerability to bugs rises as N-squared. N-squared tracks the number of communications paths (and potential code interfaces) between developers' code bases. — The Revenge of the Hackers . 2000.。隨" +630,"提出的Open Source作為共通名稱。 == 开源软件列表 == === 應用軟體 === 三維繪圖及渲染 * Blender 整合式開發環境 * Eclipse * Lazarus 圖像處理 * GIMP 視頻編輯 * OpenShot * Kdenlive 桌面出版 * Scribus 繪圖軟體 * Krita * LibreCAD * FreeCAD 矢量圖形編輯器 * Inkscape 數位聲波編輯 * Ardour * Audacity * OpenAL 純文字編輯器 * Emacs * Vim 電子郵件客戶端 * Mozilla Thunderbird 虛擬地球 * NASA World Wind 線上直播 * Open Broadcaster Software 桌面搜尋 * 遊戲引擎 * Godot === 操作系统 === * MINIX——為Linux之前的操作系统。 * Linux——类Unix操作系统家族。 * Android——基於Linux的操作系统。 * Firefox OS——基於Linux的操作系统。 * FreeBSD——類Unix的操作系统。 *" +631,"都能夠輕易地獲取它的配方并对其做出修改。開源可樂發行運動的最初目的是推廣自由和開源軟體。 == 参考文献 == === 引用 === === 来源 === ; 书籍 * 葛林·穆迪(Glyn Moody)著,杜默 译:《Linux传奇:让比尔盖茨坐立难安的天才》,时报文化,2001年,台北,ISBN 9571333628 * 林納斯·托瓦茲(Linus Torvalds)著,梁曉鶯 譯:《Just for Fun—Linux創始人托瓦茲自傳》,經典傳訊,2001年,ISBN 978-957-476-123-4 == 外部链接 == * 开放源代码促进会 * 《开源软件文集》 * 台湾中央研究院 自由軟體鑄造場 * 中国的LUPA开源社区 供参考 * OSMSG开源资讯 * 10 things people get wrong about open source (images) (10個對於開源碼的錯誤印象) * 自由開源軟體侵權警告與因應流程 * DARPA Open Catalog - 美國國防高等研究計劃署 == 参见 == * 开源软件 * 内容开放 * 开放源代码软件列表 *" +632,"运算数学是数学的一个分支,研究的内容包括设计和分析算法以及数学建模等,目的是为了在实际工程中利用快速稳定的算法得到精确值的近似值。在运算机科学高度发展的今天,其基础运算理论的发展使运算数学进入现代化阶段。 ==运算数学的几个主要方面== *逼近论与函数逼近 *非线性方程求解 *矩阵及特征值理论 *最优化及其算法 *微分方程及其数值解 *建立在上面基础之上的数学建模及其应用 ==参见== * 计算科学家 * 信息与计算科学 ==参考文献== ==延伸閱讀== * * * * * * R. E. White (2003), Computational Mathematics: Models, Methods, and Analysis with MATLAB, Chapman and Hall. ISBN 978-1-58488-364-7 * X. S. Yang (2008), Introduction to Computational Mathematics, World Scientific, (2008). ISBN 978-9812818171 * G. Strang (200" +633,"高德納(,音譯唐納德·爾文·克努斯,),出生於美国密尔沃基,著名计算机科学家,斯坦福大学计算机系榮譽退休教授。高德纳教授為现代计算机科学的先驅人物,創造了演算法分析的領域,在數個理論計算機科學的分支做出基石一般的貢獻。在计算机科学及数学领域发表了多部具广泛影响的论文和著作。1974年圖靈獎得主。 高德纳所写的《计算机程序设计艺术》()是計算機科學界最受高度敬重的參考書籍之一。他也是排版軟件和字型設計系統Metafont的发明人。此外,他还曾提出文学编程的概念,並創造了WEB與CWEB軟體,作為文學編程開發工具。 ==名稱== 高德納的英文名直译为唐纳德·爾文·克努斯(Knuth發音為//),「高德納」這個中文名字是1977年他訪-{}-問中國之前所取的,命名者是儲楓(姚期智的夫人,計算機科學家,2004年至2011年在香港城市大學電腦科學系擔任系主任 )。 == 简历 == === 早年 === 高德納出生於於美國威斯康辛州密西根湖邊的密爾瓦基。密爾瓦基是一個山靈水秀、人才輩出的地方,諾貝爾獎得主司馬賀(H. A. Simon)也是在這裡出生的。他父親 Ervin Henry Knut" +634,"好的編譯程式,便動手開始做。 1959年,高德納成了第一屆Engineering and Science Review 期刊的編輯,該期刊是當時最好的工程科學期刊,獲得了國家獎。高德納從主修物理改成主修數學。1960年, 高德納從凯斯理工學院畢業時,由於他的傑出表現,同時獲得學士與碩士學位。 === 博士生研究時期 === 1960年,高德纳進入加州理工學院研究所,研究所二年級時,為私人公司寫編譯器,賺得5,000元美金。1962年1月Addison-Wesley公司的顧問理查·瓦嘎(Richard Varga)請他出版一本有关编译器的书籍。1963年,高德纳获得加州理工學院数学博士学位,指導教授是數學家。 === 學術成就 === 高德纳畢業之後留在加州理工學院任教,并在数学与计算机程序设计领域取得多项成就。 1966年,当高德纳的著作已经长达3000多页时,他与出版商商定,编纂一部系统地介绍计算机程序设计的巨著《计算机程序设计艺术》。截至2018年12月,该书已经出版了4卷,并对计算机领域产生了深远的影响。高德纳本人预计第5卷将会在2025年完稿。《科学美国人》杂志曾将该书与爱因斯" +635,"”,他设立了0.32美元的奖金。高德纳可以算是一名标准的黑客,他最喜欢的软件是Emacs,并曾向其作者理查·史托曼提交-{zh-hans:修补补丁; zh-hant:修改提案}-。 与大多数传统黑客一样,高德纳酷爱音乐。高中的時候,Knuth興趣所在並非數學,而是音乐,尤其是聽音樂和作曲。他一度还曾考虑报考音乐专业。他在他的书房中放了一个特别定制的84管的管风琴。除此之外他也会吹萨克斯风和大号。 高德納的作品《歌曲的计算复杂度》(The Complexity of Songs)曾兩度刊印在计算机协会期刊上。 * 重刊印: == 著作 == 高德纳的《计算机程序设计艺术》計劃出版7卷,该书自第3卷于二十世纪七十年代初版以来,一直沒有下一卷的進展。因此他自1990年起就停止使用电子邮件(高德納自1975年就開始使用電子郵件)以专心撰写余下的4卷。 在计算机科學以外,高德纳亦著有論述基督教信仰的書籍,如《3:16 Bible Texts Illuminated》(1991年,ISBN 0-89579-252-4),以及《Things A Computer Scientist Rarely T" +636,"gory:美国数学家 Category:图灵奖获得者 Category:法兰西科学院院士 Category:凱斯西儲大學校友 Category:加州理工學院校友 Category:组合学家 Category:计算机科学教育家 Category:美国国家科学奖获奖者 Category:IEEE约翰·冯·诺依曼奖获得者 Category:京都奖获得者 Category:富兰克林奖章获得者 Category:古根海姆学者 Category:密尔沃基人 Category:巴勒斯公司人物 Category:康考迪亚大学荣誉博士 Category:牛津大學計算機系教師" +637,"中國,傳統觀念指位於天下正中的國家。原指包括河南省及附近的黄河中下游流域地區《辞海》释“中原”:古称河南及其附近之地为中原,至到东晋南宋亦有统指黄河下游地區为中原者。。歷代王朝政權透過與周邊各政權的交流與征戰,中國的疆域版图几经扩张与缩减,目前擴及黑龍江流域、塞北、西域、青藏高原及南海諸島等地。現今,国际上广泛承认代表「中国」的政权是中华人民共和国。 华夏是人类的文明摇篮之一。 新石器时期,中原地区开始出现聚落组织Li Liu,《中国的新石器时代》,剑桥大学出版社:剑桥,2004,ISBN 0-521-81184-8.(英文);公元前27世纪左右出现方国,方国制度以共主為首;前20世纪开始,古代中国进入世袭的封建阶段;公元前2世紀,秦滅六國,完成中國第一次大一統。此後两千年來,中國的政治制度以半傳統的夏代為基礎的世襲君主制運作,此後经多次擴大,破裂,重組,朝代更迭,經過數次统一与分裂交替进行。直到1911年辛亥革命後建立中華民國,中國的政體從君主制轉為共和制。1945年第二次國共內戰爆發後,中國共產黨逐漸控制中國的大部分領土;1949年10月1日,由中國共產黨執政的中华人民共和国建立," +638,"長期的歷史發展中,中國形成了多元一體的中華文化格局。作为其主体的汉文化对日本、朝鮮和东南亚有深远影响,形成漢字文化圈。中国传统艺术形式有国乐、相声、戏曲、书法、国画、文學、陶瓷藝術、雕刻等,传统娱乐活动有象棋、围棋、麻将、中国武术等。茶、酒、菜和筷子等为中国的特色饮食文化,春节(阴历新年)、元宵、清明、端午、七夕、中秋、重阳、冬至等为中国传统节日。中国传统上是一个儒学国家,以夏历为历法,以五伦为道德准则。春秋时期孔子「有教无类,因材施教」开始办私塾培养人才,汉朝时采用察举推选政府官员,隋朝起实行科举在平民中选拔人才。此外,中国歷朝歷代都设有史官,因此保存有十分详尽的历史资料,如《二十四史》、《资治通鉴》等。古代中國在科學領域上也有豐厚的成就。 == 名稱 == 「中國」一词目前最早見於公元前11世纪商周之際,此前则难以考证。在殷墟甲骨文中已有“四方风”、“西或(西面的国家)”、“东或(东面的国家)”等词汇,殷人则自称“中商”张兴照. 甲骨文所见殷人空间观念[J]. 黄河科技大学学报, 2017, 19(4):8.。西周初年的何尊中周成王提到“余其宅兹中国”,意为“我要到中国(殷商故地)" +639,"余其宅兹中國,自兹乂民。”這個「中或(中國)」指的是以洛陽盆地為中心的中原地區(此處「國」作「地域」解)。春秋戰國時期,中國主要指諸夏列邦之全體秦統一前「中國」主要指諸夏列邦之全體,並包括其所活動之全部領域。載於《中國近代思想史論》臺灣,商務出版,1995年,第448-449頁。另參: 《毛詩正義》註《詩經》:「中國之文,與四方相對,故知中國謂京師,四方謂諸夏。若以中國對四夷,則諸夏亦為中國。言各有對,故不同也。」 《左傳·莊公三十一年》:「六月,齊侯來獻戎捷,非禮也,凡諸侯有四夷之功,則獻于王,王以警于夷,中國則否,諸侯不相遺俘。」 《左傳·閔公元年》:「狄人伐邢,管敬仲言於齊侯曰,戎狄豺狼,不可厭也,諸夏親暱,不可棄也,宴安酖毒,不可懷也,《詩》云,『豈不懷歸,畏此簡書』。簡書,同惡相恤之謂也,請救邢以從簡書,齊人救邢。」 《公羊傳·僖公二十一年》:「執宋公以伐宋。孰執之?楚子執之。曷為不言楚子執之?不與夷狄之執中國也。」 《左傳·僖公二十五年》:「陽樊不服,圍之,蒼葛呼曰,德以柔中國,刑以威四夷。」 《左傳·僖公二十八年》:「君子謂(晉)文公其能刑矣。三罪而民服,《詩》云:『惠此" +640,"傳·定公十年》:「孔子曰『裔夷之俘,以兵亂之,非齊君所以命諸侯也,裔不謀夏,夷不亂華。』」 。 漢代延續了先秦時期的天下觀念《史記·天官書》:「自初生民以來……內冠帶,外夷狄,分中國為十有二州……及秦并吞三晉、燕、代,自河山以南者中國……其後秦遂以兵滅六王,并中國,外攘四夷。」 《史記·五帝本紀》:「鄭玄曰:猾夏,侵亂中國也。」 《史记·武帝本纪》:「天下名山八,而三在蛮夷,五在中国」 《漢書·溝洫志》:「中國川原以百數,莫著於四瀆,而河為宗。」 《漢書·武帝紀》:「今中國一統而北邊未安,朕甚悼之」 《鹽鐵論·卷一》:「匈奴桀黠,擅恣入塞,犯厲中國,殺伐郡、縣、朔方都尉,甚悖逆不軌,宜誅討之日久矣。」,並且出現了中外之分、敵我區隔這樣與現代相似的用法《史記·天官書》描述征西吉兆時﹕「常在東方﹐其赤﹐中國勝﹔其西而赤﹐外國利。 無兵於外而赤﹐兵起。其與太白俱出東方﹐皆赤而角﹐外國大敗﹐中國勝」 《後漢書·西域傳》:「安息國居 ,其人民皆長大平正,有類中國,故謂之大秦。」 《後漢書·南匈奴列傳》:「王莽篡位,貶易侯王,由是西域怨叛,與中國遂絕」 《史記·匈奴列傳》:「然至冒頓而匈奴最強大," +641,"顯位,駕御吳人,吳人頗怨。” 《宋書·列傳第八》:“初,荒人去來,言脩之勸誘(拓跋)燾侵(宋)邊,并教燾以中國禮制,(宋)太祖甚疑責之。” 《宋書·列傳第十九》:“(魏使)孝伯曰:‘鄰國之君,何為不稱詔於鄰國之臣?’暢曰‘君之此稱,尚不可聞於中華……’”、“孝伯曰:‘周公握髮吐哺,二王何獨貴遠?’暢曰‘握髮吐飡,本施中國耳。’” 《宋書·列傳第三十四》:“爽等請曰:‘奴與南有仇……’虜群下於其主稱奴,猶中國稱臣也。” 《宋書·列傳第三十七》:“元景詰之曰:‘……順附者存拯,從惡者誅滅,欲知王師正如此爾。’(虜兵)皆曰:‘……非敢背中國也。’諸將欲盡殺之,元景以為不可。” 《宋書·列傳第五十五》:“(太祖)詔曰:‘河南(指北魏)、中國多故,湮沒非所,遺黎荼炭,每用矜懷。’獫狁侏張,侵暴中國,使長安為豺狼之墟……(柔然)歲時遣使詣京師,與中國亢禮。 《齊書·列傳第三十八》:“小馮為宏皇后,生偽太子詢……偽征北將軍……渾守桑乾,宏……在桑乾西北。渾非宏任用中國人。”、“是年,王肅為虜制官品百司,皆如中國。”、“史臣曰:齊、虜分,江南為國歷三代矣。華夏分崩,舊京幅裂,觀釁阻兵,事興東晉……號令" +642,"” 《陳書·本紀第四》:“世祖文皇帝克嗣洪基,光宣寶業,惠養中國,綏寧外荒。”《隋書·第三十六卷》:「突厥嘗與中國交市,有明珠一篋,價值八百萬,幽州總管陰壽白后市之。」 《貞觀政要·卷九·論安邊三九》:「匈奴世寇中國...涼州都督李大亮以為於事無益,徒費中國,上疏曰:「臣聞欲綏遠者必先安近。中國百姓,天下根本,四夷之人,猶於枝葉,擾其根本以厚枝葉,而求久安,未之有也。自古明王,化中國以信,馭夷狄以權。故《春秋》云:『戎狄豺狼,不可厭也;諸夏親昵,不可棄也。』自陛下君臨區宇,深根固本,人逸兵強,九州殷富,四夷自服..以中國之租賦,供積惡之兇虜,其眾益多,非中國之利也。」 《舊唐書·太宗》:「丁亥,上谓侍臣曰:遼東本中國之地, 隋氏四出師而不能得,朕今東征,欲为中國報子弟之仇。」 韓愈《上佛骨表》:「夫佛者,夷狄之一法耳,自後漢時傳入中國,上古未嘗有也。」 。五代十國時期,中原政權自稱中國,其他政權大多受封、朝貢《新五代史》記載:南漢皇帝劉龑「自言家本咸秦,恥王蠻夷,呼唐天子(李存勖)為洛州刺史」《南唐書·卷十三·劉潘李嚴張龔列傳第十》:「燕之南,越之北,日月所生,是為中國。」《旧五代史》" +643,"0-01, 辽与北宋,金与南宋,彼此都自称“中国”,都不承认对方为中国。严格地说,古代“中国”是一个形容词,而不是一个专有名词。我国古代各个王朝都没有把“中国”作为正式国名。然而清代前已陸續有各朝代於其國家律法、對外條約和外交事務中,以「中國」一詞行文或發言自稱。 對外官方交往中,已知最早使用「中國」的是西漢,比如漢使陸賈對南越王趙佗說:「中國之人以億計,地方萬里,居天下之膏腴,人眾車轝,萬物殷富,政由一家,自天地剖泮未始有也。」唐太宗赐薛延陀国书有:“中國禮義,未始滅人國,以頡利暴殘,伐而取之,非貪其地與人也。”元世祖賜高麗國書有:“中國之姓雖更,外邦之貴不闕。”給日本国书有:“日本密邇高麗,開國以來,時通中國,至於朕躬,而無一乘之使以通和好。”明太祖赐日本国书有:“朕本中国之旧家,耻前王辱,兴师振旅,扫荡胡番。”萬曆皇帝給豐臣秀吉的詔書稱:“咨爾豐臣平秀吉,崛起海邦,知尊中國。”清朝自入主中原以来即以中國自稱。1689年,清朝与俄国签订的《尼布楚条约》中,将“中国”譯成拉丁文,作为现代国家概念的名稱使用。第一次鴉片戰爭後簽訂《南京條約》,這是漢文的“中国”首次在条约中作为现代国家概" +644,"惟俄語仍稱契丹。英文對中國的稱呼為China,古日語對中國的稱呼有諸越、伽羅、唐土、西土、吳國等。 == 历史 == === 上古史 === 中國歷史如果從文字出现的商朝算起約有三千五百年(信史)但贾湖文化遗址内出土的一批具有文字性质的契刻符号距今八千到九千年;從经过二重證據法證明的夏朝算起約有四千二百年;從三皇五帝的傳說時代算起約有四千六百年;從盤古、伏羲、女媧等不確定的神話時代算起約有五千年(意義上的長度);從標誌著人類文明萌芽的新石器時代磁山文化算起約有一萬年;從人類開始脫離原始生活的舊石器時代藍田猿人文化(非智人)算起約有八十萬年的歷史。 中华文明起源于黄河文明和长江文明。中国旧石器时代早期文化有蓝田人文化、元谋人文化、巫山人以及郧县人文化等,中期则以北方的北京人和南方的长阳人、观音洞文化为代表,晚期有山顶洞人、柳江人、资阳人等《旧石器时代考古》.王幼平.文物出版社.2000年11月.中国北京.isbn=7-5010-1218-0。中石器时代有下川文化等。新石器时代有裴李崗文化、磁山文化、仰韶文化、龙山文化等张之恒,《中国新石器时代考古》,南京:南京大学出版社,2004年,页1" +645,"翻夏,创建商朝,是目前通过考古确认的第一个中原王朝。约前1046年周武王灭商建立周朝。周经周武王、周成王和周康王三代的治理,建立完备的礼治社会,成王、康王在位期間後被稱為成康之治。前841年的国人暴动成为中国历史有确切纪年的开始,此後周朝開始衰退。前771年西周滅亡,东周建立,中原進入诸侯争霸春秋、戰國時代。 公元前221年,秦始皇消灭六国,建立秦朝,统一中国。从此中国结束分封体系,开始中央集权的君主统治时代。前二零七年秦朝灭亡,在随后的楚汉战争中,刘邦打败项羽于前202年建立汉朝,西汉經歷文景之治、漢武盛世、昭宣之治,中國的國勢來到第一個輝煌的高峰,与西方的罗马帝国同时成為舉足輕重的大帝國。经历王莽短暂的新朝后,刘秀于公元25年建立东汉。东汉末年,中国又进入分裂战乱的三国时期。280年司马炎重新统一,建立晋朝(西晋)。此后不久胡人入侵,在北方先後建立十六国的割據政权,与南方的东晋并存。420年东晋灭亡后,由刘裕建立南朝宋,北方则由北魏于439年统一,中国进入长达170余年的南北朝对立局面。 581年,隋文帝杨坚建立隋朝,589年灭陈后,重新统一中国,结束逾270年的分裂局面。618年" +646,"题长期不能解决,与此同时西班牙帝国、葡萄牙帝国等欧洲国家逐渐崛起,并开始向外扩张,外国势力开始进入中国,并爆發戰爭與衝突,葡萄牙在澳门建立根据地,最终明朝于1644年被李自成所灭。同年清兵入关,建立清朝。明朝遗臣在南方建立南明,但很快被清朝所灭。郑成功在台湾击败荷兰東印度公司殖民者,明郑觸角擴及大員,荷屬福爾摩沙解體。1683年清朝灭明郑,把原明鄭領地纳入中国版图,统一全国。1689年在黑龙江与沙俄交战,其後簽訂尼布楚條約,首度确立现代边界。之后康熙帝、雍正帝和乾隆帝三朝皇帝开创康乾盛世。 === 近世史 === 1840年,鸦片战争打开了中国的大门,标志着中国近代史的开端郭廷以:《近代中國史綱》,香港:中文大學出版社,1979年。此后清政府在多場對外戰爭中戰敗,被迫同列強签订一系列不平等条约,赔偿巨额白银,并割让香港给英国,承认澳门归屬葡萄牙,割让台澎给日本,以及割让东北、西北大片领土给沙俄。而中国内部也发生民變,其中太平军在1851至1872年间,攻克过城市六百余座《曾文正公全集》奏稿二十1864年7月26日《金陵克复全股悍贼尽数歼灭折》。清政府曾开展了洋务运动、戊戌变法、清末新政" +647,"民國總統。中國共產黨及部分民主黨派拒絕承認該憲法和政府改組,國共內戰仍然持續。1949年底,中華民國政府在內戰失去對中國大陸的控制,遷至臺北,形成两岸分治的局面。 1949年10月1日,中国共产党领导下的中华人民共和国成立。在第一代最高领导人毛泽东的领导下,建国初期进行了镇压反革命、抗美援朝和土地改革等三大运动,并通过生产资料所有制的社会主义改造在全国范围内建立社会主义制度。然而,自1950年代末至1970年代末的大跃进、文化大革命等連串激烈的政治运动,對中国大陆的经济文化造成了嚴重傷害。不过,在建国的前三十年间,中国大陸在蘇聯的大力幫助下仍初步建立了现代工业、农业、国防和科学技术体系,实现了两弹一星等科技成果。1971年10月25日,联合国决议“恢復中華人民共和國在聯合國組織中的合法權利問題”,承认中华人民共和国是五个安理会常任理事国之一。1978年,中共十一届三中全会决定进行改革开放,邓小平成为第二代最高领导人,随后中国大陸逐步建立“以公有制为主体、多种所有制经济共同发展”的经济制度,并通过大规模体制改革逐步建立中国特色社会主义制度。1989年,六四事件使中国大陸受到西方国家全面的" +648,",以及新疆和西伯利亚部分地区,其疆土為中國歷史上最大的时期,而后明朝中叶时的疆界又被蒙古壓至明长城一帶。清朝初期,中国疆域再次扩张,并控制了今蒙古国、新疆和西藏等地,但后来在清末政府积弱不振且屡受外国侵略的影响之下,失去了大片领土。中华民国大陆时期疆域數度變更,後期由于外蒙古独立,中国版图北面凹陷,變成现在的形狀。中华人民共和国政府从2014年,开始在南海大规模填海造岛,拓展海洋疆域台高官:习近平对南海填海造岛有全盘战略规划 .大公报.2014-10-16。 ===地形=== 喜马拉雅构造期,印度板块与欧亚板块撞击,青藏高原隆起,形成了中国的地貌。中国地势西高东低,成阶梯状分布:西南部是“世界屋脊”,全球平均海拔最高的高原青藏高原,地势最高,平均海拔4000米以上,为第一阶梯;以昆仑山脉、祁连山脉、横断山脉为界,向东向北下降为一系列高原和盆地,平均海拔1000~2000米为第二阶梯;在大兴安岭、太行山、巫山、武陵山、雪峰山一线以东多为平原和丘陵,为第三阶梯。再向东为中国大陆架浅海区,也就是第四级阶梯,水深大都不足200米。 中国山区广阔,山地、高原和丘陵约占全国土地总面积的三分之二;中" +649,"政權崩溃,外族亦會乘機入侵。 由于中国南北方向跨纬度较大,达四十九度,冬季南北气温差别很大,最冷月均温攝氏零度等温线大致沿秦岭—淮河一线分布;漠河镇与海口市的一月平均气温相差接近攝氏五十度。夏季,除青藏高原等地区外,各地七月平均气温大多在攝氏二十度以上。中国冬季气温最低的地方在黑龙江的漠河镇,一月平均气温为攝氏負三十點六度,极端最低气温攝氏負五十二點三度;气温最高的地方在西沙群岛附近,一月平均气温为攝氏二十二點九度。中国夏季温度最高的地方是新疆维吾尔族自治区的吐鲁番,七月平均气温为攝氏三十三度,极端最高气温攝氏四十九點六度。 == 政治 == ===政治制度=== 原始社会以氏族部落构成,后来逐渐出现社稷政权。堯、舜、禹时代采用禅让制继承统治权。禹的儿子啟建立的夏朝,是中国第一个世袭王朝。后被商推翻,商又被周推翻。夏商周都采用分封制,诸侯贵族的权力很大。周王朝丧失统治权以后,中国进入了诸侯混战的春秋战国时期。春秋战国时代,是中国历史上的大变革时期,各诸侯国为了图存自强,先后进行了政治和经济的改革,中国史书称之为“变法”。其中,管仲、子产是法制的先驱。战国时期又有魏国李悝变法,楚国吴起变" +650,"对立导致第二次国共内战,中国共产党最终取得大陆地区的控制权,建立中华人民共和国,实行社会主义制度;而中华民国政府撤退至台湾,实行资本主义制度。1978年后,中华人民共和国实行经济改革,转向社会主义市场经济并成功的实现了经济快速发展,中华民国亦在1990年代先后实施解严、解除党禁报禁、实施总统直选等民主化改革,急速转型为多党制的自由民主政权。 ===行政区划=== 中国区划可以追溯到夏朝。《尚书》中的《夏书·禹贡》记载,大禹的时候,天下分为九州,分别为冀州、兖州、青州、徐州、扬州、荆州、梁州、雍州、豫州。到了商、周时期,实行分封制,各地由诸侯国统治。自秦朝至清朝,中国的地方行政区划制度大致可分为三个阶段:秦汉魏晋南北朝时期的州、郡时代,隋唐五代宋辽金时期,道、路时代,元明清时期,行省时代谭其骧:《历代行政区划略说》,《中国古代文化史讲座》(中国传统文化经要丛书),桂林:广西师范大学出版社,2003.6 ISBN 7-5633-3938-8。在中国历史上,很多城市都曾經成为過中国的首都。公众认知度最高的“四大古都”是西安、南京、洛阳、北京,此外与开封、杭州、安阳、郑州、大同则并称为“九大古" +651,"膑在马陵道战役大败魏军,为人类史上第一次大规模运用弩的战役。除了一些正常的軍事途径外,中国古代亦有發生組織刺客暗殺敵人的事情,如戰國時期的荆轲刺秦王。 明朝曾组建了一支强大的海军舰队。清政府和俄罗斯帝國之間因边境冲突,曾爆發雅克萨之战,康熙帝派遣彭春率领清军三千人围攻雅克萨,最終以俄军戰敗告终,並签定了中俄《尼布楚条约》,俄军撤出雅克萨,毁掉雅克萨城,划定中俄边界,整个外满洲归中国所有。以後中国的军事力量日漸衰弱,直至一八四零年前後,已无力抵抗入侵勢力,這段時期中国和列强的战争几乎都以戰败告终,直到一九四五年抗日战争的成功,中国才取得了一场完整意义上的胜利。 在国共内战後,中国共产党取得中国大陆的统治权,中国国民党退守台湾,目前台湾海峡依然存在较高的战争风险。中华人民共和国參與的战争有:朝鲜战争、中印战争、越南战争及中越战争等。現時中华人民共和国是一个已开发出核武器、化学武器、等离子体武器、高能量激光武器、次声波武器以及远程弹道导弹等高端武器技术的国家。一九六四年十月十六日十五时整,中国第一颗原子弹在新疆罗布泊爆炸成功。一九六七年六月十七日,中国的第一颗氢弹在罗布泊沙漠上空成功引爆,其" +652,"汉朝皇帝青睐,同时中亚的一些作物如苜蓿等也被引进到中土。兩千多年前的中国就以丝绸之路和中亚、欧洲相互交流商品,出口丝绸和茶叶等商品。从汉朝开始,朝鲜半岛和日本就和中国有贸易往来和交流。自东汉起,印度的佛教文化开始传播到中国,并最终在中国扎根,还经由中国传到朝鲜半岛和日本。 唐朝的繁荣时期,长安成为当时世界的最重要城市之一,宽容地接受世界各地而来的文化。新罗在唐王朝支持下统一朝鲜半岛,和中土的往来非常密切。日本曾多次派遣遣唐使到唐朝,全盘学习和接受大唐的政治、经济、社会、文化诸方面,还仿制汉字,以为日本文字,其都城也仿造长安的样子打造;遣唐使中最著名的就是晁衡和吉备真备等人,其中晁衡和王维、李白等人是好友,王维为其回国饯行的时候还亲手写了一篇《送秘书晁监还日本并序》相赠;鉴真和尚也东渡日本,讲经说法,将佛教教义播撒到这个东瀛岛国。另外,阿拉伯商人在长安街头也经常可见,并且有人通过科举考试在大唐做官。大唐和史称天竺的印度交往也很频繁,唐僧玄奘法师历经艰险,过沙漠、翻山越岭,终于在印度取到真经,宣扬大乘佛法,成为众所仰慕的三藏法师,其故事后来还被改编成广为流传的《西游记》,为后世所称道。 宋" +653,"划定其势力范围、抢占租借地。俄国划定长城以北的地区和新疆作其势力范围,英国划定长江流域,德国划定山东,日本划定福建,法国划定广东、广西和云南。另外,1905年的《朴次茅斯和约》(即《日俄讲和条约》)虽然是俄国和日本所签订的和约,但是日俄战争的主要战场卻在中国东北,且该条约也涉及两国在中国东北利益和特权的划分,因而也算是一个涉及中国的不平等条约。1901年八国联军攻入北京后签定的《辛丑条约》,是清末中国和列强的最后一个主要的不平等条约,也是中国近代史上赔款数目最庞大(参见庚子赔款),主权丧失最严重的条约。 直至民國時期,中國作為一戰戰勝國,沙皇俄國決定將「庚子赔款」緩付五年。及至1924年5月底,蘇聯政府聲明,庚子賠款是帝國主義列強強加給中國人民的不義賠款,蘇聯政府決定放棄沙俄剩餘全部庚子賠款,提倡將放棄的賠款作為中國教育款項。而戰敗的德國和奧匈帝國亦終止了其賠款条约。对日抗战后期,中华民国国民政府才有能力废除自鸦片战争后所签订的大部分不平等條約。中华民国大陆时期内战不断,经常出现几个政府代表中国,没有一个统一的政府来积极发展外交关系。北洋政府时期各个派系的军阀都依仗不同的国家来对自己的" +654,"澳门特别行政区。八十年代改革开放以后,中华人民共和国与西方关系一直较为融洽,但由于1989年的六四天安门等事件,西方对华进行经济和贸易制裁,其中军备禁运至今未能解除。 == 经济 == === 经济规模 === 中國古代的經濟重心轉移的趨勢是由北至南,並大體上可分三個階段:第一階段為遠古至西晉末年,其中以三國孫吳時轉變較大;第二階段為東晉至北宋末年,其中以唐安史之亂劃分為前後;第三階段為南宋建立至鴉片戰爭,此時長江以南經濟已完全超越黄河中下游。 明清时期,中国社会仍是小农经济。明代中后期,传统手工业中,出现资本主义萌芽,但由于战乱并未能发展起来。清朝的本地生產總值曾在世界總額中佔到將近三分之一。政府经济奉行重农抑商政策的同时,而西方世界则实现了工业革命,中国经济遂逐渐落后于西方。。自一八四零年鸦片战争至一九七八年改革开放前,中国效仿西方发展工业的过程进行的并不顺畅,其间受到内外因素的多重干扰。中日甲午战争证明了清朝洋务运动的彻底失败;而中华民国经济利用国民政府北伐胜利后至抗日战争爆发前的短暂和平时期,得到了快速发展,被称为南京十年,但经济成果随着日本侵华而化作泡沫,并陷入长期恶性通货膨" +655,",香港為49334美元,澳門為81151美元。2021年大陸地區的人均国内生产总值為12990美元,香港為49036美元,澳門為58004美元(因受新冠肺炎影響,2021年香港和澳門人均GDP有所降低。) ===货币=== 中国货币历史悠久,早在新石器时代,一些类型的货币就通用。夏商時代,中國使用天然海貝作为貨幣。商代晚期墓中出土的鑄造銅貝,是世界上現存最早的金屬鑄幣。而现存最早的中国古代银质钱币被发现于战国时期的楚国。宋仁宗年间,已于四川产生交子、会子等纸币,到元代纸币的流通扩大到全国范围,称为钞。1935年,中国四大国有银行颁布法律通告,正式放弃银本位。彭信威著:《中國貨幣史》(上海:上海人民出版社,1958)。2020年,中国四家大型国有银行同时在深圳等城市对央行数字货币进行大规模测试,中国人民银行行长周小川表示数字货币将取代有800多年历史的纸币。 == 文化 == === 語言及文字 === ;语言 中國的語言包括漢語族、藏語、維吾爾語、蒙語等,但中文常常指代漢語。中國的主要語言是漢語族,漢語在語言學上並不是一種語言,而是屬於漢藏語系的語族,包括官話、吳語、粵語、閩語、客家話" +656,"文字中时代最早、体系较为完整的文字。甲骨文主要指殷墟甲骨文,又称为“殷墟文字”、“殷契”,是殷商时代刻在龟甲兽骨上的文字。19世纪末年在殷代都城遗址(今河南安阳小屯)被发现,继承陶文的造字方法,是中国商代后期(前14~前11世纪)王室用于占卜记事而刻(或写)在龟甲和兽骨上的文字。殷商灭亡周朝兴起之后,甲骨文还延绵使用了一段时期。在中国,除了汉字以外,还有藏文、蒙文、满文、女书、维吾尔文等文字,以及一些消亡了的文字如西夏文、契丹文等。中国的书法有着两千多年的传统,文房四宝之一的毛笔有着丰富的表现力。历史上有许多著名的书法家及国画家。 === 識字率及教育 === 中国在虞舜时代已经建立学校《尚书•舜典》:“舜时,设庠为教,分下庠,上庠。七岁入下庠,庶老为师;十五岁入上庠,国老为师。”,以后夏、商、周时代均有发展。古代中國官学体系最大規模的學府稱為「國學」。虞之上庠,夏之東序,殷之瞽宗,周之辟雍,漢後之太學,隋後之國子監,皆為國學。先秦上古之大學,一般貴族子弟才能入學;庶民子弟入讀的學校則稱為小學,如舜帝時之下庠、夏朝之西序、商朝之左學、周朝之虞庠。到了秦漢,周室封建制度瓦解,漢代建立太學" +657,"陽明出,書院再度興盛。隨後書院因批評時政,遭當道之忌,明世宗、張居正皆曾毀書院;尤其是東林書院事件,魏忠賢盡毀天下書院,書院乃大沒落。清初对书院加以监管,至雍正十一年(1733年),才改採鼓勵態度,正式明令各省建書院,書院漸興;惟不分官立私立,皆受政府監督,不復宋元時的講學自由。直到庚子後新政,庚子詔令將全國書院改制為新式學堂,書院制度瓦解。 清末中国颁布壬寅学制,并取消科举制度,中国教育由儒学为主体的教育转入近代新式学科教育。1922年北洋政府颁布壬戌学制,建立起现代教育制度。中华人民共和国教育起初實行蘇聯共产主義教育,改革开放后重新审视并進行改革。八十年代后,中华人民共和国中小学教育普遍采用九年制义务教育制度,规定学龄儿童均要接受小学和初中教育,目前还在深圳、珠海、浙江、福州、唐山、山东部分地区采用十二年制小学到高中免费教育体制。政策还规定应当免除一切学费和杂费。中国高等教育包括普通高等教育和成人高等教育,实行高考制度来选拔学生进入大学本科或者高职高专就读,如果因高考发挥欠佳升至高职高专的学生,可在高职高专三年级当年参加专升本考试可转入大学本科就读,但不同于高考和考研,专升本仅限一" +658,"取材于现实,这个传统一直维持到現代。 === 神话史诗 === 中国神话一般指的是关于上古传说、历史、宗教和仪式的集合体,通常它会通过口述、寓言、小说、仪式、舞蹈或戏曲等各种方式在上古社会中流传。某方面而言,上古神话会被假定是历史真实的一部份,关于中国神话的最初文字记载可以在《山海经》、《水经注》、《尚书》、《史记》、《礼记》、《楚辞》、《吕氏春秋》、《国语》、《左传》、《淮南子》等古老典籍中发现。 由于中国民族众多,每个民族都各自至少有一部神话史诗,如少数民族的三大英雄史诗《格萨尔》、《江格尔》、《玛纳斯》。诸如南方20个民族共有30多部创世史诗,仅云南16个民族就有20多部创世史诗。 不同民族间的神话史诗内容不同,样式、文学特点也会不同,如汉族神话史诗多书面记载,文献典籍丰富,而众多少数民族多口头流传,且内容篇幅多于汉族。并且也受地域的影响,中国北方民族多流传英雄史诗,南方多流行创世史诗。 === 哲学思想 === thumb|left|浙江宁波天一阁收藏明刻本前汉书,五伦书,钱起诗集 古代中国在春秋战国时期,出现过百家争鸣,思想文化飞跃发展的局面,涌现出老子、庄子、孔子、荀子、墨" +659,"中國在儒家礼教思想的影響下,以四書五經為基礎形成冠昏喪祭的習俗,以及衣冠制度, 冠禮、婚禮、喪禮、祭禮等。五四运动后,一部分中國人接受了马克思唯物主义辩证法,并对各種宗教信仰以唯心主义的理由加以否定,他们是無神論者,并在此基础上产生了毛泽东思想。 中華文化雖然孕育了諸子百家(道家、儒家、法家、墨家、杂家、纵横家、阴阳家、小说家、名家、农家等),但主要哲學流派為儒、道、釋,且三派間相互影響深遠。有人认为中國文化以儒道釋三家之說為總本﹐而又以道家為中心﹐儒家為主幹﹐佛學為相互配合。中國人的宇宙觀方面以易經與老莊為代表﹐倫理社會觀是以禮記孔孟之說為代表﹐佛家則以宣揚因果輪迴、眾緣唯心的道理與儒道互相輔助而成。 儒家思想是以「孔孟之道」為源頭,在中國文化發展史上源遠流長,對中國人的普遍倫理道德,對中國文化的價值和價值優先觀念都有著深刻的影響。已經成為中華民族的一種集體潛意識。儒家的價值觀影響古代中國大多數公開場合是以儒家的價值觀作為優先的標準。因此可以說儒家的價值取向在總體上代表了中國傳統文化的基本價值優先觀。儒家注重自身修養並講求倫理道德,其中心思想乃「仁義」,也就是人與人之間應注重和諧的關" +660,"的難題之所以無法解決,皆是因為干預行為過多,故提倡行為上要以無所作為就可以達到無為養息。道家也嚮往著反璞歸真的樸實社會,認為人類回歸原來樸素、無知、無慮的境界,人類的紛爭和煩惱即可真正的解脫。老子在總體上傾向治國方面,在人生觀上,老子主張「清虛自守,卑弱自持。」政治上提出「無為而治」,以無為而無不為。道家由人生觀和社會觀擴展至宇宙論,提出「道」、「氣」、「自然」等哲學概念。道既是生物之源,亦是生成萬物之根本,亦是天地萬物之本性,亦是人安身立命之根據,亦為治國安邦之根本方略。道家強調「游心」,並推崇自由,道家強調「虛心」,並推崇自然。主張要聯繫性、心、情三者又有所區別。性是指人之先天之本性,;心是指人之內在精神,突出者為人之為人之主體性因素;情是指人之主觀情感。有性、有心、有情,故而之為人。道家所追求的人生,即是自然、自在而自由的人生。道家對人之命運,持一種自然的無可奈何的態度。對於生死,持一種純自然的態度。與(今之所谓)道教(传统中国并不区分“道家”与“道教”)之追求長生不老有所不同,老子首倡致虛守靜,莊子則將致虛守靜具體化為「心齋」與「坐忘」,並進而將致虛守靜提升為本體論的高度,而標" +661,"本作改良,或通过辩证后推翻,形成一套经验主义科学传统。至隋唐及宋朝时,中国的科学曾长时期高据世界领先地位。然而由于近代历史原因,许多古中国科学著作或失传或流失海外,均不利于中国科学史的发掘整理。科学著作的失传及流失,甚至令后世史家曾一度认为中国没有科学。 古代中国为世界贡献诸多发明创造,包括:四大发明(造纸术、雕版印刷、指南针、火药)、地动仪、伞(现今所用,能张开收回的伞)、丝绸、瓷器、石油井、纸币、火柴、漆器、风筝、热气球、連弩及中国青铜器等等,而且在天文、数学、医药、机械、冶金、陶瓷、纺织、建筑等众多方面发展出独具特色的先进成果。英國科學史家李約瑟所著《中國的科學與文明》和美国科学史家罗伯特·坦普尔所著的《中國的100個世界第一》中列举了中国領先於当时世界各國的多項创造發明。 中国在商朝就开始使用十进制系统。战国时的《甘石星经》中的《石氏星表》测定当时一百二十余颗恒星的位置,次於《巴比倫星表》,被认为是世上第二早的星表,有从公元前六一三年到一九一零年间共十三次的记录。现今举世公认的最早的太陽黑子纪事,就是载于《汉书·五行志》中的河平元年(即公元前二十八年)三月出现的太阳黑子。秦、汉" +662,"化石是古代动物和植物的遗蹟,并且根据化石推论古代的自然环境。元朝在天文历法方面比较发达,一二八零年郭守敬等人主持编订的《授时历》延用了四百多年。明朝科技在航海方面的成就显著。明太宗時由於有鄭和下西洋的海上活動,使得繪製海圖以及羅盤運用的技術大為進步。清代有不少學者、匠師和科學家如黃履莊、黃履、孫玄球等對西方「奇器」有一定的研究和高度仿製,西方「奇器」在一定程度上亦推動中國的物理實驗和機械製造的發展,由於清政府對天文曆法的重視,民間的天文學研究也很活躍,主要代表人物有著有《曉庵新法》和《五星行度解》的王錫闡等南炳文、李小林、李晟文《清代文化——傳統的總結和中西大交流的發展》,天津古籍出版社,第246-255頁。 1840年鸦片战争之后,西方科学傳统及技術传入並主導了中国,在清末的洋务运动主张“中体西用”。以中国传统的思想、文化及制度为基础,引进西方先进的科学技术,是为「中学为体,西学为用」。洋务运动的拥护者冯桂芬主张接受欧美思想为主的学术,提出“以中国之伦常名教为原本,辅以诸国富强之术”的主张。中华民国时期的科技水平不断提高。一九四九年中华人民共和国成立后取得两弹一星的重大科技成果,改革" +663,"十二釐米,每道隔梁用三四塊木板榫接而成,並與船肋骨緊密結合在一起,艙內採用水密艙壁。宋船曾採用榫接、鐵釘加固、船板縫隙中填塞捻合物的辦法,來保證船的堅固性和水密性,當時中國的造船技术已经相當成熟。 南朝刘宋时期,中国海员掌握了从安息国(在今波斯湾地区)到斯诃条国(今斯里兰卡)的两万里海湾航路,开辟了近海航行性质的、直接通达波斯湾的“海上丝绸之路”。隋唐时期,中国海员的航海技术又有了进一步提高。贾耽记载的“广州通海夷道”中写到,从狮子国(今斯里兰卡)续航,循阿拉伯海东北岸驶达波斯湾的乌剌国(今奥波拉),仅需37天。北宋朱彧在宣和元年(1119)所著的《萍洲可谈》中说:“舟师识地理,夜则观星,昼则观日,阴晦则观指南针。”这是人类利用指南针进行海上导航的最早文献记载。 明代早期1405年至1433年間,明成祖命郑和率領以二百四十多海船組成的船队七下西洋,足迹遍及東洋世界、印度次大陸、阿拉伯半島、及東非马达加斯加北部水域,是當時世界上規模最大的遠洋航海项目。鄭和的船隊拜訪了三十餘個西太平洋和印度洋的國家和地區。這段時期是在迪亞士發現好望角的七十年前及哥倫布發現美洲大陸的八十年前;是當時世界上規" +664,"减缓疾病的恶化时,还能兼顾生命与生活的品质。此外,中医学的最终目标并不仅止于治病,更进一步是帮助人类达到如同在《黄帝内经》中所提出的真人、至人、圣人、贤人的境界。 传统的中医学思维模式与源于欧洲的现代科学并不相容,然而,当今之科学期刊已多有论文研究之,并试图用现代医学的角度分析中医中的部分现象和治疗机理。在现今世界的医疗体系中,中医学被归类为替代医学中的一支。美国食品药品监督管理局在定义“完整医药体系”这个概念时提到中医学:“NCCAM(美国国家补充与另类医学中心)把完整医药体系描述为涉及‘完整医药体系是与对抗疗法(常规)医学独立地或平行地演变的完整的理论和实践体系’。这些可能反映了独特的文化体系,比如中医学和印度的阿输吠陀医学。完整医药体系都有一些共同的元素,相信机体有自愈的能力,这种自愈可能涉及到了应用情绪、身体和精神的治疗方法。 == 人口 == === 族群 === 中國民族自古就有华夷之分。華夏族自认为居于“中央之國”,称周边民族为“四夷”。春秋时期的族群已经开始混合:齐国(与东夷混合)、楚国(南蛮混合)、秦国(西戎混合)、晋国(北狄混合)、燕国(北狄混合)。秦汉时期进一步融" +665,"有汉族、满族、维吾尔族、回族、蒙古族、壮族、藏族等56个民族,汉族是人數最多的民族。汉族广泛分布在全国各地,其中主要集中在东北、华北、华东、中南、甘陕以及云贵川渝等地区。根据中华人民共和国2000年第五次人口普查,中国大陸(包括中国内地、香港、澳门)总人口为12.9533亿,其中汉族占91.59%。其他55个民族统称为“少数民族”。主要少数民族中藏族主要分布在西藏、青海、四川和云南等地;满族主要分布在东北,以及内地许多城市中;蒙古族主要分布在内蒙古自治区;维吾尔族主要分布在新疆;回族主要分布在宁夏;壮族主要分布在广西。其他各少数民族(如苗族、哈萨克族等)也分布在全国不同地方。 ===数量=== 中国人口在历史上一直占世界人口的较大比例。据估计,战国末期(前230年)各诸侯国人口总计有三千至三千五百万人。十九世纪上半叶清朝人口约占全世界人口的三分之一,目前中国人口约占全世界的五分之一,但新生儿数量仅占全世界的十分之一。因此,未来中国人口占全世界比重将显著下降。进入21世纪后,面對人口老化,计划生育政策從2011年「雙獨二胎」開放為「單獨二胎」到2015年「全面放开二胎」。[洪嘉蓮,《指尖" +666,"人钻研儒家经典,参加科举考试,学而优则仕。古代皇权政府提倡三纲五常,这对于维系社会和谐稳定有正面的作用,但是也束缚了中国人的思想。 === 人倫關係 === 中國傳統社會是一個以人倫來維繫的社會,中国社会重视家庭、血缘关系。傳统儒家重視孝道,中国古代社会深受儒家思想影響,甚至到了過份的地步。 表明沒有告知父母就娶妻,也屬於不孝的一种。在礼教盛行的年代,男女婚姻不是靠自由恋爱,而是要听父母之命、媒妁之言。傳統社會與同時期世界大部分地區一樣,屬於父權社會,妇女的地位並不高;而納妾在古代中国上流社会中,更是普遍的現象。中国历史上只有一个女皇帝武则天,其余的皆为男性。 === 治安與法制 === 原始社会的社会关系比较简单,并没有法制,而是依靠习俗和惯例来施行《淮南子·览冥训》: 伏羲、女娲不设法度,而以至德遗于后世。 。中国奴隶制法律形成于夏,发展于商,完备于西周,瓦解于春秋中国法制史 .北大法宝法律数据库。战国时期魏国的李悝总结了各诸侯国立法经验,制定了中國历史上第一部初具體系的法典。商鞅變法为秦国统一中国打下了基础。汉朝刘邦废除秦朝的苛法嚴刑,颁布了约法三章:“杀人者死,伤人及盗抵罪”。" +667,"費,靠常例規費生活,與明代有很大的不同。 == 艺术 == 中国艺术包括中国传统的各种工艺(如陶瓷、玉器)、美术(如书法、国画)、音乐(如古琴、古筝)、表演艺术(如舞蹈、戏曲)等。 === 音乐及表演艺术 === 中国传统音乐是在中原音乐、四域音乐和外国音乐的交流融合中形成发展起来的,包括民间音乐、文人音乐、宗教音乐、宫廷音乐等类别。中国传统音乐有鲜明的特色。常用的乐器有筝、琴、箫等,音律以宫、商、角、-{徵}-、羽五音为基础。中国新石器时代出现的骨笛,是目前已知世界上最早的管乐器。中国传统舞蹈有着悠久的历史,体现中国的传统文化和美学,而且和武术、杂技、戏曲有着密切关系。戏曲是中国传统的舞台艺术,综合唱念做打等多种表演方式,包括京剧、豫剧、河北梆子、秦腔、评剧、粤剧、越剧、昆曲、黄梅戏、潮剧、晋剧、花鼓戏等多个剧种。 ===书法=== 中國書法是汉字的书写艺术,属于视觉艺术。许多书法作品是以书信、诗词手稿、碑文、墓志铭、牌匾、屏风、室内装饰品等形式存在的。中国书法与中国绘画、篆刻、舞蹈、音乐等艺术形式相互影响。 === 绘画及工艺 === 中国绘画艺术的代表是国画,主要是用毛笔、软笔或" +668,"中心、國際金融中心、廣州塔等。 === 城市規劃 === 中国古城邑在规划上,很早便有在水利、交通運輸、排水、防灾、工業配套、軍事防御等範籌上的考量,對於世界城市规划史發展有顯著的影響。 對於古代城市的选址,《管子》一书中就反对商周以来用占卜确定营邑的方法,説「凡立国都,非于大山之下,必于广川之上,高毋近旱而水用足,下毋近水而沟防省」,意思是建设城市要选择依山傍水的地形,以免受旱涝之害,节省开渠引水和筑堤防涝的费用,是中國早期有關城市規劃的描述。中國許多古城的选址,如杭州、苏州、北京、南京、西安、洛阳、开封,都是经过周詳的考虑。 === 基礎建設 === 据考古发掘分析,中国石器时代的桥梁为木梁桥。随着铁器时代的来临,加工大型石料成为可能,石材成为主要的建筑材料。其中,王莽时期修建的灞桥,以石为墩,以木为梁,是石木结合的代表作。石拱桥的出现使中国古代桥梁进入了一个新纪元。建于隋朝的赵州桥制作精良,结构独创,其石拱桥跨径纪录,直到1959年湖南黄虎港桥的建成才被打破。而卢沟桥和双龙桥都属于厚墩联拱石拱桥。与厚墩联拱相对应的是薄墩联拱石拱桥,多见于南方软基水网地区,其中最著名的是建于唐朝的" +669,"原的窑洞、安徽的古民居、福建和广东等地的客家土楼和蒙古的蒙古包。 方形建築為中式住宅的普遍類型。四合院的格局为一个院子四面建有房屋,通常由正房、东西厢房和倒座房组成,从四面将庭院合围在中间,故名四合院。四合院就是三合院前面有加门房的屋舍来封闭。若呈“口”字形的称为一进院落;“日”字形的称为二进院落;“目”字形的称为三进院落。一般而言,大宅院中,第一进为门屋,第二进是厅堂,第三进或后进为私室或闺房,是妇女或眷属的活动空间。四合院在中国至少有三千多年的历史,在中国各地有多种类型,其中以北京四合院为典型。四合院通常为大家庭所居住,提供了对外界比较隐密的庭院空间。三合院为中国传统古厝的基本形制。三合院一般由北面正房(正身)和东西厢房(护龙)组成。由于房屋坐落于三个方向,故名三合院。正厅为祭祀与接待宾客之处,左房是户长居室,右房是长辈居室;左护龙为长子所住,右护龙为次子所住;若中人丁旺盛,则于左右护龙外,再加盖“外护”,屋舍高度则随正厅(房)、护龙依次下降。三合院是四合院的简化形式,通常是将四合院南房直接用院墙代替,大门也采用门楼样式。其它形式有一条龙、单伸手。 徽派建筑是中国建筑中的一支重要流" +670,"派风格,只是将建筑加上一些徽派元素,如马头墙安放在屋顶,加之白墙黑瓦。 窑洞是一种民居建筑形式,是中国大陆黄土高原地区,例如陕西省和山西省的特色建筑。黄土高原比较缺乏木、石等建筑材料和烧砖、瓦所需的燃料,但有质地细密的黄土层。窑洞特别是土窑充分利用了这一情况。外部的土层有利于室内恒温和隔音。下面是实地的地板可以大量承重。易燃材料不多因而火灾不易传播。建築的主要特点是顶上覆土,内部下面方型上面拱型。多数后面比前面稍窄,呈喇叭形。常见的单间窑洞宽約三點五米左右,高约三至四米,深約五至九米。前面有木结构、带门和格子窗的外壁。内部墙面上抹泥、熟石灰或掺石灰的泥,还可以贴纸和窗花进一步装饰。窑洞前的地面平整后,在周围围以土墙。土窑建築直接在黄土形成的崖壁上挖孔形成居室。多数在内部加盖砖或石墙,以防止土层倒塌。福建省南部與西部常見此類型,最著名的栱圆形建筑是福建土楼。福建土楼总数约三千余座,主要分布地区包括龙岩市永定县和漳州市南靖县、诏安县、平和县、华安县。福建土楼是世界独一无二的山区大型夯土民居形式,产生于宋元兩代,成熟于明末、清代和民国时期。 中国疍家人是以船为家的渔民。由于他们生活在船艇上," +671,"中國飲食文化具有極高藝術和文化價值,強調飲食精緻、審美享受、情感交流與講究禮節,與西洋世界的飲食文化不同,中國主要餐具為筷子;歷史上不少東洋世界國家同樣以筷子為餐具,為一文化特色。 中国是茶的故乡,中国茶文化源远流长,博大精深。几千年来中国不但积累大量关于茶叶种植、生产的物质文化、更积累丰富的有关茶的精神文化。中国是造酒的古国。在龙山文化时期出现自然发酵的果酒;后来发展将谷物糖化再酒化的技术;先秦时期出现曲法酿酒;汉代以后发展制曲技术;东汉引进葡萄酒;宋代出现药酒;元代、明代出现从阿拉伯传入的蒸馏法酿制的烧酒。中国酒文化源远流长。酒不但用于祭祀、会盟、祝捷等公务活动,还用于民间的喜丧礼仪、欢聚迎送等场合。中国酒文化和诗歌、戏曲也有着丰富的联系。中国菜已经历数千年的发展历史。它由历代宫廷菜、官府菜以及不同的风味的地方菜系所组成,分为八大菜系。中国菜选料范围广泛,制作精细,品类繁多,注重色香味的统一和调料的使用,体现精湛的烹饪技艺和丰富的文化内涵。中国菜的食物大多加工成小块宜食的尺寸,固体食物使用筷子进食。 === 傳統服饰 === 中國服飾為華夏文明的重要組成部分。中國服飾在几千年的时间" +672,"加详备。 秦汉服装基本沿袭战国时期,仍然以衣裳、深衣、襜褕为主,袍按裁剪方式大致分为曲裾、直裾两种,都是男女均可穿着。武士则着短衣小袖大绔。《汉书·王莽传下》: 乃身短衣小袖,乘牝馬柴車 汉代窄袖紧身的绕襟深衣,衣服几经转折,绕至臀部,然后用绸带系束,衣上还绘有精美华丽的纹样。衣袖有宽窄两式,袖口大多镶边。衣领部分通常用交领,领口很低。如穿几件衣服,每层领子必露于外,最多的达三层以上,現代称“三重衣”。魏晋南北朝时期,服装基本承袭秦汉的遗俗。男子的服装流行大袖翩翩的衫子。直到南朝时期,这种衫子仍为各阶层男子所爱好,成为一时的风尚。褲褶是游牧民族的傳統服裝,基本款式為上身穿齊膝大袖衣,下身穿肥管褲。這種服裝的面料,常用較粗厚的毛布來制作。穿褲和短上襦,合稱襦褲,但封建貴族必須在襦褲外加穿袍裳,只有騎馬者、廝徒等從事勞動的人為了行動方便,才直接把褲露在外面。封建貴族是不得穿短衣和褲外出的。到了晉代這種習慣才有所改變。南北朝的褲有大口褲和小口褲,以大口褲為時髦,穿大口褲行動不便,故用錦帶將褲管縛住,有、又稱縛褲。 隋唐妇女的常服仍然是襦裙。在隋代及初唐时期,妇女的短襦都用小袖,下着紧身长裙," +673,"社,2006年,第45頁。清代漢族婦女服飾變化方面較男服為少。后妃命婦,仍承明俗,以鳳冠、霞帔作為禮服。普通婦女則穿披風、襖裙。披風是清代婦女的外套,作用與男褂相似,其制為對襟、大袖、下長及膝。披風之上,裝有低領,點綴著各式珠寶。披風的裡面,還有大襟、大襖小襖,小襖是婦女的貼身內衣,顏色大多用紅、桃紅、水紅之類。婦女的下裳,多為裙子,顏色以紅為貴。裙子的樣式,初期尚保存著明代習俗,有鳳尾裙及月華裙等。清末,在普通婦女中間,還流行穿褲。 === 節慶習俗 === thumb|left|250px|北京东城区地坛公园的灯笼 陰历,又称农历,是源于夏朝的汉族传统历法,在儒教“行夏之时”的影响下沿用四千多年《论语》“子曰:行夏之时。”,属于阴阳合历,以十二个朔望月为一年,二十四节气来反映季节变化,在東亞文化圈影响广泛,汉族根据夏历形成的祭享民俗构成民俗节日。中國的歲時、歲事、月令、時令在日本被稱為“年中行事”或者“名節”,在韓國稱為“歲時風俗”。江戶時代齋藤月岑著有《東都歲事記》,朝鮮時代柳得恭著有《京都雜志()》(1800年前後)、金邁淳著《洌陽歲時記()》(1819年)、洪錫謨著《東国歲時" +674,"中國有不少傳統遊戲,可分為棋类、牌类、猜射等類型。古代中国發展出不少棋類遊戲,如围棋就是起源于中国,中國古时有「弈」、「-{碁}-」、“手谈”等多种称谓,屬世界三大棋类之一。围棋是一種策略性棋類,使用格狀棋盤及黑白二色棋子進行對弈。对弈双方在棋盘网格的交叉点上交替放置黑色和白色的棋子。Matthews, Charles. Teach Yourself Go, p.1落子完毕后,棋子不能移动。对弈過程中圍地吃子,以所圍「地」的大小決定勝負。围棋规则简單,但玩法千變萬化,被认为是目前世界上最复杂的棋盤游戏之一。另外,灯谜亦是中国受歡迎的傳統遊戲。在中国习俗的活动上会称做猜灯谜,每年逢正月十五元宵节,会将灯谜结合赏花灯,变成许多人可共同参与的猜谜游戏。与一般的谜语相比,具有更严格的规则。方法是将谜语贴在灯上,多数谜语都是诗词,猜中谜语的人,有时可获得奖品。随着时代改变,现今的灯谜活动未必都将灯谜写在灯笼上。猜灯谜有时又说为<射灯谜>,因此多会在谜语之后看到:“射一个字,某某格”之类的提示。 此外,觀看评书亦是中國傳统的余暇節目之一,评书在湖北、粤廣地区及闽南地区称為「讲古」,在四川称为「讲书" +675,"踘是現今足球運動的前身。捶丸則是類似高爾夫的運動,規則是以將球捶進洞最多的一方贏出。至隋唐之時,古代體育發展出諸如摔跤、拔河、秋千等新類型,其中最為興盛的要數馬球與足球這兩種,此兩者在唐代傳到日本。宋真宗時,曾因相撲運動時有人受傷而下令禁絕。九十四年教育部發布之普通高中國防通識課程《國防通識(一)》明清之際中国體育活動仍有發展。然而,部分活動經過清末至民初一連串的戰爭,中國古代運動至今多已失傳,然而如拔河、秋千等活動仍歷久不衰,如角觝等競技則為日本保存至今。台灣省台北縣中和市中經社《中國通史》 中國武術俗稱中國功夫,意即打鬥技巧,它也被分成多個門派,由拳腳至運用刀劍等的技巧也有,中國人不單把武術作為打鬥技巧同時也作為一種健體強身和養生的方法,現在中國武術也作為電影的主題。 == 宗教 == 中国上古存在上帝及祖先崇拜等原始宗教信仰,祭神祀祖自商朝为中国的国家宗教,祖先神是人们祈求上帝的媒介。《诗经·大雅》:“文王陟降、在帝左右。”尧舜禹的臣子皋陶兴“五教”、定“五礼”、创“五刑”、立“九德”、亲“九族”。《尚書·皋陶謨》曰:「天秩有禮,自我五禮有庸哉」。中國人的信仰以儒教、祖先信仰、道" +676,"礼记·王制》:“非其所祭而祭之,名曰淫祀。淫祀无福.”天子是上帝之子、是受天命,即奉天命治理天下。人间的帝王和朝代就是五方上帝轮流所感应而生的,因此也称为“感生帝”。祭祀的法则详细记载于儒教圣经《周礼》、《礼记》中,并有《礼记正义》、《大学衍义补》等经书解释。古代中国“神不歆非类,民不祀非族”,祭祀有严格的等级界限。天神地祇只能由天子祭祀,祭天的圣所是天坛。诸侯大夫可以祭祀山川。士庶人则只能祭祀自己的祖先和灶神。《曲礼》“天子祭天地,祭四方,祭山川,祭五祀,岁遍。诸侯方祀,祭山川,祭五祀,岁遍。大夫祭五祀,岁遍。士祭其先。” 宗庙供奉祖先灵位。供奉和祭祀祖先的祠堂往往又被稱為「家廟」或「祖堂」。 ===道教=== 道教是起源於中国的宗教。春秋时期(约公元前六世纪),老子创立道家,战国以后又产生神仙家,秦汉时期在道家和神仙家的基础上,吸收阴阳家、墨家、儒家诸家思想,先后兴起方仙道、黄老道,经过长期发展,到东汉,张道陵创立正一盟威道,正式形成道教。 ===薩滿教=== 薩滿信仰在中國的傳統始於史前時代,滿洲人的祖先女真人直到公元11世紀也保持著薩滿信仰。薩滿信仰清代以前便在中國東北盛行,並" +677,"朝皇族舉行儀式的堂子,例如坤寧宮。 ===佛教=== 西汉末年,佛教由印度传入中国,汉朝成立后,有鉴于秦朝的法家思想并不能有效地帮助朝廷维持帝国的长久统治,继而以无爲而治的黄老之道取代严管苛治的法家,汉武帝时,实行独尊儒术,自此儒家思想成爲中国文化的主轴。两晋南北朝时,佛教兴起,并深受玄学影响。佛教在中國唐朝時發展最興盛,因為當時有一名叫玄奘的僧人,原因是他感於當時傳入中國的佛經翻譯不善而有太多不明所以之處,故唐三藏親自去天竺(印度)取佛經,他於古印度那爛陀寺鑽研和翻譯佛經,然後把這些成果帶回中國,這就是有名的唐三藏取西經。 佛教對於中國習俗與其他宗教也產生了巨大的影響。具體表現在信三世。佛家認為,世間的萬事萬物都因緣和合而生,一切都在輪迴之中。雖然現世作惡而得福得壽,但其業果若不報於現世而將報於來世。因信三世六道,故信善惡定有果報,或報之自身體,或報之子孫,或報之來世。同時還引進了悔罪植福、延壽薦亡、修德禳災、設供祈願等觀念。 佛陀之學即為佛學,佛學以因果輪迴為理論,以五戒十善法為規律,藉以出世解脫為佛家一主要思想﹐以萬事無常轉變生死痛苦為理論。以無我、緣生、苦空為方法﹐以證得無為自" +678,"年间清政府禁止天主教在中国活动,1840年西方的坚船利炮打开中国的大门,大批传教士来到中国传教,并先后在大清帝国和中华民国开办一些教育、医疗等事业。十九世纪晚期基督宗教在华推行本色运动。当年教会常被指为西方列强入侵中国的工具,因此教堂和西方传教士成为攻击目标。教会觉得应该结合中国文化传道,摆脱与西方传教士的从属关系,因此提出自治、自养、自传的“三自”观念,以消除与民众的隔阂,使基督宗教在中国扎根。一九二四年,教宗碧岳十一世委宗座首位驻华代表刚恒毅总主教在上海徐家汇大堂召开第一届中国主教会议(教会史上又称上海公会议,自五月十四日开至六月十二日),大会主题是要求建立一个正常的、自由的、中国化的天主教会。会中研讨适应中国地区之传道方向,给中国教会制订了一套全备的传教典章,为中国教务竖立了一个新的里程碑。会议中主教们决定了把中国奉托给圣母照顾保护,并奉圣母为「中华母后」,事经圣座核准施行,会议开幕时举行了隆重的奉献典礼,会后又由总主教率领数十位主教及神父赴佘山朝圣,向天上母后举行再奉献礼。其时有不少外国建筑师在华工作,他们开始以西方技术结合中国传统形式建造教堂,以示对中国文化的尊重。 ===伊" +679,"社会科学院近代史研究所思想史研究室,2007,《中国近代史上的民族主义》,社会科学文献出版社。华夏文化随着中原王国的对外交流和扩张而传播,史载大禹的活动范围已经达到越地;周泰伯建立吴国;在春秋时期,楚、吴、越等地已经成为华夏文明的一部分。 自1840年鸦片战争以来,中国传统文化受到西方文明的强烈冲击,处于劣势地位的中国人产生前所未有的危机感。为了自保、发展,“师夷长技以制夷”,起初以“中学为体,西学为用”的态度向西方学习近现代科学、技术,再后来到民国采用民主共和政体,建立现代国家。民初有識之仕發起「新文化運動」,与此同时,各种西方政治文化思潮,如“马克思列宁主义”、“国家社会主义”,也传入中国。中華文化在中国大陸的文化大革命期间遭受较大损失,全國各地被毀文物、被殺文人较多。目前,在政治思想上,中华人民共和国仍烙有马列主义的特征,但近年来随着经济的崛起,很多大陆民众逐渐恢复民族自信,建立在现代社会基础之上的中国传统文化和以儒家为主体的中国传统信仰的恢复、发展也越来越得到重视。 中国文化的地域性也很强烈。除了一些普遍的特征之外,十里不同风,百里不同俗。1990年以后,随着中国其他地区的人员" +680,"的性质 〉。 Category:亚洲国家 Category:中國的名稱 Category:中華民國的名稱 Category:亚洲分治地区 Category:文明摇篮" +681,"中华人民共和国历史始于公元1949年10月1日在北京举行的开国大典,先后制定并通过了《共同纲领》、《五四宪法》、《七五宪法》、《七八宪法》、《八二宪法》五部宪制性文件。截止2023年,中华人民共和国共经历了毛泽东、邓小平、江泽民、胡锦涛和习近平五位最高领导人,而华国锋曾担任两年过渡时期的领导人。截止2024年,中华人民共和国的历史以1978年12月中共十一届三中全会为转折点,分为以社会主义转型及阶级斗争为主题的毛泽东时代(1949年至1976年)、与以现代化建设及改革开放为主题的邓小平时代(1978年-1997年)和后邓小平时代(1997年起)。中国共产党是中华人民共和国建国以来唯一的执政党,并坚持对全国武装力量的绝对领导。 重大历史事件参见“中华人民共和国历史年表”。 == 中华人民共和国历史概述 == === 建国简史 === ==== 国共内战 ==== 1945年8月,日本无条件投降。抗日战争期间,既已存在的国共摩擦随即演变为国共冲突。1946年,中国共产党与中国国民党争夺中国政权的第二次国共内战全面爆发。1949年初,中国共产党领导解放军取得遼西會戰、徐蚌會戰與平津會戰“三大" +682,"一届全体会议的文件中,中国人民政治协商会议共同纲领和政府组织法中的国号均在“中华人民共和国”后,括号加注“(简称中华民国)”六个字。周恩来受命于毛泽东,于9月26日中午,紧急宴请二十多位元老,讨论去掉“中华民国”的简称。9月27日在政协全体会议讨论和通过共同纲领和政府组织法时,正式决定去掉“中华民国”的简称。此后,新政权的名字确定为“中华人民共和国”。 1949年9月21日至9月30日,中國人民政治協商會議第一屆全體會議在北平召開。9月27日,會議通過《中國人民政治協商會議組織法》和《中華人民共和國中央人民政府組織法》,並決定「中華人民共和國的國都定于北平,自即日起改名北平為北京」;「中華人民共和國的紀年採用公元,今年為一九四九年」;在中華人民共和國的國歌未正式制定前,以義勇軍進行曲為代國歌;中華人民共和國的國旗為五星紅旗,象徵中國革命人民大團結。9月29日,會議通過具有臨時憲法性質的《中國人民政治協商會議共同綱領》,會議代表全國人民的意志,宣告中華人民共和國的成立,組織人民自己的中央政府,並一致同意以新民主主義即人民民主主義為中華人民共和國建國的政治基礎。在普選的全國人民代表大會召開" +683,"%(另一份数据显示,截止1965年文盲率为38.1%)。按照中国国家统计局数据,文化大革命结束后,1978年中国成为世界上最贫穷的国家之一,人均国内生产总值(GDP)仅为156美元,81%的人口为农民,全国文盲人数将近2.4亿、文盲率达25%、青壮年中有30%-40%是文盲。 1970年代末到1980年代初,中国经历了拨乱反正、开始了改革开放,逐渐成为世界上经济发展速度最快的国家之一,2010年中国的国内生产总值超过日本、成为世界第二大经济体。2018年,国际货币基金组织数据显示中国大陆人均GDP达到9608美元(世界银行数据为9771美元)、位居世界第69名;2019年,中国人均国内生产总值人均GDP达到10276美元,开始向高收入国家迈进。2018年,中国大陆农村人口比例降至40.42%,另据联合国教科文组织统计2015年的中国文盲率下降至3.6%。中国官方数据显示,1981-2012年间中国大陆贫困人口减少7.9亿,大多数人民享有食品、住房、医疗等生活必需品。 改革开放期间,中国南方诞生了新兴超大城市深圳市,被誉为“中国硅谷”。2019年,依据《财富》杂志,中国的世界500强公司" +684,"211工程”、“985工程”先后启动。 1979年,中华人民共和国恢复了在国际奥委会的席位。21世纪以来,在历届奥运会奖牌榜上,中国国家队也位居世界前列。此外,中国还先后举办了2008年北京奥运会及残奥会、2010年上海世博会、2010年广州亚运会、2011年深圳大运会、2022年冬季奥运会及残奥会。 ==== 外交军事 ==== 在外交上,1949年后的中国变得较为独立自主,虽然在长达22年的时间里遭受到美国及苏联两大国的封锁、被联合国排除之外(直至1971年取代中国席位),却依然被视為冷战中少数几个独立于两阵营外的最重要国家之一。随着经济实力的发展,中国也日渐成为全球瞩目的大国,区域与全球影响力不斷上升,改革开放后1991年加入亚太经合组织、1997年香港回归、1999年澳门回归、2001年6月上海合作组织成立、2001年12月加入了世界贸易组织,截止2019年9月世界上共有180个国家与中华人民共和国保持外交关系。而一些重要的国际峰会开始在中国大陆举行,譬如杭州主办了2016年二十国集团杭州峰会、厦门主办了2017年金砖国家峰会。 在军事方面,中国也取得了较大的发展,截止2019" +685,"多中央高层的子女或亲属移民海外或拥有外国绿卡。2020年位列透明国际“清廉指数”排名世界第78名。另一方面,社会底层出现了严重的“农民工问题”,2010年以来包括农民工在内的许多社会低收入人群逐渐构成了新兴的“蚁族”阶层。此外,中国的城乡差距逐渐拉大,1996年“三农问题”被正式提出。 人口方面,截止2019年底中国大陆人口数量达14亿、居世界第一,男性人口比女性人口高出3049万。2020年的第七次全国人口普查数据真实性和准确性则遭到多方面质疑。截止2013年,中国国家人口和计划生育委员会等机构估计1970-1999年出生的适婚单身人口中,男性人口比女性高出2315万; 而截止2018年底,中国国家统计局数据显示中国老龄人口(65岁以上)占总人口的11.9%、属于老龄化社会。 此外,随着工业化和城市化的推进,中国大陆的环境污染、资源短缺及过度开采、人均资源占有量低、生产监管漏洞等也逐渐成为较为严重的问题,其中突出的例子包括矿难频发、雾霾、土壤污染、淡水资源短缺等。中国大陆的食品药品安全问题也屡见不鲜,21世纪以来曾曝光过毒奶粉事件、地沟油事件、假疫苗事件、校园食物中毒等重大案件。与此" +686,"等,对中国的民主和法制造成了严重的破坏,包括刘少奇、彭德怀、贺龙等中共中央领导人在内的数千万人遭到批斗和迫害,中国的知识分子、农村精英阶层等受到了巨大打击,文革时期中国的传统文化及道德沦亡,大量文物古迹被毁,科研教育事业遭受了重大冲击。据各方估计约有4000万-8000万人因此非正常死亡。 1980年代,邓小平、赵紫阳等人推动的中国政治体制改革曾被外界寄予期望,但后期的改革最终因为“六四事件”的爆发而失败,中国政府也因此事件遭到了国际社会的普遍谴责和制裁。六四事件直接导致了中国经济上的改革开放放缓甚至停滞,直至1992年邓小平南巡后才重新提速。 1990年代末期起,江泽民时代的镇压法轮功事件和兴起的非法器官买卖、胡锦涛时代开始的对中国维权运动的打压也备受争议。2000年后,随着网络时代的兴起,中国政府采取了严苛的网络审查制度进行舆论控制、异见打压,导致谷歌、雅虎等公司陆续退出中国大陆市场。其中,中国政府还对包括维基百科在内的部分国外网站进行了网络封锁,并被指责雇佣大量网络评论员(俗称“五毛党”)发表拥护中国共产党和中国政府的评论、控制舆论导向。 2010年代,习近平主政下的中国政府为了" +687,"菁英階層卻在此時期被迫害。 1950年代开始“土地改革运动”,并于1950年6月颁布《土地改革法》以指导这项运动,将地主的土地没收,从政治和经济上彻底消灭了中国自秦汉以来作为地方政治精英的乡绅阶层,改变了两千多年来“皇权不下县”的局面,国家政权的动员力从此深入村镇基层。这次土地改革是中国共产党在第二次国共内战时期对老解放区土地改革的继续,基本完成了全国范围内的土改,至1953年,除新疆、西藏、青海、川边等少数民族地区,中国大陆大部分地区的土地改革基本完成,3亿多无地或少地的农民无偿分得约7亿亩土地及生产资料,并每年免除地租3000万吨粮食。但是从1953年开始,中共开始实行土地集体所有制,强制实施农产品统购统销政策和农业合作化运动,再次剥夺了农民的土地,且学者估计土地改革共造成100万-500万地主、富农及亲属死亡,导致农村精英阶层的消亡。与此同时,中共以大城市和经济发达地区为主展开了“三反五反运动”,许多资本家和商人被迫害自杀,亦有许多人士被处决或折磨致死,中国大陆资产阶级消亡。同时间发起了对知识分子的“思想改造运动”。 中華人民共和國成立初期,支持中華民國的部隊到處與解放军和新建的" +688,"还被国军控制。 === 社会主义初期建设 === 1953年至1956年,中国政府对生产资料所有制进行的社会主义改造,内容包括“农业社会主义改造、手工业社会主义改造、资本主义工商业社会主义改造”,又合称为「三大改造」,其目的是改变生产关系的性质。1956年底三大改造基本完成,中共八大提出社会主义制度在中国已经基本建立,并开始全面推行计划经济。在三大改造时期,中国政府同时间开始了社会主义工业化的起步,制定并实施了國民經濟的第一个五年計劃(1953年至1957年),培養了大量的技術專家、產業工人,一定程度上提高了國民收入,並且通過蘇聯和東歐技術的引進提高了解放軍的現代化水平。与此同时,各地农民和手工业者爆发大规模“退社运动”(退出合作社)甚至引发暴动,反抗“统购统销”、“合作化”等政策,但遭到镇压。1954年9月20日,第一部《中华人民共和国宪法》(又称《五四宪法》)在第一届全国人民代表大会第一次会议上通过,共四章106条。 与此同时,各类政治运动逐步开展。1955年7月1日,中共中央發出《關于展開鬥爭肅清暗藏的反革命分子的指示》,在中國大陆各省、市、自治區、人民解放軍和中央一級黨的、政府" +689,"世界各地,大规模疫情持续到1958年,据世界卫生组织估计,全球共有100万-400万人因此死亡。此后,亚洲流感病毒变异,并于1968-69年导致“香港流感”爆发,再度造成全球100万-400万人死亡(世卫组织数据)。 * 1950年代末,中国大陆基本消滅血吸蟲病,但是近年又有死灰復燃趨勢。 * 1955年,江苏南通爆发“脊髓灰质炎”,俗称小儿麻痹症,此后一段时间内,中国每年报告的脊髓灰质炎病例有2万-4.3万例,造成大量儿童残疾甚至死亡。1958年起,顾方舟等人开始进行脊髓灰质炎疫苗研究,在美国、苏联的技术和疫苗基础上,进一步研发出口服型“糖丸”疫苗。 === 大跃进及大饥荒 === 1957年11月,《人民日报》发表社论,提出了“大跃进”的口号。1958年,人民公社运动、“打麻雀运动”开始在全国范围内推广。1958年5月,中共八大二次会议提出要使中国在15年或更短的时间内,在主要工业产品产量方面在十年內超过英国、十五年內赶美國(所謂「超英趕美」),提出“社会主义建设总路线”(此后与大跃进、人民公社被合称“三面红旗”),中共中央主席毛泽东号召大家要破除迷信,解放思想,发扬敢想敢说敢干的" +690,"8年青海爆发“循化事件”,解放军镇压造成数百人死亡;1959年爆发藏区骚乱,造成约8万7千人死亡,第十四世达赖喇嘛流亡海外。 1959年庐山会议期间,彭德怀元帅等人批评大跃进政策,但被毛泽东打成“彭德怀反党集团”,引发了此后中共党内的“反右倾运动”。此時中蘇關係也開始出現裂痕,1960年蘇聯撤走在華的所有人員,並且停止對中國的技術協助,中國的工業幾乎因此完全停頓。中蘇兩國的矛盾之後又迅速公開化,兩國在國際論壇上公開翻臉,史称“中苏交恶”。1960年冬,由于大跃进导致大饥荒和经济困难,同时在思想上“左”的错误泛滥成灾、浮夸风盛行,中共中央和黨主席毛澤東開始糾正農村工作中的「左」傾錯誤,並且決定對國民經濟實行「調整、鞏固、充實、提高」的八字方针。隨即在國家主席劉少奇、國務院總理周恩來、黨副主席陳雲、書記處負責人鄧小平等的主持下,制定并执行了一系列补救性措施。 大跃进导致原有的經濟體系被破壞,農業產值大幅度減少,直接导致“三年經濟困難”和1958-1962年的大饥荒。當時由於中國內地的消息封鎖,一般專家不易精確的統計死亡人數,因此导致死亡人数一直有所争议,学术界各方估计有1500万-5500" +691,"攻擊,譬如姚文元发表的文章“评新编历史剧《海瑞罢官》”。 1966年5月16日,中共中央政治局擴大會議通過五一六通知,「無產階級文化大革命」爆发,毛澤東第一次試圖利用民意來打擊領導層的劉鄧派,稱後者是「資產階級司令部」,從此中國陷入十年動亂。8月5日,毛澤東的《炮打司令部——我的一張大字報》,矛頭直指劉少奇。北京“红八月”期间,毛泽东首次接见红卫兵,此后北京红卫兵开始“破四旧”,并迫害和屠杀黑五类及其家属,成为文革“红色恐怖”源头,全國的青年學生組織「紅衛兵」被教唆來攻擊全國各階層的黨的當權者。此后诸多地区发生大屠杀事件,在没有饥荒的情况下,广西大屠杀中甚至发生了大规模人吃人事件。1967年1月,上海爆发“一月风暴”,各地造反派开展夺权运动,建立革命委员会以取代原有的党政机关。而各派系之间亦有权斗,导致大规模武斗,致使1967-68年间中国大陆进入全面内战。一些地區情況失控,甚至連毛澤東本人也無法控制,不得不派出軍隊來平息這些過激的衝突。毛澤東發現學生失控之後,決定發動知識青年「上山下鄉」,转由工人阶级领导文化大革命。相对应的,文革期间高考取消,而工农兵学员于1970年起靠推荐上大学。" +692,"共和国历史上的第二部宪法。1976年,周恩來逝世,清明節期間發生以紀念周恩來為名、實為支持鄧小平的四五天安門事件。此后鄧小平再次被打倒,毛澤東任命華國鋒為中共中央第一副主席兼國務院總理。1976年9月,毛澤東逝世,黨內第一把手的位置由毛澤東評價為「你辦事,我放心」的第一副主席華國鋒取得。10月初,華國鋒在葉劍英、汪東興的支持下,成功一举粉碎四人帮,文革结束。此后,华国锋正式出任中共中央主席和中共中央軍委主席,繼任最高領導人。 文革期间,知识分子被视为反动分子、资产阶级、反动学术权威,受到广泛的批判和迫害,甚至被贬低为“臭老九”。大批知識分子、專業技術人員、早期革命家被粗暴對待,有些人被活活打死,有些則自殺身亡,中国的科研事业也因此受到重大冲击。仅在青海省的核基地内,截止1971年9月就有四千多名职工受到迫害和审查、310多人伤残、40多名职工自尽、5人遭到枪决。1968年,“两弹一星元勋”姚桐斌被毒打致死、“两弹一星元勋”赵九章遭迫害自杀身亡。截止1968年底,中国科学院在北京的170余位高级研究人员中(均是院士)就有131位被打倒,当年即有20位一级研究员自杀,全院在文革中被抄家的达" +693,"著中華人民共和國首次受到主要國際組織的承認。1973年5月,中美雙方同意互設聯絡處,增加聯繫。1970年代,在美苏冷战、中苏交恶等国际背景下,毛泽东提出了“一条线、一大片”的构想,即美国、日本、中国、巴基斯坦、伊朗、土耳其、西欧结成准联盟,共同对付苏联的扩张势头,此后还提出了“三个世界”的理论。 文革时期的外交曾受极左思潮的影响,导致中国与近30个国家发生了外交纠纷、与部分国家恶化到断交的地步。此外,自1962年中印边境战争爆发后,1967年再度爆发中印边境冲突。1967年发生的红卫兵“火烧英国代办处事件”是建国以来最严重的涉外事件。许多中国驻外大使、参赞几乎全部奉调回国参加政治运动,许多人受到批斗,驻外使馆的工作不能正常进行。对外政治、经济、文化交流和合作基本中断,与各国元首和政府首脑级的互访急剧减少,实际退出了几乎所有的国际组织,也不出席国际会议;外贸进出口大幅度下降;派出的留学人员全部被召回国,也停止接受外国留学生。也因对外输出革命而导致世界部分地区出现屠杀、排华现象,造成大量华裔伤亡(如:柬埔寨红色高棉大屠杀、印尼九三〇事件)。 === 灾祸 === 下表(可点击展开)只展示重" +694,"1960年5月 大同老白洞煤矿瓦斯爆炸事故 山西 684 中华人民共和国历史上最严重的矿难事故。 1963年 海河特大洪灾 海河流域 5000余人 北京、河北受到严重影响。 1966年3月 邢台大地震 河北 8064 震级为里氏6.8级,震中烈度9度。 1966-1967年 流脑大流行 全国 16万余人 1968-1969年 香港流感 全球 1968年7月源于香港,是1957-58年亚洲流感的变种。中国大陆死亡人数未知,全球约100万-400万人死亡(世界卫生组织数据),是人类历史上致死人数最多的大瘟疫之一。 1969年7月 长江洪水 长江流域 1603 湖北、安徽等地受灾,71万间房屋倒塌。 1969年7月 超强台风维奥娜 广东 1554 在中国大陆又称“七二八风灾”或“牛田洋风灾”。 1970年1月 通海地震 云南 15621 矩震级为7.7级,震中烈度10度。 1970年代起 乙型肝炎 全国 70年代起,中国大陆开始爆发乙肝疫情,此后1.2亿人成为乙肝病毒携带者,每年有数十万人因此死亡,主要由于乙肝引起的肝硬化和肝癌,被称为“中国第一病”。 1972年11月 台风柏美娜 广东 4" +695,"間(1949-1976年),作為中華人民共和國的主要創建者之一,對中國近、现代史產生了重大影響。一方面,在他的领导下中国共产党統一了中國大陸絕大部分地區,建立強而有力的中央政府,为此后中国的恢复和发展奠定了基础;中華人民共和國政府稱,1949年中國人平均壽命僅為35歲,而到毛澤東去世時,平均壽命則提升到男人65岁、女人69岁。但另一方面,毛澤東沒有積極控制人口增長,反而鼓勵生育,与人口学家马寅初展开论争、导致后者遭到批判。1949年中國大陆有4.5億人,到1976年時達到9.3億人。批評者認為人口激增造成了災難性的結果,而也有學者認為人口增長並非來自於高生育率,而是來自於死亡率的大大降低。毛澤東死後,中國領導人面對這個嚴峻的問題時採取了嚴格的計劃生育政策。 与此同时,如同過去中國歷史的開國皇帝,在其任內也有許多人遭到政治迫害及大清洗,尤其在他晚年,由於嚴重的政策錯誤,給中國精神文化領域造成巨大的損失。毛澤東時代歷經了建政初期1950-1953年的三大運動「抗美援朝、土地改革運動、鎮壓反革命運動」、1951-1952年的三反五反运动、1957年起的反右運動、1958年起的大躍進、1966" +696,"胜利,开始了平反冤假错案和公审四人帮的工作,并逐步将矛头对准了文化大革命和晚年的毛泽东。拨乱反正时期以《关于建国以来党的若干历史问题的决议》的通过和中共十一届六中全会上中共中央主席华国锋辞职而结束《拨乱反正》, 中共党史出版社, 1998《文化大革命时期资料选辑:重要事件的平反和拨乱反正》,中共浙江省委党校党史敎硏室, 1985。鄧小平在1977年重新出山,再次恢復中共中央政治局常委和中共中央副主席等職務,並在黨內安排許多自己的支持者。在1978年鄧小平成功地取代由毛澤東欽點的繼承人華國鋒。不過這次鬥爭並沒有像共產黨以前的政治鬥爭中那樣你死我活,而是讓華國鋒漸漸地隱退。1980年,華國鋒國務院總理的職務由趙紫陽取代,而恢復設立的總書記職務則由胡耀邦取得。直到1989年11月在中共十三届五中全会后卸任中共中央军委主席(甚至到了1997年逝世前),鄧小平一直都被认为是中國的實際最高領導人,雖然他後期握有的頭銜只是中央軍委主席和中央顧問委員會主任。 === 历史事件 === ==== 文革落幕 ==== 1975年,邓小平继任中共中央副主席和国务院第一副总理,开始了大整顿、史称“1975年整" +697,"远新安置问题和毛泽东手稿问题,以及中央全会召集问题大闹政治局,华国锋与四人帮矛盾升级,也引发了华国锋发动政变的想法。10月6日,华国锋得到了中共中央办公厅主任汪东兴的协助,与叶剑英一起以召集毛泽东选集讨论的名义,诱捕了张春桥、王洪文和姚文元,并逮捕了江青和毛远新,史称“粉碎四人帮”。华国锋连夜召集在京中共中央政治局会议,政治局一致同意华国锋继任中国共产党中央委员会主席和中国共产党中央军事委员会主席职务。「四人幫」的垮臺意味著「文革」的正式終結。四人帮垮台之后几天,中央宣布了这一消息,全国沸腾。10月13日,中共中央主席华国锋登上了天安门城楼,接受全国人民的拥戴。从此华国锋被称为“英明领袖”,享受与毛泽东生前同等的待遇。 ==== 华国锋当政 ==== 华国锋延续了毛泽东以“阶级斗争为纲”的路线,继续坚持在无产阶级专政下继续革命的理论。1977年2月7日,《人民日报》发表了新年社论《学好文件抓住纲》,社论里出现了“凡是毛主席作出的决策,我们都坚决维护;凡是毛主席的指示,我们都始终不渝地遵循”的说法,被称为“两个凡是”。这既是政治宣告,也是华国锋执政的基本依据。两个凡是受到了在文革期间遭迫" +698,"云、王震等人的支持下,邓小平获得平反。1977年5月,中共十届三中全会召开,这次全会有两个议题,一个是宣布邓小平恢复原有的中共中央副主席、第一副总理和军委副主席兼总参谋长的职务,一个是公布了四人帮的罪行。邓小平第三次重返政坛,与之相应的,则是胡耀邦、赵紫阳、彭冲等也进入了中共中央政治局。胡耀邦开始主持中共中央党校,担任中共中央党校副校长。1977年8月,邓小平主持召开了科教座谈会,决定当年便恢复高考。同年9月,邓小平在与教育部负责人谈话时,首次公开提出要进行“拨乱反正”。而拨乱反正也延续到了邓小平时代的初期。在这一时期,文化大革命结束,国家由乱到治,经济开始复苏,同时大批在文革期间被打倒的干部得以重新工作,从而揭开改革开放的序幕。 == 邓小平時代(1978年-1989年) == === 拨乱反正及十一届三中全会 === 文革后的拨乱反正延续到邓小平时代早期。1978年5月,邓小平、胡耀邦等人推动了一场社会各阶层参与的真理标准大讨论,反对华国锋的“两个凡是”和“凡是派”,促成了全社会的一次思想解放运动,也是中国大陆80年代“新启蒙运动”的起点。1978年民间也自发出现“民主墙运动”,要" +699,"而王洪文曾说过:“十年以后再看分晓”,他的地位也最高,罪行及影响仅次于江青和张春桥,所以将他判为无期徒刑。 1981年6月27日,中国共产党第十一届中央委员会一致通过《关于建国以来党的若干历史问题的决议》,全面否定了文化大革命,也标志着意识形态上的拨乱反正基本完成。1983年,文化大革命后社会治安混乱、恶性犯罪事件频发,邓小平等人进行了大规模的严打,三年多共有2.4万人被判处死刑。 === 改革开放 === ==== 意识形态 ==== 拨乱反正自1970年代末持续到1980年代初,1981年的《关于建国以来党的若干历史问题的决议》,官方正式認定毛澤東的「文化大革命」為一個錯誤。中國也公開審判包括江青在內的「四人幫」等「文革」中的主要領導者。新的、較務實的領導層更加重視經濟建設,而不是頻繁的政治運動,拨乱反正否定了“以阶级斗争为纲”的路线,提出了“以经济建设为中心”的路线。鄧小平也批評華國鋒的「兩個凡是」,認為並不能因為與毛澤東的看法不同就放棄某一政策,事實上打破個人崇拜的土壤。鄧小平也不像陳雲等一批比較保守的共產黨人,認為那些發達資本主義國家的政策就不能用在社會主義中國。 1978年" +700,"標指的是農業、工業、科技與國防的四個現代化。而實現這四個現代化、使中國成為一個現代化工業國家的方法,就是實行社會主義市場經濟。鄧小平指出,中國處在社會主義的初級階段,而黨的任務就是完善「有中國特色的社會主義」。這種對馬克思主義的新解釋降低意識形態在經濟發展中的重要性,而更強調政策的有效性。鄧小平認為共產主義遠景需要很長時間,需要幾代甚至幾十代的努力才能實現,認為社會主義並不意味著共同貧窮(原話為:貧窮不是社會主義)。雖然鄧小平為經濟改革提供理論基礎與政治上的支持,鄧小平本人並沒有提出很多經濟改革的具體方案。很多時候改革是由地方領導自行提出的,往往並不符合中央政府的決策。但是如果這些改革措施是有效的,那么全國就都可能普遍採取這種措施(大陸媒體多稱之為:貓論,即不管是黑貓還是白貓,抓到老鼠的就是好貓。)。很多經濟改革的政策都來自其它有經驗的國家(和地區),例如亞洲四小龍等。 中國的經濟改革與蘇聯共產黨中央委員會總書記戈爾巴喬夫在蘇聯實行的「新思維」有很鮮明的對比。「新思維」的很多政策是由戈爾巴喬夫本人親自貫徹落實的。許多經濟學家認為,鄧小平採取的「從下到上」的改革方法,而不是蘇聯「從上到下" +701,",如匈牙利或南斯拉夫相比,中國的輕工業投資並不需要政府批准。而中國重工業投資主要來自銀行貸款,而銀行的資金則主要來自消費儲蓄。鄧小平改革的第一個項目就是防止利潤通過稅收或銀行體制以外的任何方式進行再分配;因此,在更高端產業中的再分配往往是間接的。總而言之,鄧小平的改革開啟中國的工業革命。這些改革措施與毛澤東的自力更生政策是相互補的。 改革也十分重視勞動生產力的提高。新的物質刺激與獎賞制度在全國實施。農村市場恢復銷售家產或多餘的農產品,也促進農村工業發展,還為更艱難的經濟改革提供政治上的支持。鄧小平的(特別是早期)市場社會主義在某些地方與列寧的新經濟政策或布哈林的經濟政策有某些相似之處。它們都允許私有企業以及貿易市場的存在,而不是完全的政府控制。有趣的是在鄧小平首次會見阿曼德·哈默時,鄧小平要求這位曾經參與投資過列寧時代蘇聯的工業家向他詳細地解釋列寧當時的新經濟政策。 ==== 对外开放 ==== 改革开放期间,中國決定通過增加國際貿易,特別是購買來自日本和西方的機械來加速現代化進程。通過這種出口導向經濟模式,中國獲得一定的外國資金、市場、先進技術與管理理念,從而加快其經濟建設的步伐。1" +702,"乱反正期间,“实施义务教育”的口号被提出;在邓小平等人的推动下,1982年“普及初等义务教育”被写入新编宪法《八二宪法》,恢复了对知识和教育的尊重。1985年,在时任国务院总理赵紫阳的提议下,人民代表大会通过,确定每年“9月10日”为教师节。1986年,中国大陆《义务教育法》颁布,九年制义务教育确立。 邓小平曾提出“科学技术是第一生产力”、“四个现代化,关键是科学技术现代化”等观点。1981年12月,邓小平批准修建“北京正负电子对撞机”,此后又多次会见支持该项目建设的诺贝尔奖得主李政道;1984年10月7日,对撞机工程破土动工。1985年2月,中国南极长城站建成,是中华人民共和国的第一个南极科学考察站;同年3月,秦川核电站动工建设,是中国大陆建成的第一座核电站。1986年3月,在四位两弹一星元勋的建议下,邓小平亲自批准开启了“863计划”,由时任国务院总理赵紫阳负责,同年即调拨100亿人民币作为项目专项资金(占全国财政总支出的二十分之一),863计划此后的重大成果包括神舟飞船、蛟龙号潜水器、天河一号及天河二号超级计算机、龙芯等等。1987年,深圳大亚湾核电站动工建设,是中国建成的第二座" +703,"表大会制度; # 要体现不允许权力过分集中的原则; # 要明确哪些问题应当由集体讨论和决定,决定时,要严格实行少数服从多数,一人一票,每个书记只有一票的权利,不能由第一书记说了算。 但此后的“波兰团结工会事件”对中国大陆的政治体制改革产生了负面影响。1982年12月4日,第五届全国人民代表大会第五次会议审议通过了具有历史性意义的《八二宪法》,该宪法也成为了中华人民共和国的第四部宪法。《八二宪法》收入了许多宪政主义的内容和条款,为改革开放奠基。其中的部分修订内容包括: * 删除了诸如“在无产阶级专政下继续革命” 的文革语言此说法开始于1969年中国共产党第九次全国代表大会林彪的政治报告,参加《中国共产党第九次全国代表大会政治报告》; * 取消了“国家由中国共产党领导”的条款,不在具法律效力的宪法正文直接出现“中国共产党”字样; * 规定了包括中国共产党在内的“任何组织或者个人都不得有超越宪法和法律的特权” ; * 一定程度上理顺了党与法的关系、推行党政分离,将中国共产党党务机构分离出国家机构运作体系,明确区分了立法权、司法权和行政权例如宪法正文不再直接出现“中国共产党”字样,参见1982" +704,"制改革涉及政治体制,不进行政治体制改革,经济体制改革就改不动,改不了。” 1986年9月,“中央政治体制改革研讨小组”成立,成员包括赵紫阳(时任中国国务院总理)、胡启立、田纪云、薄一波、彭冲。9月3日,邓小平在会见日本公明党委员长竹入义胜时说:“我们提出改革时,就包括政治体制改革”。但他的政治改革出发点是,在中国共产党一党专政的前提下,实行党政分开、提高行政效率、革除官僚主义弊端、推动经济制度进一步改革等,并要求不能照抄西方的宪政制度。邓强调:要使行政机构能够有效地工作,不能过多干扰行政机构的工作。决定了就办,这是我们的优势,这个优势不能丢。不能放弃专政,不能迁就要求民主化的情绪。我们要搞一个什么样的体制?要搞一个增强行政效能的体制,机构要精简。......民主还是个手段,讲民主必须要和法制联系起来讲,把法制搞起来,才能有稳定的社会环境。我们的行政机构应该很有效能。此时,体制内外的人士对加速政治体制改革的强烈要求是一致的,而外界也对此次改革的决心和力度抱有期待。其中部分人士还公开提出了“多党制”、“三权分立”、“议会民主”、“司法独立”等西方宪政主义的架构。虽然这些与邓小平等人的中共官" +705,"0年代后期中国大陆的政治体制改革陷入停滞,有学者将此次政治改革的失利归咎于邓小平的个人局限性(譬如坚持四项基本原则),也有人认为是由于中共左派保守势力的干扰和围剿,还有人认为六四事件中部分过于激进的学生也负有责任。 === 民主运动及政治風波 === 在中国經濟發展的同時,對現實政治控制以及諸如物價上漲、幹部腐敗,雙軌制引發的官倒等社會問題的不滿也開始顯現。而此时的东欧民主化也影响了中国社会。在“新启蒙运动”的大背景下,學生和一些知識份子要求更大的政治改革與自由,中共內部的一些元老则不斷懷疑改革的進度以及改革的最終目標與社會主義背離。1986年12月,一批示威的學生開始公開反對改革的緩慢進度,也使這些中共內保守派更確信對改革的懷疑,擔心這種改革可能危及社會主義制度和中國共產黨的統治地位。胡耀邦这一鄧小平的親信以及改革的積極推動者,被指責應該為1986年的示威事件負責,而於1987年1月被迫辭去中共中央總書記職務。國務院總理趙紫陽接任總書記,而副總理李鵬被任命為總理。在趙紫陽接任後,他所倡導的政治與經濟改革受到越來越多的攻擊。而他在1988年5月提出的價格調整也引起普通民眾對通貨膨脹的強" +706,"队干部并讲话,认为“六四”风波“是国际的大气候和中国自己的小气候所决定的,是一定要来的”,认为“极少数敌对势力”希望“建立一个完全西方附庸化的资产阶级共和国”。他强调,中国共产党先前制定的方针和路线没有错,要继续坚持。 六四天安门事件後,中国共产党第十三届中央委员会第四次全体会议召开,会议分析了国内发生政治风波的原因以及性质,对中央领导机构成员进行了调整,赵紫阳、鲍彤等中共改革派高层被撤职,江泽民、宋平、李瑞环成为中央政治局常委,江泽民成为中央委员会总书记,江泽民、李鹏、乔石、姚依林、宋平、李瑞环组成新的中央政治局常务委员会,此后中共强调开展“反和平演变”。国际社会对此事件中的武力镇压普遍表示了谴责和甚至进行制裁,也有部分国家表示同情或者支持。 === 九二南巡前后 === 六四事件后,中國再度進入快速發展時期。1990年底,上海证券交易所、深圳证券交易所先后成立。起初,黨內主張減緩政治改革力度以維持政局穩定的觀點逐漸成為主流觀點,為了維護局勢的穩定,經濟改革步伐也曾一度減慢,直到鄧小平1992年的南巡讲话後,經濟局面有較大幅度的改善。鄧小平再度重申要發展市場主導的經濟,拯救了中国新兴" +707,"來,無疑是非常輕的 。根據《財富》雜誌記者吉姆·羅沃的說法,「鄧小平及其支持者在1979至1994年間的改革帶來的有可能是任何時候、任何地方所發生的最大規模的對人類生活水平的提高。」這種進步影響上億的人口。鄧小平於1997年去世。但他的時代並沒有結束。迄今,他的理論,路線,方針,政策,依然居主導地位,並以鄧小平理論的形式固定下來,在中國大陸被譽為「當代中國的馬克思主義」,獲得和馬克思列寧主義、毛澤東思想同等的地位,並被寫入《中華人民共和國憲法》和《中國共產黨章程》。 === 外交方面 === 改革開放后,中國與世界的聯繫也更加緊密。1979年1月1日,中华人民共和国与美利坚合众国建立外交关系。鄧小平上台後出訪多個西方國家及日本,1979年,他訪問美國,與卡特總統會晤。1984年12月19日,中英兩國簽署《中英聯合聲明》,正式決定在1997年英國將歸還在鴉片戰爭中侵佔的香港(中國大陸官方稱從未喪失香港的主權,1997年只是恢復行使對香港的主權)。鄧小平同意在50年內不干涉香港的資本主義經濟體制和市民的生活方式。這種“一國兩制”的構想後來沿用在澳門問題,與葡萄牙在1987年4月13日共同簽" +708,"重要城市和县镇,一个月之内便宣布胜利,撤出越南,而越方在中方撤出之后也宣布取得战争的胜利。这场战争令中越两国关系进一步恶化到最低点。中国政府方面声称“越南种种举动威胁中国的边境安全和影响东南亚的局势稳定”,但实质上是为支援亲中的红色高棉而发动“自卫还击战”进入越南。而当时国际上大部分西方国家认为越南应从柬撤军,中国则应从越南撤军(实际上中国在一个月后从越南撤军,而越南仍通过傀儡政权维持对柬占领,长期拒不撤军)。以苏联为首的社会主义国家阵营认为是中国侵略越南,亚洲大部分国家如日本、印度和蒙古等国也要求越南停止侵略柬埔寨而中国也应从越南撤军。越南于1989年1月宣布将于在9月之前将越军全部撤出柬埔寨。此外,进入八十年代后中越两国继续对抗,“两山战役”持续十年,在罗家坪大山、法卡山、扣林山、老山、者阴山等地区相继爆发边界冲突,1988年双方还进行了“赤瓜礁海战”。九十年代苏联解体后,两国关系逐步恢复正常,陆地边界也最终划定。 邓小平于1981年6月正式就任中共中央军委主席一职,同年9月解放军11.4万余人进行了华北大演习,是中华人民共和国成立以来解放军规模最大的一次军事演习。1984-85年" +709,"安康城区,造成经济损失4亿余元。 1986年7月 粤东洪灾 广东 261 直接经济损失22.6亿元。 1987年5月起 大兴安岭火灾 黑龙江 200余人 火势蔓延到苏联地区,是历史上最大规模的火灾之一。 1988年1月 西南航空空难 重庆 108 中国西南航空4146号班机坠毁,机上10名机组人员、98名乘客全部遇难。 1988年11月 澜沧-耿马地震 云南 748 另有7700人受伤,直接经济损失为20多亿元。 === 争议 === 有观点认为,邓小平时期中国共产党主导的拨乱反正存在局限性,例如“四项基本原则”的入宪,不许否定社会主义、中国共产党的领导、马克思主义、毛泽东思想。 邓小平于1980年提出要批判和反对“资产阶级自由化的倾向”,并一直坚持反对资产阶级自由化。还有学者认为,邓小平在对毛泽东本人的评价上以及在对极权主义、专制主义的反思中也体现出了邓个人的局限性,尤其是在1980年代初《关于建国以来党的若干历史问题的决议》的起草过程中,邓坚持要求对毛作出“功大于过”的评价、要求拥护“毛泽东思想”。中共左派保守人士则分别于1983年、1987年发起“清除精神污染”、“反对资产阶级自" +710,"大中連任國家主席。總理李鵬任期屆滿後擔任全國人大常委會委員長,國務院總理一職由朱鎔基接任 。 === 内政 === ==== 政治经济 ==== 在1990年代,中國經濟依然保持正常發展,這使得中國在過去二十年內一直保持著9.7%的平均增長率。1992年,邓小平在中国南方巡视,重申改革開放。在全球經濟衰退的大背景下,中國大陸的國內生產總值依然保持7%的增長,這主要是通過國家對經濟的積極干預,加大政府在基礎設施領域的直接投資來實現的。另外政府也試圖用各種辦法來刺激需求,但是效果不明顯。政府直接投資的增加,也導致財政赤字的增加。雖然統計數據顯示經濟的持續增長,但是一些深厚的結構性問題仍然存在,甚至日益嚴重:貧富分化加劇,地區間發展不平衡、國有企業效率低下以及陷入呆賬的銀行系統。对此,1996年“三农问题”被正式提出,而西部大开发也于1999年启动。而處理效率低下的國有企業的同時引發大量失業工人,但是相應的金融保障體系剛剛建立,無法起到應有的作用,大量老工業基地的工人生活水平大幅度下降,陷入貧困狀態,社会不稳定增加。 1994年12月,长江的“三峡工程”开始建设。中国政府于1997年和199" +711,"子足球队在主教练米卢的带领下,首次晋级“世界杯”决赛圈。2002年8月,在美籍华裔数学大师陈省身的努力下,四年一度的国际数学家大会在北京召开,陈省身担任大会名誉主席、吴文俊担任大会主席,这是该大会首次在发展中国家召开。 === 外交 === 1990年代,中华人民共和国外交形势嚴峻。冷戰的結束與蘇聯解体意味著中國與世界唯一超級大國美國之間原本存在著的最重要的合作基礎的喪失;六四之後的中美關係也很難在回到八十年代的蜜月期。雖然江澤民與克林頓將兩國關係定位為「戰略夥伴關係」,中美在整個1990年代實際上處於一種非敵非友的狀態之中,兩國之間存在一些根本性歧見,在一些具體問題上卻又能夠溝通合作。江澤民任內極力推動中美關係發展,包括1997年和1998年兩國元首的互訪,以及朱鎔基總理的訪美等。但是美國國內始終有一股強大的抗拒與中國保持親密接觸的力量,這些被稱為「藍隊」的人認為在中國改善其自身人權、民主等方面的紀錄之前,美國應盡量疏遠甚至孤立中國。1999年中國駐南聯盟大使館遭襲事件、2001年南海危機等令中美关系紧张;2001年美国911事件后,中美关系缓和。此时,無論是中國還是西方的大部分國際" +712,"认“九二共识”,極力與大陸修好,兩岸走向和平发展的局面,此路線也造成台灣內部的爭論 。 === 灾祸 === 下表(可点击展开)只展示重大的自然灾害或人为事故(造成至少100人死亡,或重大经济损失,或重大社会影响)。 时间 重大灾难 地点 死亡人数 描述 1990年10月 10·2空难 广东 128 中国厦门航空8301号班机被劫持,在广州白云机场降落,但先后撞上了停泊在地面的中国西南航空4305号班机和中国南方航空3523号班机。 1991年4月 山西三交河矿难 山西 147 事故造成147人死亡、2人重伤、4人轻伤。 1991年5月起 华东水灾 华东 至少431 安徽和江苏两省最为严重。安徽省受灾人口达4800多万、死亡267人,农作物受灾面积430多万公顷,直接经济损失近70亿元人民币;江苏省受灾人口达4200多万、死亡164人,农作物受灾面积300万公顷,直接经济损失90亿元人民币。 1992年7月 南京7·31空难 江苏 106-109 中国通用航空2755号班机起飞时坠毁。 1992年11月 桂林11·24空难 广西 141 中国南方航空3943号班机坠毁,机上133名乘客" +713,"997年 亞洲金融危機 亚洲 对中国经济造成一定冲击。 1998年 特大洪水 长江流域等 3656 百年不遇的特大洪水,而许多水利工程质量低劣,被时任总理朱镕基斥责为“豆腐渣工程”、“王八蛋工程”。全年因洪灾死亡4150人。 1999年11月 烟台11·24特大海难 山东 282 “大舜号”轮船在烟台附近海域倾覆,282人遇难、仅有22人生还,直接经济损失9000多万元。 2000年9月 贵州木冲沟矿难 贵州 162 2000年12月 洛阳东都商厦火灾 河南 309 2001年3月 靳如超爆炸案 河北 108 另有38人受伤。 2001年7月 南丹矿难 广西 81 事故发生后,当地政府决定对此事隐瞒不报,但记者们还是冒着生命危险对这个事故进行了调查。 2002年5月 大连5·7空难 辽宁 112 中国北方航空6136号班机由于乘客纵火而失控坠海,机上103名乘客和9名机组人员全部遇难。 2002年6月 鸡西矿难 黑龙江 124 另有24人受伤。 === 争议 === 1991年,中共中央总书记、中央军委主席江泽民访问苏联,与时任苏共中央总书记、苏联总统戈尔巴乔夫签订《中苏国界东段协定》" +714,"程”,而官方公布的伤亡人数也受到质疑。 1999年,江泽民为首的中国官方决定镇压法轮功(参见:610办公室),引发争议。而从江泽民时期出现的非法器官买卖、移植事件也屡见不鲜。 == 胡锦涛及第四代领导时期(2002年-2012年) == === 交接 === 江澤民等人在中共十六大上退出中央政治局和中央委員會,讓位給由新任總書記胡錦濤率領年輕的「第四代」領導集體。不过,江澤民依然在發揮影響力,並直到2004年9月才辭去中央軍委主席,交出軍權。胡錦濤是第一個1949年立國後加入的共產黨最高領導人。胡錦濤在1992年49歲時就進入中共中央政治局常務委員會和中共中央書記處,是所有委員中最年輕的。新一屆的領導人被賦予高度期望,無論是國內國外,大多數媒體都期望,「胡溫體制」會把中國大陸帶入一個新的階段。國外的媒體更關注新政府在政治改革方面的舉措,而中國大陸的媒體則更關心新領導層的親民形象。2004年9月,中共十六届四中全会首次提出了构建社会主义和谐社会的历史任务。 === 内政 === ==== 政治经济 ==== 雖然经历了非典疫情(SARS)的衝擊,2003年中国經濟成長還是達到近幾年來最高" +715,"GDP)的50%,并在此后继续扩大。但是在石油业和银行业等行业,国有垄断企业仍然存在。 在此期間,中國的房地產業迅速發展,甚至成為一些城市的支柱產業。與此同時,商品房價格,建築材料價格也大幅度上漲,這引發房地產泡沫和整個國民經濟的過熱。房地產泡沫和銀行巨額呆壞賬增加了金融風險。房價的過快上漲也導致非法佔地、拆遷糾紛等各種社會問題。中央政府試圖進行調控,但是由於地方政府和既得利益集團從中作梗,這些調控措施並沒有受到預期效果。 2005年12月29日,十届全国人大常委会第十九次会议决定,自2006年1月1日起废止《中华人民共和国农业税条例》。由此,中国不再针对农业单独征税,一个在中国存在两千多年的古老税种宣告终结。与此同时,2005年起,中国政府开始大力整治、关闭小煤窑。 2007年以来,中国境内的恐怖主义事件明显增多。新疆维吾尔自治区发生多次发生恐怖性质的袭警、爆炸案件,包括2008年的喀什袭击事件、库车爆炸案和2011年的和田7·18严重暴力恐怖事件、喀什暴力恐怖袭击事件。此外,也发生针对民航班机的恐怖事件,如南航班机三·七爆炸未遂案(2008年)和天津航空7554号班机劫机事件(20" +716,"容和支持。2010年5月1日至10月31日,上海世界博览会举行,是中国首次举办的综合性世界博览会,也是首次由发展中国家主办综合性世博会,共有256个国家和地区及国际组织参展。 ==== 港澳地区 ==== 香港在1997年回歸中國後,由於亞洲金融風暴和一系列的經濟、政治難題的影響,當2003年初港府宣佈將就基本法第23條有關顛覆罪立法時,引起許多人的反對。部分香港人認為,23條立法是行政長官董建華受命於中央政府而做出的決定,他們擔心立法會限制到港人的政治自由。港府做出多項讓步,但是還是無法平息爭議,最終在7月1日回歸紀念日當天,50萬市民走上街頭抗議立法,該事件也引起中央政府的關切,遊行最終導致香港政府宣佈擱置立法計劃。 在七一大遊行後,香港民主派開始更積極要求政治改革,他們公開提出,要2007年特首直選、2008年立法會全面直選。中國政府始終堅持香港是中國的內政,反對外國政府干預內部事務。中國政府的立場是,「一國兩制」應以「一國」為先,中國是單一制社會主義國家,香港作為中國的一部分,沒有權利自行改變政治體制。2004年4月,全國人大常委會對《香港基本法》做出解釋,否決2007年特首和" +717,"东盟国家开展海上对话与合作,一定程度上维护了地区的稳定。 2005年、2010年全国多个地区爆发大规模反日示威活动。2005年3月至4月其间在中国各地举行的一系列大型游行和抗议活动,主要是反对历史教科书和日本争取成为联合国安理会常任理事国的努力。2010年10月,因中日撞船事件,西安、成都、郑州、宁波等地举行反日示威游行活动。 2012年8月中旬开始,为抗议日本于2012年上半年提出的钓鱼岛国有化政策、8月15日扣押香港保钓人士和8月19日日本右翼登陆钓鱼岛。9月10日,日本政府正式“购买”钓鱼岛后,中国大陆民众发起第二轮游行抗议活动,最终演变成暴力民众对日系商家、私家车的烧抢打砸。针对日本政府“购买”钓鱼岛,胡锦涛政府表示坚决反对,认为日方采取任何方式‘购岛’都是非法的、无效的,并采取了一系列反制措施。 === 灾祸 === thumb|四川汶川大地震(2008年5月12日) 下表(可点击展开)只展示重大的自然灾害或人为事故(造成至少100人死亡,或重大经济损失,或重大社会影响)。 时间 重大灾难 地点 死亡人数 描述 2002-2003年 嚴重急性呼吸系統綜合症(SARS) 全国" +718,"灾害,造成117人丧生,其中105人为学生。 2005年8月 广东大兴矿难 广东 121 直接经济损失4725万元。 2005年11月 七台河特大矿难 黑龙江 171 169名矿工、2名机房工人死亡, 事故还造成48人受伤,直接经济损失人民币4293.1万元。 2005年12月 唐山刘官屯矿难 河北 108 另有29人受伤。 2007年8月 山东华源矿难 山东 172 另有一相邻煤矿9人遇难。 2007年12月 山西新窑矿难 山西 104 另有15人受伤。 2007-2008年 金融危机 全球 对中国经济造成一定冲击。 2008年1月起 南方雪灾 全国 129 受灾人口已超过1亿,直接经济损失1516.5亿元人民币。 2008年4月 胶济列车相撞 山东 72 另有416人受伤。事故原因包括济南局调度命令管理混乱等,引起了国内外广泛关注。 2008年5月 汶川地震 四川 69227 2008年5月12日14时28分04秒,四川省汶川县发生里氏震级8.0级地震。是中华人民共和国成立以来破坏力最大的地震,也是唐山大地震后伤亡最惨重的一次。直接严重受灾地区达10万平方公里,官方报道共遇难692" +719,"平奖授予中国持不同政见者刘晓波,以表彰他长期以来以非暴力方式在中国争取基本人权。刘晓波成为第一个获得诺贝尔奖的中华人民共和国公民,他曾发起并参与起草了《零八宪章》。但他在2008年12月8日被以“涉嫌煽动颠覆国家政权罪”刑事拘留,12月9日被监视居住;2009年6月23日因涉嫌煽动颠覆国家政权罪,经中国检察机关批准逮捕;12月25日,北京市第一中级人民法院以煽动颠覆国家政权罪判处有期徒刑11年,剥夺政治权利两年。 随着中国互联网的快速发展,信息的自由传播对中共维持执政地位构成挑战。为了应对来自网络发展形成的影响,中国建立世界上最为先进的网络审查制度,审查中国大陆的互联网内容,封锁服务器位于境外的政治敏感类网站,以达到其舆论控制的目的。谷歌、雅虎等公司退出中国大陆市场。此外,中国政府开始雇佣大量网络评论员(俗称“五毛党”)来控制舆论导向。2011年,国家的“维稳”经费预算首次超越军费预算。与此同时,中国官场开始涌现大量“裸官”,许多中央高层的子女或亲属移民海外或拥有外国绿卡。 美国《大观(Parade)》杂志认为以胡锦涛总书记为首的中国共产党领导层控制国内舆论,并强行镇压被中共官方所认定" +720,"时报》则曝光其拥有巨额家族财富。 == 习近平及第五代领导时期(2012年-现今) == === 交接 === 胡錦濤、溫家寶等領導人在2012年11月的中共十八大上退出中央領導層,由習近平按现有接班体制接任中国共产党中央委员会总书记和中国共产党中央军事委员会主席,李克強留任中共中央政治局常委。习近平是第一位出生于中华人民共和国建国后的最高领导人。国务院总理李克强被认为重视改革,其谈到改革是最大的红利,提倡进行政府体制改革,调整或裁减国务院的部门。 2022年10月,中共二十届一中全会上,习近平再次连任中共中央总书记和中央军委主席,李强、蔡奇、丁薛祥等习近平亲信成为中共中央政治局常委。2023年3月,习近平史无前例连任三届中国国家主席,成为至今任期最长的国家主席;李克强以67岁之龄退休、李强接任中国国务院总理。 === 内政 === ==== 反腐运动 ==== 2012年,中共中央总书记习近平提出《中央八项规定》等一系列反腐败措施。王岐山担任中共中央纪委书记一職後,在處理貪污腐敗方面採取積極的態度,提出“当前要以治标为主,为治本赢得时间”。在接下来的两年时间中,共计有18万官员被查处" +721,"的政改,是“政治体制改革在深化”、“走出了新路”、“硕果累累”。而外界和部分体制内人士则认为习的政改路线并没有遵循邓的政治体制改革道路,且改变了改革开放以来进行的政治体制改革方向,推行个人崇拜,是政治倒退,重新回到了毛泽东时代的体制,即中国共产党最高领导人独揽大权的政治寡头统治以及“党领导一切”的模式。 经济民生方面,2015年,中国政府结束了自1979年以来实施的一胎政策,改为二胎政策,并在2021年实施更为开放的三胎政策。2017年3月,中国国务院总理李克强在召开的第十二届全国人大五次会议上的《政府工作报告》中,正式提出建设“粤港澳大湾区”的战略;与此同时,2017年4月,雄安新区成立。李克强提出要推动中国内地与香港、澳门特区的深化合作,研究和制定粤港澳大湾区发展规划,以香港和澳门的独特优势,提升中国的经济发展。粤港澳大湾区是由围绕珠江三角洲伶仃洋,包括香港、澳门、广州、深圳、珠海、佛山、中山、东莞、肇庆、惠州和江门的城市群组成,是继美国纽约都会区、美国旧金山湾区和日本东京都市圈之后,世界第四大湾区。2018年9月23日,广深港高铁香港段正式通车,香港纳入国家高铁网络;2018年1" +722,"的劳动教养政策;2019年12月,全国人大废除了自1991年以来实施的收容教育政策。2013-2014年间,针对新疆地区出现的一些重大恐怖袭击事件,譬如莎车县暴恐袭击案、乌鲁木齐公园北街早市暴力恐怖袭击案等,中国共产党开始试点建设“新疆再教育营”。2016年陈全国调任新疆自治区党委书记,此后营地规模迅速扩大,引起国际关注和争议。 ==== 修宪取消连任限制 ==== 2018年2月25日,中国共产党中央委员会向第十三届全国人民代表大会提出对邓小平时代《八二宪法》的《宪法修正案》,建议删除国家主席和副主席“连任不得超过两届”的限制。邓小平时代以来的“废除领导干部终身制”或将结束。3月11日通过《宪法修正案》,同时习近平新时代中国特色社会主义思想写入中华人民共和国宪法。 根据1982年制定的《中华人民共和国宪法》(八二宪法),恢复设立自1968年缺位,1975年被废除的国家主席和副主席职位,不过取消国家主席在五四宪法中主持召开最高国务会议和领导国防委员会的权力,失去了行政权和军权,变成礼仪性的虚位元首。根据邓小平在1980年提倡的“废除干部领导职务终身制”,重新设立的国家主席和副主席任期为" +723,"建设启动。同年,暗物质粒子探测卫星“悟空”发射成功。 * 2016年,天宫二号发射成功、取代了之前的天宫一号。同年,“500米口径球面射电望远镜”在贵州落成。 * 2020年,火星探测器“天问一号”成功发射升空,并在2021年5月成功着陆火星表面。同年7月,北斗三号正式开通。 * 2021-2022年,神舟十二号、神舟十三號、神舟十四號、神舟十五號载人飞船先後与天宫空间站成功对接,執行中國太空站在軌關鍵技術驗證及建造任務。 * 2023年,隨着神舟十六號载人飞船与天宫空间站成功对接,中國太空站從此進入長年有人駐守的常態化營運模式。 另一方面,2022年2月4日,第24屆冬季奧林匹克運動會在北京開幕;這是中國首次舉辦冬季奧運會,而北京也因而成為首座既舉辦過夏季奧運會,又舉辦過冬季奧運會的城市(即“双奥城市”或“双奥之城”)。2023年7月28日至8月8日,原定于2021年于中国四川省成都市举行后因2019冠状病毒病疫情等原因二次延期的第31届夏季世界大学生运动会举行。 ==== 港澳地区 ==== 香港主權移交中国后,许多香港人士不满意中央政府对香港选举的干涉,要求进行“真普选”,即有“" +724,"资银行等组织中国都具有实际主导力,潜在的世界性影响力已经不容忽视,而且不论主动或被动,中国经济由于其庞大的规模,全球性经济政策已经不能缺少中国的参与和配合,中国开始显露其全球影响力。 * 2013年9月和10月,中国政府分别提出建设“一带一路”(即“新丝绸之路经济带”)和“21世纪海上丝绸之路”的合作倡议。中国政府指出,“一带一路”倡议坚持共商、共建、共享的原则,努力实现沿线区域基础设施更加完善,更加安全高效,以形成更高水平的陆海空交流网络。 * 2014年11月10日至11日,第26届亚太经合组织领导人年度会议于在北京市怀柔区雁栖湖举行。该次峰会通过《亚太经合组织第二十二次领导人非正式会议宣言》(《北京纲领(宣言)》),提出并通过致力于旨在打击跨国腐败的《北京反腐败宣言》。 * 2015年,“习马会”在新加坡举行,是海峡两岸(即中国大陆与台湾)自1949年政治分立以来,双方最高领导人的首次会晤。 与此同时,习近平逐步扩大中国在全球的影响力,其主推的“大国外交”也引发了西方国家、日本、印度等国的忧虑与抵制。2018年起,新一轮的中美贸易战爆发。2020年5月起,中国和印度爆发边境冲突," +725,"社会影响)。 时间 重大灾难 地点 死亡人数 描述 2013年4月 芦山地震 四川 193 另有25人失踪、12211人受伤,19个市州115个县199万余人受灾,851.71亿元。 2014年8月 昆山中荣工厂爆炸事故 江苏 146 另有114人受伤。 2014年8月 鲁甸地震 云南 617 另有3143人受伤,108.84万人受灾,22.97万人紧急转移安置,2.58万户8.09万间房屋倒塌,4.06万户12.91万间严重损坏,15.12万户46.61万间一般损坏。 2015年5月起 特大洪水 南方 319 另有81人失踪。全年洪灾死亡人数为中华人民共和国成立以来最少,但截至7月初暴雨洪水已经直接造成了约353亿元的经济损失,强度为历年罕见。 2015年6月 东方之星号沉船事件 湖北 442 “东方之星号”客轮在湖北荆州监利县容城镇一带的长江水域遇狂风雷暴雨而翻沉。 2015年8月 仓库连串爆炸 天津 165 另有8人失踪,798人受伤,直接经济损失至少68.66亿元人民币。 2015年12月 深圳滑坡事故 广东 73 另有4人失踪,属于生产安全事故,直接经济损失人民币8.8亿余元" +726,"的一次沙尘暴。 2021年7月 河南水灾 河南 398 其中郑州市380人,逾1400万人受灾。 2022年3月 东航3·21空难 广西 132 2022年9月 泸定地震 四川 93 另有25人失联、至少423人受伤。 2023年7月-8月 京津冀暴雨 北京、天津、河北 62 另有34人失踪,经济损失超1000亿元。 2023年12月18日 积石山县地震 甘肃 151 另有980人受伤。 === 争议 === 习近平上台后,推翻了自邓小平拨乱反正、改革开放以来的部分政策。2017年,习近平提出“四个伟大”,有学者认为此举是对毛泽东时期的“四个伟大”的效仿。而近年来,从修改宪法到反对“宪政”、“司法独立”,习近平的许多言论和举措招致了国内外的诸多反对声音,部分学者对他的一系列作法感到忧虑、震惊,有人认为这是对毛泽东时代的复辟,推行个人崇拜、煽动民族情绪、背离宪政主义,是体制性倒退,还有人认为这甚至有可能将中国推向第二次文化大革命。此外,中国709维权律师大抓捕事件、陕西千亿矿权案、新疆再教育营、香港反修例運動、战狼外交均引起了巨大争议或批评。 * 极权统治:习近平被普遍认为是中华人民共和" +727,"义务。此外,中国政府还将监控技术和设备输出到了俄罗斯、非洲、南美洲等地。 2018年以来,中共中央政法委员会进一步提出要建立正规的“政法网评大军”。军费预算连年增加且存在不透明,而国内的“维稳”经费多年超过军费支出。 * 香港问题:2019年香港反修例运动期间,Twitter、Facebook封禁和删除了大量“具有中国官方背景、用于煽动舆论”的账号,其中Twitter声称处理了约20万个相关帐号。期间中国中央电视台声称1984年签署的《中英联合声明》已经“过时无效”,而随后中国外交部以“香港事务纯属中国内政”应对外界质疑,引发了巨大争议;此外在2020年6月30日颁布的《香港国安法》同样引发了较大争议。 * 抗疫抗灾:2019冠状病毒病疫情发生后,习近平一直强调自己“亲自指挥、亲自部署”防疫工作,甚至早在2020年1月7日已经在中央政治局常委会上提出防疫要求。不过有消息指出中央领导人初期对疫情并不重视,甚至要求不要造成民众恐慌,影响即将到来的春节气氛。外界亦有对中国大陆官方公布的死亡人数及染病人数的真实性质疑。2020年5月下旬起,儘管长江中下游地区、淮河流域、西南、华南及东南沿海等地" +728,"國共產黨、中共黨史 * 中共對歷史的修改 * 土地改革运动、镇压反革命运动、抗美援朝 * 大躍進、三年大饥荒 * 文化大革命、四个现代化、粉碎四人幫 * 改革開放、六四天安门事件 * 中国共产党集体领导制度、胡溫體制、習李體制 * 中國歷史、中國政治史 * 香港歷史、澳門歷史、台灣歷史 * 对毛泽东的评价、对周恩来的评价、对江泽民的评价、对习近平的争议 * 毛泽东崇拜、习近平的个人崇拜、习近平吃包子事件 * 共产主义政权下的大屠杀、中国大屠杀列表 == 参考资料 == == 延伸阅读 == * 黄修荣 主编:《中国二十世纪全史》,中国青年出版社,2001年,ISBN 978-7-5006-4261-9 * 《中华人民共和国史(1949-1981)》,十卷,香港中文大学出版社,2008年,ISBN 978-988-17274 * 《剑桥中华人民共和国史(1949-1982)》 * 《中华人民共和国实录》,吉林人民出版社,1994年,ISBN 978-7-206-02101-5 * 薄一波:《若干重大决策与事件的回顾》,中共中央党校出版社,1994年,ISBN 978-7-5035-043" +729,"中華民國是位於東亞的民主共和國,曾在國際上廣泛代表「中國」,現今多通稱為「臺灣」;目前有效管轄範圍包括臺灣、澎湖群島及其附屬島嶼,以及中國大陸福建沿岸的金門群島、馬祖列島等島嶼,多合稱為「臺澎金馬」或「臺灣地區」;土地面積共36,197平方公里,其中臺灣及其附屬島嶼占99%以上。目前以位居東亞島弧的臺灣本島爲主要領土,東臨太平洋、西隔臺灣海峽、南界巴士海峽、北瀕東海,其地形陡峭、景觀多樣。國土約三分之二的面積為山地和丘陵地形,大部分人口則居住於臺灣西部的平地。全國人口約2,300萬人,人口密度在全世界人口大於1,000萬人的國家中位列第二。首都為臺北市,人口最多的城市則為新北市。 1911年10月10日,辛亥革命爆發,革命黨人於1912年1月1日在南京宣告建立中華民國,並在數月後從清廷手中正式繼承中國政權。北洋政府時期,政治上陷入長期南北分裂、軍閥混戰,社會上則持續走向自由氛圍。1928年,中國國民黨領導的國民政府統一中國,實施訓政,但此前有合作關係的中國共產黨與中國國民黨決裂,建立割據政權。1937年,中國抗日戰爭全面爆發,之後成為第二次世界大戰的主要戰場之一,期間國共短暫再次合作。" +730,"漢族及新住民等族群組成,兼容南島、中華、日本、西方及全球各地文化,同時在視覺藝術、文學出版、表演藝術、電影及戲劇等領域均有所發展。 中華民國曾是聯合國會員國與安全理事會常任理事國,並參與創立聯合國,但在1971年中華人民共和國取得聯合國「中國」席次後逐漸失去國際普遍承認。中華人民共和國建國後主張已繼承中華民國,並進一步認定擁有臺灣主權,常被認為是中華民國面臨的最大軍事威脅,但也是後者最大的經濟貿易夥伴。目前中華民國受限於「一個中國」政策僅有12個邦交國,但與美國、日本等多個非邦交國發展夥伴關係,同時也是數個政府間國際組織及多邊機制的成員。中華民國實行市場經濟制度,產業主要包括製造業、服務業及貿易業等,是世界半導體、IT產業的重要領導者,在全球經濟占有重要地位,同時與國際社會之間有着極高的經貿依存度。 == 國名及象徵 == 「中華民國」一稱最早是由孫中山於1905年在日本東京召開的中國同盟會成立大會上所提出。1912年1月1日,各省都督府代表聯合會宣布建立民主共和制國家,使用「中華民國」作為國號。延續中國改朝換代的慣例,民國紀年以1912年為元年。1949年後,中華民國政府遷往臺灣,主" +731,"寒表現堅貞剛潔,三蕾五瓣代表三民主義及五權憲法。中華民國國家象徵還有「中華民國之璽」與「榮典之璽」兩枚中華民國國璽。 == 發展歷程 == 1912年,中華民國在中國大陸宣告成立,並經歷北洋政府、國民政府等時期。同時期,臺灣、澎湖群島接受日本殖民統治(1895年至1945年),更早以前則歷經荷西(1624年至1662年)、明鄭(1661年至1683年)和滿清(1683年至1895年)的統治。1945年,中華民國政府接管臺灣,臺灣的日本政經體制與中國大陸體制接軌。1949年,中國共產黨建立中華人民共和國,中華民國政府僅統轄臺灣地區。1970年代後,中華民國無法在國際上繼續代表「中國」、及民主多元社會成形,臺灣地方意識、本土文化備受重視,原住民與客家族群亦爭取族群地位,出現新的國族觀念與文化認同。而官方參與的民國史修撰,敘事主體也由大中國的國土框架轉為在臺灣的中華民國。 === 民國肇建 === 1840年第一次鴉片戰爭後,清朝外交及戰役失利,中國淪為次殖民地。隨著清朝改革運動和立憲運動失敗,孫中山等人組成中國同盟會。1911年10月10日,革命軍武昌起義,各省宣告獨立。同年12月,各省都" +732,"國分裂政局宣告統一。其後,中國國民黨為首的國民政府實施以黨治國的訓政,國家建設得到發展。同時期內部有地方軍閥與中國共產黨勢力割據,外部有大日本帝國威脅。1927年起,中國共產黨在多地建立革命根據地。1931年,日本發動九一八事變,成立傀儡政權滿洲國。 1937年,隨著日本在盧溝橋發動七七事變,中國抗日戰爭全面爆發,日本軍在戰爭初期攻下南京市,國民政府遷往重慶市,雙方陷入僵持;其後中華民國與美國、英國、蘇聯結為同盟國,最終在1945年獲得勝利。在日本投降後,國民政府還都南京市,中國共产黨則進駐日本控制區,雙方展開政治協商會議。1946年,制憲國民大會通過《中華民國憲法》。1947年,《中華民國憲法》施行,召開第一屆國民大會。1948年,國民大會選舉蔣中正為行憲後首任總統,同年頒布《動員戡亂時期臨時條款》。在這期間,中華民國國軍與中國共產黨爆發全面內戰。 === 臺灣歷史 === 至少在10,000年前,臺灣原住民族的祖先陸續前來臺灣,成為臺灣最早的居民。在12世紀後,中國和日本商人、漁民和海盜在臺灣活動,從事非法貿易。1624年,因明朝軍隊驅逐,荷蘭人自澎湖轉往安平建立據點。1626年," +733,"地方。1920年代,在臺灣的軍事統治一度轉為文人政治,並試圖同化臺灣居民。臺灣知識分子發起地方自治運動,並發生霧社事件。在太平洋戰爭期間,日本軍國主義下的臺灣成為侵略東南亞的軍事基地,並因而發展重工業。同時還推動皇民化運動,許多臺灣人加入日本軍,但各地也遭到美國轟炸。至1945年,在日本投降後,中華民國政府接管臺灣。 === 在臺發展 === 1947年,因為臺灣省行政長官公署施政錯誤等因素,臺灣爆發大規模抗爭,爾後國民政府派遣軍隊鎮壓,造成臺灣省籍矛盾擴大。但隨著第二次國共內戰不斷失利、通貨膨脹持續失控,中華民國政府逐漸喪失中國大陸的控制權。1949年初,下野的蔣中正逐步將軍隊調往臺灣,而臺灣地區亦宣告戒嚴。同年10月,中國共產黨成立中華人民共和國。同年12月,中國國民黨掌控的中華民國政府遷往臺北市,另有200萬人遷往臺灣。在韓戰後,中華民國獲得美國軍事支持及援助;日本則與同盟國及中華民國分別簽署《舊金山和約》、《中日和約》,正式放棄臺灣的主權。 其後,中華民國政府持續主張代表「中國」並計劃反攻大陸,同時在臺灣建立威權主義的黨國體制,控制媒體出版、禁止成立政黨,造成白色恐怖;而其黨國" +734,"脅,臺灣爆發太陽花學運。2015年,馬英九和習近平在新加坡舉行兩岸領導人會面。2016年,民主進步黨籍蔡英文當選首位女性總統,並在2020年連任總統,民主進步黨在2016年首次取得立法院多數席次,2020年仍持續持有立法院多數席次。2024年,民主進步黨籍賴清德當選總統,使得民主進步黨首次取得第三個連續執政任期,但未獲得立法院多數席次而成為少數派政府。2024年5月,立法院改革法案爭議導致社會運動以及大規模抗議。 == 地理環境 == 中華民國轄有臺灣及其附屬島嶼、澎湖群島、金門群島、馬祖列島、烏坵列嶼、東沙群島、南沙群島之太平島與中洲礁等地。有效管轄土地面積36,197.067平方公里,在世界各國及自治地區中排行第136名。臺灣及其附屬島嶼面積35,886.8623平方公里,占中華民國管轄土地面積99%以上。臺灣是位於太平洋西岸東亞島弧間、亞洲大陸東南邊緣的海島,四面環海;其北方和東北方瀕東海,東北方與日本琉球群島接壤,東方與墨西哥相隔太平洋,南方與菲律賓相隔巴士海峽,西方與中國東南地區相隔臺灣海峽。經度約在東經120度至東經121度,緯度約在北緯21度至北緯26度之間。 臺灣大致呈" +735,"並有沼澤和沙洲等地形),南部和離島海岸則有珊瑚礁群。許多主要港口設在西部海岸線,僅有東北部蘇澳港和北部基隆港例外。在島嶼部分,臺灣擁有21個附屬島嶼,較重要者有龜山島、綠島、蘭嶼、琉球嶼等;澎湖群島位於臺灣海峽,共計64個島嶼;金門群島位於中華人民共和國福建省東南海岸外,共計12個島嶼;馬祖列島位於臺灣海峽西北西方,共計36個島嶼;東沙群島、中沙群島和南沙群島位於南海;釣魚臺列嶼位於東海。 === 地質 === 臺灣位居歐亞大陸與太平洋海盆的東亞島弧中央位置,是由歐亞大陸板塊與菲律賓海板塊撞擊形成的新生代褶皺山脈。東亞島弧位在東亞至東南亞沿海,北起阿拉斯加阿留申群島,在行經日本、菲律賓後,南至印度尼西亞島嶼;臺灣位於日本和菲律賓之間,但形成時間與琉球群島和菲律賓不同。大約在中生代末期的南澳運動中,臺灣因為造山運動浮出水面成為一個小島,但不久深埋海底。直到大約200萬年前,臺灣東南方向的板塊在花東縱谷一帶撞上歐亞大陸板塊,強大的壓力再度把海底的沈積物抬升到海面,形成北—東北到南—西南走向的地勢。 其後的褶皺運動、斷層運動使臺灣逐漸上升成為穩固的島嶼,且上升運動不斷進行;自然環境的風化、雨" +736,"串聯許多河流。長度100公里河流共有8條,濁水溪是長度最長的河流,高屏溪具有最大流域面積,而淡水河是唯一具有航運功能的主要河流。因為降雨季節分配不均,河流流量變化很大:夏季遭遇暴雨高漲、迅速流入大海,冬季枯水期則只剩乾溪。天然湖泊及人工湖泊共55個,最大的天然湖泊是日月潭,而曾文水庫及翡翠水庫是最大的兩個人工湖泊。目前主要河川、水庫均有執行管制與整治措施。 === 氣候 === 臺灣氣候帶廣泛,南部為熱帶氣候、北部為亞熱帶氣候、山區為溫帶氣候,北回歸線貫穿嘉義縣、嘉義市及花蓮縣。臺灣四季溫度變化明顯,每月平均溫度約在攝氏15度至30度之間;而東部黑潮暖流調節臺灣溫度,海洋氣流更造就溫潤的天氣。夏季至秋季漫長炎熱,氣溫可達攝氏35度以上。冬季短暫溫暖,偶有寒流南下,最低可達攝氏10度以下,高海拔山區常見飄雪。臺灣溫室氣體排放占全球約0.56%。因應《聯合國氣候變遷綱要公約》和《巴黎協定》,中華民國長期溫室氣體排放減量,並宣示將「2050年淨零轉型」納入目標。 臺灣全年降雨豐沛,年降雨量約2,200毫米(東部和山區降雨量更多),春雨、梅雨及颱風為主要降雨型態。降雨分布取決於季節性季風:夏季" +737,"種裸子植物。其中約30%為特有種與特有亞種(包括45種哺乳動物、85種鳥類),其它則與中國大陸物種類似。最大的哺乳動物是臺灣黑熊,並有鹿和野豬出沒,各地候鳥群則在淤灘及紅樹林棲息。離島動植物種類則較少,且缺乏樹木。作為世界保育的重地之一,中華民國持續推動自然資源與環境生態維護,並促進生物多樣性、野生動物及濕地保育、教育推廣工作,並公告臺灣黑熊等保育物種。 為了維護自然景觀與保育動植物生態,臺灣地區還設置國家公園、國家自然公園、自然保留區、野生動物保護區、野生動物重要棲息環境、自然保護區,各類型自然生態保護區共98處,約占土地面積19.18%。其中9座國家公園包含許多特殊地形景觀,分別是墾丁國家公園(第一座國家公園)、玉山國家公園、陽明山國家公園、太魯閣國家公園、雪霸國家公園、金門國家公園、東沙環礁國家公園(第一座保育海洋環境的國家公園)、臺江國家公園、澎湖南方四島國家公園,另有首座國家自然公園壽山國家自然公園,累計面積750,479.43公頃。 == 政治制度 == 中華民國是半總統制國家,主權屬於國民全體。《中華民國憲法》針對行政機關、立法機關、司法機關三權不足,增加考試及監察兩權。" +738,"監理委員會;其由考試委員組成,設置考試院院長、副院長各1人。監察院為為國家最高監察機關,設有審計部與國家人權委員會;其由監察委員組成,設置監察院院長、副院長各1人。司法院正副院長、司法院大法官、考試院正副院長、考試委員、監察院正副院長、監察委員由總統提名,經立法院同意後任命。 中華民國規定公費補助選舉、投(開)票所監察員等制度,並推動提高透明度、防範貪汙、電子政務等政策。公職人員選舉皆為直接選舉,年滿20歲選民便依戶籍取得投票權,並有登記參選權利與女性保障名額。隨著民主選舉成為重要政治活動,傳播媒體、輿論調查、民間團體和抗議活動越趨重要。各級政府主要歲入為稅課收入、營業盈餘及事業收入、規費、罰款及賠償收入,主要支出為社會福利、教育科學文化、經濟發展、一般政務及國防。2022年度各級1年以上公共債務未償餘額為63,994億元,占前3年度名目國內生產毛額平均數37.70%。 目前人民團體分成職業團體、社會團體與政治團體,其中民主進步黨和中國國民黨是兩大政黨,主要政治議題有臺灣統獨問題、海峽兩岸關係、經濟發展等。民主進步黨經常被描述為主張獨立的左派政黨,而中國國民黨是反對獨立的中間偏右政黨。" +739,"及縣(市)議會,鄉(鎮、縣轄市、直轄市山地原住民區)設立鄉(鎮、縣轄市、直轄市山地原住民區)民代表會,地方民意代表行使立法權和質詢權。地方行政首長和地方民意代表由市民普遍選舉產生,任期4年。直轄市、縣和市選舉往往被視為重要的民主選舉,且地方政府之間彼此競合。目前中華民國首都為臺北市。 區劃單位 行政區劃 政府所在地 面積(平方公里) 人口(人) 工商業生產總額(百萬元,2021年) 直轄市 臺北市 信義區 8,901,789 直轄市 新北市 板橋區 4,739,838 市 基隆市 中正區 416,379 縣 宜蘭縣 宜蘭市 330,205 直轄市 桃園市 桃園區 4,664,371 縣 新竹縣 竹北市 1,928,830 市 新竹市 北區 2,218,562 縣 苗栗縣 苗栗市 819,744 直轄市 臺中市 西屯區 4,446,290 縣 彰化縣 彰化市 1,365,337 縣 南投縣 南投市 344,169 縣 雲林縣 斗六市 1,495,572 縣 嘉義縣 太保市 373,678 市 嘉義市 東區 205,897 直轄市 臺南市 安平區 3,247,644 直轄市 高雄市 苓雅區" +740,"戒法院掌理公務員、法官及檢察官懲戒。法官為終身職,經由遴選或考試選拔、並在法務部司法官學院受訓產生;法官必須依法獨立審判,不稱職者可以免職。 目前法律體系採,繼受歐陸法系國家制定《六法全書》。地方法院和高等法院為事實審,最高法院為法律審。刑事訴訟系統從過去法官兼掌的「糾問主義」,改為檢察官、被告及法院組成的「彈劾主義」,並採國家追訴與私人追訴雙軌制。不過司法體系運作仍遭批評,在2023年新增國民法官制度。地方法院行政訴訟庭為事實審,高等行政法院為事實審與法律審,最高行政法院則是法律審。內政部警政署負責、秩序維護、保安警備等警政工作,使臺灣成為。海洋委員會海巡署平時是海域執法機構,戰時具有國防任務。 === 外交關係 === 目前中華民國有12個邦交國,並在數十個非邦交國城市設有駐外機構,多方面建立持久關係。中華民國政府配合邦交國執行互助互惠的合作計劃,並與理念相近國家建立夥伴關係,共同維護區域安全及和平發展。同時外交政策秉持「踏實外交」原則拓展國際空間,除了在亞太區域扮演重要角色,也協調相關部門參與國際組織。中華民國政府還協助國內非政府組織參與國際活動、推動人道援助和衛生合作,並回應氣" +741,"發展基金會、青年世代交流等計劃,並鼓勵國際非政府組織設置據點。 中華人民共和國宣稱擁有臺灣的主權,強迫中華民國接受單方面設定的政治統一框架,不斷進行政治打壓及軍事脅迫。不過雙方持續維持經貿、人員、文教與社會交流,開放並執行多項協議措施,中國大陸也是臺灣最大的貿易夥伴、出口市場及對外投資對象。除此之外,臺灣也與香港、澳門發展關係,並推動僑務工作。目前中華民國與汶萊、中華人民共和國、馬來西亞、菲律賓、越南存在南海諸島爭議,並與日本、中華人民共和國存在釣魚臺列嶼主權問題。中華民國政府支持多邊體系解決領土爭端,提出和平協商處理東海、南海分歧。 === 國防軍事 === 臺灣位處東亞島鏈中心,是鄰近臺灣海峽與呂宋海峽的戰略要地。中華人民共和國至今強調不承諾放棄使用武力侵犯臺灣,持續對臺灣展開軍事演習、侵擾西南空域、間諜情報活動,使其成為中華民國唯一的軍事威脅。中華民國國軍包括陸軍、海軍、空軍,並有憲兵、陸軍後勤、後備、防空飛彈、資通電軍等指揮部。在指揮架構部分,總統為三軍統帥,行使統帥權指揮軍隊;其直接責成國防部部長,由部長命令國防部參謀本部參謀總長執行。國防部主管整體國防事務,依法提交相關報告" +742,"建設 == 臺灣經濟屬於資本主義市場經濟,主要由製造業、國際貿易和服務業推動,而大量外匯儲備、勞動生產率增加、產業私有化、政府經濟規劃、亦促成經濟成長。在2021年,名目國內生產毛額7,750億美元,人均名目國內生產毛額33,011美元,人均購買力平價59,398美元。而中央銀行藉由貨幣與外匯政策穩定新臺幣和外匯流通,持有世界排行前幾名的外匯儲備。這些讓臺灣在全球經濟中占有重要地位,與國際社會有極高依存度,並在許多調查位居世界前列,如2022年經濟自由指數第6名、《世界競爭力年鑑》第7名、投資環境風險評比第3名。 中華民國積極在亞太經濟合作會議與世界貿易組織推動區域和全球經濟一體化,與巴拿馬、瓜地馬拉、尼加拉瓜、薩爾瓦多、宏都拉斯、紐西蘭、新加坡、巴拉圭、史瓦帝尼、貝里斯等國簽署自由貿易協定或經濟合作協定。同時強化貿易合作和產業形象、規劃自由經濟示範區、吸引臺灣商人、中國大陸資金及其他外商投資、推動加入《跨太平洋夥伴全面進步協定》和新南向政策。臺灣證券交易所是世界最大的證券交易所之一,上市公司總家數為971家,總資本額約74,999億元。不過臺灣產業主要基於,且大多是集中在北部的家族企" +743,"低收入及中低收入戶約58.1萬人。 === 第一產業 === 2022年,中華民國第一產業占名目國內生產毛額(生產面)1.41%。農業就業人口占4.6%。臺灣氣候溫暖、雨水豐沛,適合作物生長。過去臺灣藉由土地改革、技術引進促成農業發展,但近期因加入世界貿易組織受到衝擊。目前農耕地約78.7萬頃(占土地面積22.7%),但農戶平均耕地0.72公頃,屬小農兼業經營,生產成本高且不具競爭力。稻米是最具價值的主食,主要蔬果有竹筍、捲心菜、西瓜、香菇、綠葉蔬菜和蔥。蔬果、茶葉和為重要出口產品,並出現數百個休閒農場。畜牧業奶製品與肉業經濟規模較小,豬肉與家禽比牛肉更歡迎。 過去、和漁業為重要經濟部門,曾大量開採黃金、硫磺、大理石等資源,但現今重要性下降。採礦業與林業由於資源枯竭及管理限制,僅占經濟總產值一小部分,但木材與竹材產量持續增長。由於資源狀況與影響,漁業由沿海漁業轉為遠洋漁業與,主要出口秋刀魚、石斑魚、吳郭魚等;臺灣南部則有觀賞魚養殖場。目前中華民國政府推動建立產品安全、糧食安全、防疫檢疫等機制,並改善農產品運銷、農作物出口、水利建設、農漁民團體、農漁村環境,以及發展農業創新知識和高經濟價" +744,"。 === 第三產業 === 2022年,中華民國第三產業占名目國內生產毛額(生產面)60.78%。服務業勞動人口占60%,是成長速度最快的產業類別,並朝向精緻化與專業化發展。主要服務業有批發和零售貿易、政府服務、金融和保險、房地產(包括出租和租賃)、運輸(包括倉儲)、醫療保健和社會服務、高科技服務、住宿(包括飲食)、教育和文化(包括體育和娛樂)。在商業部門方面,中華民國政府推動電子商務業、國際物流服務、餐飲科技、商業服務設計暨廣告服務業、連鎖加盟業、智慧商業服務加速升級與數位轉型,並強化冷鏈物流、國家標準檢驗、智慧財產權等基礎環境。 中華民國⾦融機構分成、信用合作社、農會、漁會信用部、票券⾦融公司、證券⾦融公司及⾦融控股公司。其中銀行機構持續拓展據點,保險業經歷快速成長有相當高覆蓋率,證券業和票券業機構則趨於穩定。中華民國政府相當看重自己的文化特色,陸續規劃臺灣世界遺產潛力點、國家公園、國家森林遊樂區、林業文化園區、國家綠道、國家級風景特定區等。在2022年,前來臺灣的旅客共895,962人次,主要來自中國大陸、日本、香港、美國和東南亞;出國國民共1,482,821人次,主要前往中國" +745,"另一方面,中央研究院是國家學術研究最高機構,招攬自然科學與社會科學頂尖研究人員。中華民國在文學研究、經學、歷史學、考古學、人類學、語言學、宗教學、政治學、法學、經濟學、社會學、心理學、教育學、圖檔學、傳播學、體育學、數學、天文學、物理學、化學、地球科學、生物學、基礎醫學、臨床醫學等學術領域均有研究成果,並朝向全球化、在地化、多元化方向發展。目前臺灣在科技競爭排名領先,如在2019年的《全球競爭力報告》創新能力部分排行第4名,在2021年《世界競爭力年鑑》技術基礎設施和科學基礎設施部份分別排行第9名與第6名。 == 公共建設 == === 交通運輸 === 臺灣四面環海,位處印度洋與太平洋交通樞紐。目前擁有7個國際商港和4個國內商港,前四大港口為高雄港、基隆港、臺中港及臺北港。主要是民營企業(如長榮海運、陽明海運等),擁有大量商船,2022年國際貨櫃裝卸量1,469萬20呎標準貨櫃。臺灣還擁有35座機場,包括桃園國際機場(全球第四繁忙機場)、臺北松山機場、高雄國際機場及臺中清泉崗機場4座國際機場。目前計有中華航空(國家航空公司)、長榮航空、臺灣虎航、星宇航空等8家航空公司,各主要城市間班" +746,"印度尼西亞等13個國家採購。 早期臺灣發電廠採用水力發電和燃煤發電,其後則仰賴火力發電,並在1970年引入核能發電。在日本福島核電廠事故後,反對核能者增加,核能發電的能源占比亦下降。再生能源占一小部分比例,以太陽能、風力和生質能發電為主,水力和地熱發電相對次要。在電力需求增加、發電機組屆齡下,臺灣電力公司推動增加燃氣機組、天然氣接收站等計劃。同時中華民國政府也推動能源轉型,輔導提升能院效率和潔淨能源,特別是太陽光電與風力發電。截至2022年,再生能源裝置容量累計14,128.6千瓩。目前臺灣家用電源電壓為110V或220V,頻率為60Hz。 === 其他建設 === 中華民國的郵政事業由國營的中華郵政公司專營,並推動數位轉型。在2023年,中華民國計有114家電信業者經營語音、數據等服務,市內電話用戶1,028萬4,961戶,寬頻網路帳號3,723萬戶(709萬戶固網寬頻、2,836萬戶行動寬頻)。隨著中華民國政府鼓勵發展及市場自由化,固網、行動網絡和光纖服務相當普及。目前行動電話數量已經超過固定電話,包括最大電信業者中華電信在內的電信公司均提供服務。中華民國政府持續推動兆位元寬頻聯網" +747,"估計51%女性已婚。隨著都市化提升與社會變遷,傳統家庭結構持續縮小且日趨多元,單親、隔代、新住民、近貧等家庭持續增加。 同時期,中華民國人口年齡中位數為43.8歲。國民0歲平均餘命逐年波動遞增,在2021年0歲平均餘命合計為80.86歲。隨著死亡率降低和預期壽命增加,65歲以上老年人口的比率已經快速上升,在2022年底占總人口17.56%(扶養比42),屬於高齡化社會。預計至2025年老人人口將超過總人口20%,成為超高齡社會。因應高齡化社會趨勢及其對國家發展的影響,中華民國政府與民間推展各項老人福利服務,同時推動兒童及少年福利、婦女福利、身心障礙者福利、專業保護網絡等社會福利服務,建構社會救助與社會保障等社會安全制度。 目前中華民國人口密度每平方公里643人,為全世界1,000萬以上人口國家第二位,僅次於孟加拉。其中99.7%人口居住在臺灣,並隨著產業發展往西部和北部都會區聚集,導致都市化快速(80.1%為都市人口),超過100萬人的直轄市更占人口69.43%。人口前三名直轄市為新北市(近400萬人,占17.17%)、臺中市(281萬人,占12.10%)和高雄市(273萬人,占11." +748,"8萬人,並有逾28萬人遷居都市。阿美族、排灣族與泰雅族三大族群占原住民族人口70%,邵族和拉阿魯哇族僅有數百人。隨著不同族群相互通婚、文化同化、社會變遷和都市化,族群差異逐漸趨同,但仍影響選舉政治。 自1990年代,隨著婚姻移民定居及開放引進移工,居住在臺灣的外國人人數增加,人口組成日趨多元,目前已設戶籍的移民人口占人口1.1%。臺灣新住民以來自中國大陸與港澳地區最多,其次為越南、印度尼西亞、菲律賓,共計57.79萬人。因應經濟發展需要,中華民國自1989年首度開放引進特定工作的國際勞動人口,並對移工及仲介機構進行管理,目前移工人數已經有71萬餘人。不過臺灣淨遷移率估計為每1,000人僅0.85名移民,在世界排行第68名。相反地,許多國民移民至美國、澳洲、紐西蘭等國家,或前往中國大陸定居。 === 語言文字 === 目前臺灣使用的聽覺語言相當多樣,分屬漢藏語系中的漢語、和南島語系的原住民族語兩大類。臺灣漢語系統包括國語(華語)、臺灣臺語、臺灣客語、馬祖語等,依族群及區域還有不同方言和腔調,且彼此並不互通。臺灣原住民族至少使用42個語言別,同樣彼此不互通;目前撒奇萊雅語、噶瑪蘭語、邵語、" +749,"法語、德語、西班牙語、韓語、俄語、義大利語、越南語等第二外語。為了提升全球競爭力,中華民國政府推動2030雙語政策。 === 宗教信仰 === 《中華民國憲法》規定人民有,在法律上一律平等,各種宗教在此基礎上蓬勃發展,成為世界上宗教最寬容的地方之一。根據2019年的調查,民眾主要信仰傳統民俗宗教(49.3%)、佛教(14%)、道教(12.4%)、基督新教(5.5%)、一貫道(2.1%)和天主教(1.3%),其他宗教還包括伊斯蘭教、東正教、三一教、理教、道院(世界紅卍字會)、天德教、耶穌基督後期聖徒教會(摩門教)、天理教、、統一教、山達基、崇教真光、天道教、天帝教、彌勒大道等。另外,有80%民眾同時信奉不同宗教,13.2%民眾則無宗教信仰。 過去臺灣原住民族多信仰泛靈信仰,中國移民引入佛教、道教和儒教,荷蘭人和西班牙人傳入基督新教與天主教,日本則導入神道教。在1949年,許多儒教、佛教、道教、民間教派等團體從中國大陸逃往臺灣,彼此競爭消長。臺灣登記在案的宗教團體可分為財團法人(基金會)、或社會團體,不同的宗教還辦理各式慈善公益、教育教化、醫療服務、社會福利等機構(如慈濟基金會是全球最大的" +750,"原則,中華民國政府提供部分弱勢族群保險費補助。全民健康保險提供所有必要且完整的診療服務,被保險人可以自由選擇特約醫院、診所等機構。 在2021年,中華民國國民0歲平均餘命80.86歲,男性77.67歲、女性84.25歲;新生兒死亡率每1,000活產2.7人,嬰兒死亡率每1,000活產4.1人。十大死因以慢性疾病為主(占76.6%),依序為惡性腫瘤(28.0%)、心臟疾病(11.9%)、肺炎(7.4%)、腦血管疾病(6.6%)、糖尿病(6.2%)、高血壓性疾病(4.3%)、事故傷害(3.7%)、慢性下呼吸道疾病(3.4%)、腎炎、臟病症候群與腎病變(3.0%)及慢性肝病與肝硬化(2.2%);前三大癌症是肺癌、肝癌和結腸直腸癌。成人過重及肥胖盛行率在2016年至2019年47.97%,18歲以上吸菸率在2020年13.1%。 由於人口老化快速,全民健康保險的醫療支出持續增加。2021年,醫療保健支出14,265億元,占國內生產毛額6.6%;平均每人花費60,783元,不過全民健康保險制度也引發爭論。中華民國政府建構嚴重特殊傳染性肺炎疫情、蟲媒傳染病、腸病毒、結核病、愛滋病、肝炎、猴痘、流感" +751,"校。臺灣頂尖的大專院校多是國立大學,其中國立臺灣大學被視為是最好的大學,其前身臺北帝國大學是臺灣第一所研究型高等教育機構。技職教育對接產業所需人才,具中等至高等教育體系,並注重縱向銜接與橫向轉軌。 臺灣傳統社會的階層頂端是受過教育的精英,而教育政策和產業政策的發展便能促成社會流動。由於對高等教育的重視,大專院校以上學歷人口已經超過高級中學程度者。許多學生前往美國、澳洲、英國、日本等地留學,大專校院亦招收與展開國際交流。臺灣在國際科學奧林匹亞生物、 化學、地球科學、數學、資訊和物理項目具有一定表現。中華民國政府還培育藝術教育、特殊教育、國家語言及雙語教學師資,並規劃原住民族及新住民教育、終身教育、家庭教育、高齡教育、數位教育等政策。至2021年,識字率為99.1%。 == 文化藝術 == 臺灣是開放且包容的移民社會,具有多民族、多語言的文化樣貌。《中華民國憲法》明定保障自由權利、多元文化為基本國策,並應建設平等、均富的國家。這導致其核心價值除了民主、自由、人權、法治、平等意識外,亦推動全民健康保險、社會福利、多元語言文化、同性婚姻等政策。不過傳統社會仍以漢文化為主,屬父權制、父系制和階級" +752,"祖靈祭、豐年祭與收穫祭、狩(漁)獵祭及農耕祭等傳統節慶,而客家族群亦會舉辦客家桐花祭、義民祭、龍等活動。 === 視覺藝術 === 臺灣原住民族有各具特色的織繡、雕刻工藝。清治時期文人推展傳統書法和水墨畫,民間有木雕、石雕、漆藝、陶藝、藺草編、竹工藝、纖維工藝、金屬工藝、紙藝等工藝。日治時期引進西洋畫及東洋畫觀念,地方題材成為特色。中華民國政府在臺灣推動中華文化復興運動,並因西方思潮與藝術運動衝擊,出現中國與西方、現代與傳統調和探索。其後的鄉土運動影響水墨等創作。1980年代起,出現裝置藝術、行為藝術等作品,關注政治、性別和族群等議題,並籌辦大型展覽。2000年後,臺灣藝術融入在地性與全球化、美術與科技的思考。 目前雕塑藝術因為傳統宗教和當代主題而盛行,版畫藝術有多元表現,攝影藝術則歷經日治時期人像攝影、戰後沙龍攝影、鄉土寫實攝影演進。工商設計與文化設計蓬勃發展,並結合當代工藝,逐漸發展出文化創意產業;而衣飾則已經西化。臺灣建築概分為臺灣原住民族建築、中式傳統建築、西洋式與中西混合型建築及戰後現代主義建築,都市地區普遍是公寓住宅。目前建築呈現多元面向的發展,其中臺北101在2004年至" +753,"翻譯大量外國著作,中文書籍亦在其他華文地區銷售。但整體紙本圖書市場持續衰退,線上書店與數位出版漸趨活絡。 === 表演藝術 === 臺灣原住民族音樂分為歌舞音樂與器樂。漢民族音樂分為福佬與客家兩系,並有民俗音樂、戲曲音樂、器樂與宗教音樂類別,教會則引進西洋音樂。臺灣古典音樂及西式音樂在日治時期開始普及,後來出現「中西合璧」、「和漢交融」、現代主義及民族主義等風格音樂,不同西式音樂團體和國樂團體也相繼成立。在歷經白色恐怖時期後,臺灣相繼出現校園民歌、華語、臺語及客語流行音樂等通俗音樂風潮。目前臺灣流行音樂受到世界各國文化的影響,並創造許多華語歌手及經典歌曲。每年會舉辦理金曲獎、金音創作獎、臺灣原創流行音樂大獎等獎項。 過去傳統戲曲有南管戲、北管戲、高甲戲、四平戲、客家大戲、歌仔戲、京劇、豫劇、車鼓戲、牛犁陣、傀儡戲、皮影戲、布袋戲等,並歷經大量轉型與現代化。現代戲劇最早有日治時期的臺灣新劇運動,其後歷經反共抗俄劇,再受西方當代劇場藝術與國際交流影響。目前現代化商業劇場多元成長,亦出現探索藝術形式的前衛小劇場。舞蹈藝術受不同文化傳統影響,引入芭蕾舞、現代舞、民族舞蹈等,並發展出高等舞蹈教育" +754,"= 傳播媒體 === 中華民國媒體相當多元,且競爭激烈。2021年,彩色電視機普及率98.5%,有線電視頻道85.6%。其中有5家無線電視臺事業,播出23個數位節目頻道。臺灣公共廣播電視集團為公共媒體,辦理客家電視臺及原住民族電視臺。有線電視廣播業者有64家,提供80個至110個基本頻道節目,為最大收視平臺。目前有線電視業者多-{zh-hans:通过;zh-hant:透過;}-衛星頻道供應節目,共計91家、218個衛星頻道。中華民國政府輔導業者開發電視劇內容,辦理金鐘獎、金視獎頒獎等活動,並設置國際平臺TaiwanPlus。另有185家無線廣播事業,以音樂節目最受歡迎,並具多種語言。 中央通訊社是臺灣最重要的通訊社,另有數百家新聞機構。2020年,臺灣有32家通訊社、252家報紙出版商、1,267家雜誌出版商。《中國時報》、《聯合報》、《自由時報》是發行量最大的報紙,而財經資訊、企業管理、新聞時事、時尚雜誌廣受歡迎。2022年,臺灣在新聞自由指數排行第38名,領先韓國(第43名)、日本(第71名)、香港(第148名)、中國大陸(第175名);不過相關新聞分析仍受到外部力量影響。臺灣還有" +755,"國還是全球尖端自行車的外銷大國,推廣自行車比賽及休閒。 在中華人民共和國加入聯合國後並獲得承認後,中華民國在國際賽事往往只能以權宜名稱「中華臺北」參加。儘管反對相關條件,但仍定期派出代表團參加奧林匹克運動會等國際體育賽事。中華民國在奧林匹克運動會競技項目中獲得多面獎牌,並在2020年夏季奧林匹克運動會創下最佳成績。中華民國在世界運動會、世界大學運動會、亞洲運動會、青年奧林匹克運動會、帕拉林匹克運動會、聽障奧林匹克運動會等賽事亦有獲獎紀錄。為了培育學校優秀運動人才,中華民國舉辦各項學生運動聯賽遴選運動人才,並推動體育學術、體育教學、校園競賽等措施。 中華民國定期舉辦全國運動會、全民運動會、全國原住民族運動會、全國身心障礙國民運動會、全國中等學校運動會、全國大專校院運動會等比賽。中華民國政府還推動運動環境改善,興設國民運動中心等體育場館設施,並鼓勵民間職業運動產業發展。多個城市設有公共自行車系統,並規劃環島自行車道路網。其他受歡迎的休憩方式還有爬山、散步、度假、電視、電影、音樂、跳舞、閱讀等,各類型登山運動相當風行。臺灣各地都有大型購物中心、連鎖百貨公司和特色商圈,臺灣中部更擁有多處渡假村" +756,"宗教(英语:religion)有多种定义,可以是联系人与神祇或超自然、神聖存在的文化体系;还可以定义为,以热情和坚定地信念而坚持信仰的原则或理论体系。 按前者定义,可分为多神论、泛神论、一神论。 按后者定义,无神论,自然神论, 不可知论等,也都是不同的宗教信仰。 这些宗教体系,包括个人行为、传统仪式、价值观念、世界观念、经典作品、朝拜圣地、道德规范或社会团体等形式。宗教信仰是人们对其中某个体系的共识和崇拜。人类学家克利福德·格尔茨声称与神话和哲学相辅相成,宗教相当于人文社科中的一门包罗万象的“生存之道”。 不同宗教可能包含不同元素,包括但不限于神性,圣物,信仰,超自然存在(一个或多个),给予信徒规范或力量的终极性或超验性生命体验。宗教的表现形式包括仪式,讲道,纪念或崇拜、祭祀神明,牺牲,节日,节庆,殡葬服务,婚姻服务,祷告,音乐,艺术,舞蹈,公共服务或其他文化形式。宗教可能通过神圣历史、叙述(可能通过神圣经文保存)、符号意义和圣地,来记录生命、宇宙或其他事物的起源、并以此表达生命的意义。传统意义上,信仰被认为是宗教信念的来源。全世界大约有10000个不同宗教 ,大约84%的人口附属于五" +757,"iversity of Alabama.而宗教學者羅德尼·斯塔克(Rodney Stark)和威廉·西姆斯·本布里奇(William Sims Bainbridge)在1996年出版的《宗教的未來》(The Future of Religion)中,也下評論說「在開始時,所有的宗教都是由隱蔽、微小且偏離主流的『邪教』起家的。」Gallagher, Eugene V. 2004. The New Religious Movement Experience in America. Greenwood Press, . p. xv. == 詞源 == 在漢語中,宗、教二詞各有其義,本不為一個統一的聯綴詞。按《說文解字》:「宗者,尊祖廟也,以宗從示。」,「示者,天垂象見吉凶所以示人也,從二。三垂,日月星也,觀乎天文以察時變示神事也。」故而,“宗”表示對被神格化和人格化的自然宇宙及人類祖先神靈的尊敬和敬拜。“教”則指教育、育化、啓發等,进而側重於表述對神道、人道、或某一家理論體系的信仰和傳播,這一點反而與西方的religion一詞內涵較為接近。近代中國常將儒家思想和傳統稱為“聖教”,此“教”的內" +758,"認真。另外古羅馬神學家奧古斯丁在《論真宗教》及《論靈魂的數量》皆用religare表示人、神與靈魂間的重新結合,以人神聯盟說來解釋人與神之間的密切關係。奧古斯丁又在《訂正》及《上帝之城》中使用re-eligere來表示人在信仰上的重新抉擇及決斷,人需要靠重新考慮和選擇與神修好。故religio一詞在拉丁語中基本用於表達人對神聖的信仰、義務和崇拜,以及神人之間的結合修好,其與神學的結合更為緊密。中世紀歐洲人從神學的角度出發,往往以略帶貶義的paganism(中譯為“異教”,但是由於中文缺乏相關語境,只作為中性詞)稱呼伊斯蘭等其他非基督教的信仰;而今日的英文詞彙religion及其對應的漢語概念“宗教”則是中性詞彙,可以用來代指基督教、伊斯蘭教、佛教及其他任何宗教。 == 定义 == 宗教有着各种各样的定义,多数定义试图在很多极端的解释和无意义表述中找到平衡。有人认为应用形式和理论去定义它,也有人更强调经验、感性、直觉和伦理的因素。 東西方也因歷史背景的不同而存在理解差異,宗教是一个翻译辭彙,冯友兰指出:「有其自己的意义,不能在中文中看见一个有教字的东西就认为是宗教」。取「以供奉神灵的宗庙" +759,"下是西方一些神學家對宗教所下的定義: 神學家 對「宗教」的詮釋 門辛 人與神聖真實體驗深刻的相遇、受神聖存在性影響之人的相應行為 繆勒 人對於無限存在物的渴求、信仰和愛慕 泰勒 對靈性存在的信仰 弗雷澤 人對能夠指導和控制自然與人生進程的超人力量的迎合、討好和信奉 施密特 人對超世而具有人格之力的知或覺 海勒爾 人與神聖的交往、相通和結合,是對神聖的生動經歷 范·德·列烏 人與神秘力量的獨特關係 奧托 對超自然之神聖的體驗,表現為人對神聖既敬畏而嚮往的感情交織 施萊爾馬赫 宗教是人對神的絕對依賴感 蒂利希 宗教是人的終極關切 == 内容 == 一個宗教之所以成為宗教,是因為它包括三個層面,其一為宗教的思想觀念及感情體驗(教義),二為宗教的崇拜行為及禮儀規範(教儀),三為宗教的教職制度及社會組織(教團)。 === 教義 === 宗教的教義主要分為「觀念」和「體驗」兩種。 * 觀念的類型 ** 靈魂觀念 ** 神靈觀念 ** 神性觀念 * 宗教經驗的類型 ** 敬畏感 * 經驗的類型 ** 宗教理論 ** 宗教道德 ** 宗教修習 即人的神觀及對神的敬畏心態,是整個宗教的內在因素及核心所在" +760,"者由不斷詮釋、匯集、編纂宗教經典而深化,後者也是經驗累積確定其施行的程度,於是教義及教儀皆在所處的文化氛圍中產生一種人們公認的社會慣例。 === 教團 === 宗教的教職制度及教团體系(如:基督教的教会、牧区,佛教的僧伽),代表着宗教信仰體系的機構化及社會化,是宗教的最外殼,卻又不可或缺。一方面替宗教提供了活動空間,另一方面也給予存在保障。教職制度令宗教有了可見的外在形體,從而對社會起作用。在歷史上,政教常有合一或分離現象,體現了社會行政功能與祭司宗教功能的有機結合,說明了宗教這一個體與世俗社會既有聯繫,又有區別。 教團及教職的形成,標誌著人類信仰體系的成熟,充實了宗教,補足了教義及教儀,由此凸現了宗教的獨特性與其他社會意識和社會文化歷史現象的不同特點。 == 起源 == 宗教源於准宗教現象,產生於原始宗教以前,為現今所知人類意識活動最早的形態之一,有一定程度的宗教因素,但當無超自然體的概念,對於客體尚未神化、無敬拜求告之念,一切全靠幻想,認為某些行動可影響某些現象或事物,如法術巫術、針刺木刻人形、佩戴玉石等。 舊石器時代洞穴壁畫的野獸圖像遍佈被利器刺傷之痕跡,多認為是行法術以增加狩獵" +761,"何考古實證。 === 巫術論 === 巫術論由弗雷澤提出,他認為人類精神是由巫術發展到宗教,再到科學。巫術是一種准宗教現象。原始人認為巫術可控制自然,原因不外乎是接觸律或相似律,即認為取得某東西的一部分作法可影響該物之全部,又或認為對該物之相似物作法可影響該物,但當人類不相信這種虛擬力量時,則轉移為對超自然存在物的撫慰和慈悲。 === 語言疾病學說 === 語言疾病學說(a disease of language)由德国哲学家馬克斯·繆勒提出,他認為古代印歐語言抽象詞彙少,當中某些概念性詞彙必須賦予人格化的聯繫,否則就無法運用。諸神最初只是自然現象的名字,後來從詞彙中人格化,造成實在感,神話由此誕生。这样,引起敬畏的對象逐漸被語言所掩蓋,並由於隱喻行為而最終與直接的知覺相分離。 === 原始一神論 === 原始一神論由施米特提出,他認為在最古老的文化中普遍有最上神的存在,可見於北美極地民族、俾格米人及澳洲東南土著,故一神論才是人類最古老的宗教觀,至上神的形象是來自神的最初啟示,往後的宗教發展呈退化趨勢,被諸神論及鬼靈精怪所掩蓋。 === 功能論 === 功能论最先由英国人类学家馬林諾夫" +762,"造了法西斯黨或這麼多恐怖主義?他又認為,如果是非標準取決於上帝的話,那上帝本身就不應該有至善性可言,如果相信上帝是至善,那麼就不得不承認是非標準由上帝以外的東西去下判斷,這種兩難,亦令有神論者為之結舌。 斯溫伯恩是著名的理性論護教者,他主要從「為什麼存在最普遍的自然定律」著手,他強調很多人忽略宇宙一開始不存在,或一直處於混沌狀態的可能都是非常大的,因此這種高度秩序的宇宙是令人驚訝的、超出科學的,而這並非由於科學落後未能解釋,而是科學的本質必終於某種終極的自然定律及物質結構。這種「終極」的其中一個可能解釋是一神論,因為在一神論中上帝肯定有能力做到這點,秩序性除令宇宙更美好外,還可讓人控制自然,改變自然以生存。進化論觀點在這個問題中是不可能優於設計論的,因為我們不免要問到進化這種法則到底是從何而來呢?這令我們要麼不作解釋,要麼從假設中作出解釋,即以一神論假設作出合理解釋。 對於以上兩種從科學、理性去解釋宗教的理性論(對立論及相關論),保羅·田立克認為太多人把宗教看成是一種只有低級證據而依賴宗教權威的知識,但他必須釐清宗教與科學有著本質上的分別(分離論),用科學去反教或護教皆是無謂的,因為兩" +763,"出這最高的被直覺者——「神聖者」——必須排除任何道德因素,這個「神秘者」令人產生「畏懼感」、「崇高感」及「活力感」。 情感論甫一推出即受到邏輯上的質疑,施萊爾馬赫暗指沒有情感的宗教體驗是談不上信仰、宗教,換言之宗教情感可證明信仰。可是別忘記如果宗教是「無理性」的,為什麼宗教情感可以以因果邏輯去證明信仰?一旦承認這種證明,即承認宗教包含理智成分,它就必受理智論批判。雖然奧托表明宗教體驗只能看作是「解畫的一種代替物」去接近宗教,是描述終極時人類以表意符號硬堆出來的最接近語言,但這亦無可避免表示當中有某些概念是能夠描述宗教經驗的。當代學者更有研究指出情感需依賴於認識或概念,當中有理性的元素,而非情感論者提出之「無理性」。另一宗教學家潘能伯格批評情感論者將宗教淪為個人經驗,宗教只是各人自己對無限情感的主觀,而這種主觀卻沒有任何約束力。 === 宗教意志論 === 宗教意志論起於帕斯卡,由威廉·詹姆士承之。總括來說,信仰輪不到我們用理智去選擇信與不信,而是分析風險作出賭注,可用「信仰的賭注」這五字來作結。前提是信仰上帝是一項「真正的抉擇」,詹姆斯對真正的抉擇定義為「有生命力的、不可迴避的、有重大" +764,"化實行,地球村逐漸形成。宗教間的對話不只是自我生存、宣揚正道的需要,也是免卻災難衝突的方法,無論是教會間、還是各宗教間,越來越多人提出要站出來對話,當中各宗教有其自我立場,分別為排他論、多元論及兼併論。 === 排他論 === 排他論者主張眾多宗教中只有一種是絕對真實的,其餘的皆為謬誤,排他論者在真理問題上有一種絕對化的觀點,而這也是宗教的本性,如果某宗教不是唯一的「真理」,那麼它就不值得信仰,事實上任何一個教徒只要它表示對自己的宗教忠心,就自然產生了一種內在的排他性。排他論是大多數一神論宗教的正統立場,它們只認為自己的神才是真神,只有委身於該宗教才可得到拯救,其他的宗教被視為非真實。當這種排他性排除其他種族時成為種族主義、或是排除其他異教徒而成為封閉宗教,在人類可知歷史上有多次因此類意識形態的排他而起之大型爭鬥。 亚伯拉罕诸教(基督宗教、犹太教和伊斯兰教)具有强烈的排他主义,强调唯一真神。基督宗教(含天主教、东正教和新教各派别)是典型的排他論宗教,但基督宗教的部分派系也提出「普世拯救論」。因此這就成了一個兩難的問題。另外,基督新教強調「信者得救」,造成反排他論者認為這只是一種狹隘的拯" +765,"兼併論者認為這種論調將兼併論放在一個很不利的位置,因為若其他宗教接受啟示並不完善也可得到神恩典救贖,那為什麼還要說服他們信正統宗教?而「無名的教徒」更是放諸四海皆準的說話,既然大家都有神的恩典,那一種宗教才是真正的宗教?那一種才是無名的宗教?這種隱含的排他性也使得兼併論者兩頭不討好。 == 影响 == 宗教在世界上各个国家和民族中都广泛存在。 === 政治 === 宗教与政治的关系很复杂,宗教信仰会影响法律。宗教也是一种社会意识形态,是上层建筑的一部分。在历史上,宗教常常被统治阶层用来作为统治人民的精神工具。另一方面,宗教也常常被反抗者用来作为反抗压迫的工具。 现代在许多国家,公民都有宗教信仰的自由,既有信教的自由,也有不信教的自由,有信这种教的自由,也有信那种教的自由,它是公民的基本权利之一。中世纪时,世界上有許多国家都采取政教合一的制度,国家政权与宗教合而为一,宗教首领就是国家首脑,直接掌握国家政权;设立单一的宗教成为国教,教规就是国法,公民必须信奉和遵守,公民没有信仰其他宗教或不信仰宗教的自由,否则会被认为是「异教徒」而遭到迫害。但在古代中国,由于儒家思想的影响,允许公民有信仰其" +766,"lf of the scientists don't believe in God, many scientists do believe in both science and God, in a perfectly consistent way. But this consistency, although possible, is not easy to attain...”摘自费曼《科学与宗教的关系》由于宗教很多的说法无法或未被证实但又被说成如同存在的「真理」,只是套套逻辑,内容并非实在而精密。皮尤研究中心的一份报告对美国科学促进会成员中的科学家进行了调查,结果发现只有 33% 的人相信上帝,而普通公众的这一比例为 83%, 科學家信仰宗教的比例远低于普通公众。 == 主要宗教 == ===非制度性宗教=== * 華人民間信仰 * 祖先崇拜 * 原始宗教 * 巫觋宗教 * 个人崇拜、、圣人崇拜、君主崇拜、领袖崇拜 * 自然崇拜 * 腾格里 * 拜物教 * 精靈崇拜 * 萨满教 * 万物有灵论 * 孤魂信仰 ===现存制度性宗教=== * 基督教:天主教、新教、普世聖公宗、東正教" +767,"教學院。 == 参考文献 == == 延伸閱讀 == * 吳汝鈞:〈論宗教與道德的關係〉。 * 张汝伦:〈不问信仰,但问有用与否〉。 * 《宗教學基礎十五講》,王曉朝,(北京:北京大學出版社,2003) * 《宗教大辭典》,任繼愈,(上海辭書出版社) * 《宗教學是什麼》,張志剛,(天地圖書) == 参见 == * 本原 * 宗教学 * 精神哲学 * 意识形态 * 文化 * 道德、伦理学 * 反宗教 * 無宗教 * 訴諸可能 * 宗教經驗 * 宗教的進化起源 * 世俗宗教 * 超越 (哲学) * 宗教證據 * 知识 * 哲学 *内在 Category:文化 Category:主要话题条目 Category:日語借詞" +768,"图书馆学是一门研究图书馆收集、加工、整理、保藏、控制、传递和利用文献訊息的理论、方法以及图书馆事业发展规律的科学,是一门综合性学科。 ==基本概念== ===定义=== “图书馆学”一詞最早由德国图书馆学家马丁·施莱廷格于1807年提出,他将图书馆学定义为“图书馆实施有目的的组织所需要的理论准则的概括”。图书馆学自概念提出已有200多年的历史。在過去,因為出版商習慣不斷推出更新版本,教科書價格不但昂貴,同時資訊內容也很容易過時或不便對照。因為其在網路上容易更新的特質,可以協助解決這類問題。同時使用開放授權以及維持線上也對老師們的幫助很大,因為這樣一來老師便可以依照自己的課程需求來編修教學資源。在这200多年间图书馆学随着时代的发展不断革新。施莱廷格最早使用“图书馆学”这一名称,表示图书馆学是一门独立的知识系统;他认为图书馆学的研究对象为“图书馆整理”,从而建立了图书馆整理论的理论体系,这在图书馆学的发展历史上具有开创意义柯平、弓克、孙情情:《图书馆学概念衍进二百年之思考》。此后,也有众多学者提出定义,但是由于时代在发展,每个人的观点都受其所处环境的限制。由此可见,图书馆学是随着时代的发" +769,"*第四定律:Save the time of reader,节省读者的时间。 *第五定律:A library is a growing organism,图书馆是一个成长中的有机体。 === 圖書館學新五律 === 圖書館學新五律(Five New Laws of Library Science)由美國學者W. Crawford與M. Gorman二人在1995年提出。 * 第一定律:libraries serve humanity,圖書館為全人類服務而存在。 * 第二定律:respect all forms by which knowledge is communicated,知識傳遞多面性。 * 第三定律:protect free access to knowledge,捍衛知識近用權。 * 第四定律:use technology intelligently to enhanced service,善用科技提升服務品質。 * 第五定律:honor the past and create the future,眺望過去、展望未來。 == 圖書館史 == 圖書館的原始功能在於保存人類文" +770,"=专业培养目标== 目前,中国大陆高校对图书馆学专业的学生的培养目标和要求如下: * 培养目标:本专业培养具备系统的图书馆学基础理论知识,有熟练地运用现代化技术手段收集、整理和开发利用文献信息的能力,能在图书情报机构和各类企事业单位的信息部门从事信息服务及管理工作的应用型、复合型图书馆的高级专门人才。 * 培养要求:本专业学生主要学习图书馆学与信息管理的基本理论和基础知识,受到文献学、目录学、信息学、传播学、管理学、经济学等方面的基本训练,掌握文献信息搜集、处理、研究、开发与传递的技能。 ==就业方向== 图书馆,IT行业,互联网公司,資訊咨询公司、資訊处理公司、数据分析公司。 ==学科分支== *普通图书馆学 *比较图书馆学 *图书馆信息学 *图书馆社会学 *图书馆经济学 *圖書館教育學 *图书馆建筑学 *图书馆技术 *读者心理学 *圖書館史 *图书馆管理学 **图书分类法 ***中国图书分类法 ***中文圖書分類法 ***中国图书馆图书分类法 ***中国科学院图书馆图书分类法 ***杜威十进制图书分类法 ***美国国会图书馆图书分类法 ***通用十进制图书分类法 ***冒号图书分类" +771,"《义勇军进行曲》是1935年5月在中國上海上映的華語電影《风云儿女》的主題歌。此歌先有歌詞再配以樂曲,田汉作詞、聂耳譜上人声主旋律、阿隆·阿甫夏洛穆夫編寫管弦乐總譜。 此曲在中國抗日戰爭時期已經是軍歌,國民革命軍第200師師長戴安澜定其為該師軍歌,該師在1942年远征緬甸也有奏唱。1949年9月27日定為中华人民共和国暫定國歌。1968年文化大革命時作詞者田汉被批斗至死,国歌歌詞随即被弃用而改為只奏純音樂。1978年,文化大革命期間集體填詞的《义勇军进行曲》被正式通過為中华人民共和国國歌。1982年撤銷1978年的新詞,恢復田汉作詞版。2004年3月14日,第十届全国人民代表大会第二次会议通过宪法修正案,正式将《义勇军进行曲》作为国歌写入宪法。 根据2015年8月26日国家新闻出版广电总局发布的“我最喜爱的十大抗战歌曲”网络投票结果,《义勇军进行曲》被选入“我最喜爱的十大抗战歌曲”名单,排名第一。 == 历史 == === 創作過程 === 1934年秋,田汉寫了電影剧本《凤凰的再生》,在1934年至1935年之交寫了主題曲歌詞,當時尚未取名,便遭國民政府逮捕入獄。1935年2月,电通" +772,"7年9月,吴晓邦参加上海救亡演剧队,在京沪线上演出,他根据《义勇军进行曲》编演了第一齣抗战舞蹈。國民革命軍第200師师长戴安澜定其为该师军歌,該師在1942年远征緬甸也有奏唱。1947年2月8日中華民國南京国民政府行政院禁止广播及灌制該曲。據叶剑英憶述,在二战時有不少反法西斯同盟國的電台將此曲視作中國國歌播放,甚至不知道當時中华民国国统区所使用的国歌(「三民主义 吾党所宗」版本)。 1949年7月4日,在中南海勤政殿第一会议室主持召开了新政治协商会议筹备会第六小组第一次会议,叶剑英推选郭沫若、田汉、茅盾、钱三强、欧阳予倩等5人组成国歌词谱初选委员会,郭沫若为召集人。8月5日,第六小组第二次会议决定,聘请四位音乐专业人士马思聪、贺绿汀、吕骥、姚锦为国歌词谱初选委员会顾问。由郭沫若等人起草的《征求国旗国徽图案及国歌词谱启事》经毛泽东、周恩来修改审批,分别在《人民日报》、《天津日报》、《光明日报》等各大报纸连续刊登8天。同时中国内地、香港和海外华侨报纸也纷纷转载。 截止到8月24日,筹备会收到有关国歌稿件350多篇。同日,第六小组第三次会议在北京饭店举行,对应徵的国歌歌词进行分析,认为应选者" +773,"东、周恩来召集的国旗、国徽、国歌、纪年、定都协商座谈会记录, 1949年9月25日。随后,毛泽东、周恩来和与会者一起合唱《义勇军进行曲》,座谈会结束。9月27日,中国人民政治协商会议第一届全体会议正式通过《关于中华人民共和国国都、纪年、国歌、国旗的决议》:“在中华人民共和国的国歌未正式制定前,以《义勇军进行曲》为国歌。”1953年定稿的管弦乐总谱出自李焕之。 1949年11月15日,《人民日报》发表了关于国旗、国歌和年号“新华社答读者问”:“《义勇军进行曲》是十余年来在中国广大人民的斗争中最流行的歌曲,已经具有历史意义。采用《义勇军进行曲》为中华人民共和国现时的国歌而不加修改,是为了唤起人民回想祖国创造过程中的艰难忧患,鼓舞人民发扬反抗帝国主义侵略的爱国热情,把革命进行到底。这与苏联人民曾长期以《国际歌》为国歌,法国人民今天仍以《马赛曲》为国歌的作用是一样的。” === 文化大革命時取消歌詞 === 《义勇军进行曲》作为国歌唱了17年。在1966年开始的「文化大革命」中,田汉被打成「右派」,于1968年含冤去世。其作品亦被认为有「政治问题」。特别是1970年全国大规模地批判田汉、周扬、" +774,"东旗帜,前进,前进,前进,进!”考虑到国歌不同于一般歌曲,为保持国歌的稳定性,只写“聂耳曲、集体填词”,一律不署编配者的姓名。 中国人民邮政1979年发行的中华人民共和国成立30周年纪念邮票,编号J.46(1-1),面额人民币8分,就有集体填词的《中华人民共和国国歌》和歌谱。 中国共产党十一届三中全会以后,1979年,原中央专案组对田汉的“结论”被推翻。田汉得到昭雪平反,1979年4月25日在北京八宝山革命公墓为他开了一个迟到的追悼会。1982年12月4日,第五届全国人民代表大会第五次会议通过关于中华人民共和国国歌的决议,撤销1978年3月全国人民代表大会通过的歌词,恢复田汉作词、聂耳作曲的《义勇军进行曲》为中华人民共和国国歌。 === 現行版 === 2004年3月14日,第十届全国人民代表大会第二次会议通过宪法修正案,正式将《义勇军进行曲》作为国歌写入宪法。2017年10月1日开始施行的《中华人民共和国国歌法》发布了国歌标准版本的简谱与五线谱,人民音乐出版社在《国歌法》生效前亦与中国人民解放军军乐团合作推出了国歌管乐版录音,该版本2024年9月前为中国政府网所刊载的标准版。人民音乐" +775,"| g4 g4 \bar | r8 b8 g8 a16 b16 \bar | d4 d4 \bar | \break b8. b16 g8. b16 \bar | d8. b16 a4 \bar | a2 \bar | e'4^> d4^> \bar | a4^> b4^> \bar | \break d8^> b8^> r8 d8 \bar | b8 a16 b16 g4 \bar | b4 r4 \bar | d,8. e16 g8 g8 \bar | b8. b16 d8 d8 \bar | \break a8 a16 a16 e4 \bar | a4. d,8 \bar | ^\< g4. g8 \bar | b4. b8 \! \bar | d2 \bar | \break g,8. b16 d8 d8 \bar | e4 d4 \bar | b8. g16 \times 2/3 {d'8 d d} \bar | b8 r8 g8 r8 \bar | d4^> g4^> \bar | \break b8. g16 \times 2/3 {d'8 d d} \bar | b8" +776,"日起实施的《〈中华人民共和国国歌〉国家通用手语方案》执行。2021年3月4日,在中国人民政治协商会议第十三届全国委员会第四次会议开幕会上,中国残疾人艺术团团长邰丽华与手语翻译用手语演唱国歌。 中国中央电视台(CCTV-4、CCTV-5+、数字付费频道及CGTN各频道除外)每天早晨都会播放国歌(2004年9月1日之前,CCTV-1每天晚间在《新闻联播》之前播放国歌)。从2017年10月1日《国歌法》施行起,央视开始播放新的国歌MV。在2021年1月新修改的《国旗法》和《国徽法》施行后,央视也同时发布了新的国歌MV。 此外,中央人民广播电台和中国国际广播电台的广播开始曲同样采用了该曲(中国之声/经济之声/音乐之声/香港之声也会播放基于该曲前六小节的编钟版,中国之声在星期二早上4:28播放完整版(2007年12月24日中国之声开始24小时播音前在3:58播放,大湾区之声每天清晨4:58播放完整版)中央人民广播电台第一套节目(现为中国之声)开始曲 ,Interval Signal Online,2002-08-29中国国际广播电台英语广播开始曲 ,Interval Signal Online," +777,"9(即8:00《新疆新闻联播》重播播出前))。 在电视、广播播出方面,截至2023年2月,中央广播电视总台央视、央广、国广各频道频率播出的国歌片带已更换为2021年版,而地方台播出的国歌片带则处于2018年版与2021年版并存的状态。 === 香港特别行政区 === 自從1997年7月1日香港主權移交起,香港特別行政區即使用中華人民共和國國歌《義勇軍進行曲》,國歌適用於-{zh-hans:升旗;zh-hk:升旗;zh-tw:升旗;}-儀式、中國香港代表隊在中外國際賽事得獎升旗禮時、紀念二次世界大戰捐軀者活動或其他場合,如國際會議、太陽計劃同根同心慶回歸大匯演和特區政府就職典禮、各大學畢業典禮等。2004年10月1日起,香港各大电视台每天傍晚新聞前必須播放《心系家国》政府廣告,其中有整段國歌。 2017年11月4日,第十二屆全國人大常委會第三十次會議通過把《國歌法》納入香港基本法附件三,將由當地立法引入香港成為香港法律一部分。2020年6月,經立法會三讀後時任行政長官林鄭月娥簽署《國歌條例》,6月12日刊憲生效。 2020年11月16日起,香港电台各电台频道统一在早晨播放国歌,香港商业电" +778,"017年11月4日,第十二屆全國人大常委會第三十次會議通過把《國歌法》納入澳門基本法附件三,引入澳門成為澳門法律一部分。 == 非作为国歌的使用情况 == === 中国国内 === 在2015年9月3日的纪念中国人民抗日战争暨世界反法西斯战争胜利70周年文艺晚会上,《黄河大合唱》与《义勇军进行曲》进行了共同演绎,并由知名钢琴家郎朗进行钢琴演奏。 === 中国国外 === 1939年,英属香港上映的鬼片《钟馗捉鬼》中,主人公在捉鬼途中唱起《义勇军进行曲》,然后继续捉鬼通識導賞:港式國防電影 含着熱淚笑中抗戰。 抗日战争期间,美国黑人歌王保罗·罗伯逊在纽约听到《义勇军进行曲》后,非常喜爱,不仅用英语四处演唱,还在莫斯科举行的纪念普希金诞辰150周年大会上用汉语演唱,并用汉语灌制了唱片,取名《起来!》(Chee Lai)。可參照保罗·罗伯逊演唱的英文版本。 另外,斯洛文尼亚的乐队亦演奏电子版,用英语和汉语演唱;英国歌手戴蒙·亚邦参与了中文小说《西游记》所改编舞台剧的作曲,便使用了该曲的曲调。 第二次世界大战将要结束时,美国国务院曾提出在反法西斯战争胜利之日演奏的各战胜国音乐时,选定《义勇军进" +779,"07年开始就关注国歌立法。 2017年初,中共中央总书记习近平对国歌立法作出批示,全国人大常委会将国歌立法列入2017年的立法工作计划。2017年5月2日,全国人大常委会公布《2017年立法工作计划》,《国歌法》于2017年6月初次审议。2017年6月22日,在第十二屆全國人大常委會第28次會議上,提請審議了《國歌法》草案。全国人大常委会法工委主任沈春耀在草案提审的说明中称:“多年来,国歌所承载的爱国情怀、忧患意识和奋勇前行的民族精神深入人心,广大人民群众热爱祖国、尊崇国歌,国歌奏唱和使用总体情况是比较好的。但实际生活中也存在奏唱国歌不规范、参与者举止不得体,国歌标准曲谱未予发布,影响奏唱和播放效果等。”2017年9月1日,12届全国人大常委会第29次会议表決通過《中華人民共和國國歌法》,將於當年10月1日起實施。根据《国歌法》,在全國人民代表大會和地方各級人民代表大會開幕會議、閉幕會議、中國人民政治協商會議全國委員會和地方委員會開幕會議、閉幕會議、憲法宣誓儀式、升國旗儀式、各級機關組織的重大慶典活動、重要表彰儀式、重大紀念儀式、國家公祭等、重大外交活動、重大體育賽事以及其他需要奏唱國" +780,"电台、电视台播放国歌应当使用国歌官方录音版本。在官方录音版本确定前,广播电台、电视台可以使用当前符合国歌标准演奏曲谱的通用版本。电视台播放国歌时,所配视频由中央电视台按照《国歌法》精神组织专家审定后录制。 2017年11月4日第十二届全国人大常委会表决通过的刑法修正案十规定:在公共场合,故意篡改中华人民共和国国歌歌词、曲谱,以歪曲、贬损方式奏唱国歌,或者以其他方式侮辱国歌,情节严重的,处3年以下有期徒刑、拘役、管制或者剥夺政治权利。 2019年8月29日,《关于在外交活动中奏唱中华人民共和国国歌的规定》经国务院批准开始施行。从此,在中华人民共和国国家领导人、外交部长及外交部、驻外外交机构和其他受权执行相关任务的国家机关及其人员,代表中华人民共和国进行对外交往的活动中,以及在其他部门和地方主办的外交活动奏唱国歌,应按此规定执行。 2019年11月,中共中央、国务院印发了《新时代爱国主义教育实施纲要》。新的纲要规定,要认真贯彻执行国旗法、国徽法、国歌法,学习宣传基本知识和国旗升挂、国徽使用、国歌奏唱礼仪。各级广播电台、电视台每天定时在主频率、主频道播放国歌。 == 相关事件 == 2011" +781,"溺水身亡的地方,有网友认为“这是对聂耳最好的告慰”。 2022年11月,中華人民共和國政府應對2019冠狀病毒病疫情的措施引起民眾不滿,引發抗議,民眾表達不滿的方式即包括唱《義勇軍進行曲》 。 == 相关纪念物 == 2009年9月26日,位于上海杨浦区大连路地铁站2号口(即电影《风云儿女》的拍摄地)的上海国歌纪念广场及国歌展示馆正式开馆,至2010年9月25日正式对外开放。该馆主要展示《义勇军进行曲》诞生的背景和过程、传唱和影响、被确定为国歌、国歌作者、国歌故事等国歌知识。 == 参见 == * 歌唱祖国——被称作“第二国歌”。 ==注释== == 参考文献 == == 外部链接 == * 中华人民共和国国歌(义勇军进行曲). 中国政府网 * * * * 唱了半个多世纪的《义勇军进行曲》,人民網 * 共和国辞典13期:国歌歌词浮沉,騰訊新聞 * 国歌法立法专题,中国人大网 Category:中华人民共和国国歌 Category:中华民国大陆时期电影主题歌曲 Category:抗日战争题材歌曲 Category:中国军歌 Category:聂耳作品 Category:田汉作品 Cate" +782,"2000年,中国夏商周断代工程结题,该工程对盘庚迁殷至周共和元年(前841年)之间的君主进行了详细的断代,本处的年表,即为该工程的结果。不过,由于夏商周断代工程存在一些争议,该工程的最后报告繁本也一直未能通过。因此,本处的年表推定尚且无法代表历史学界目前对共和元年之前纪年断代问题的权威与共识。 == 夏代年表 == (约前2070年-约前1600年) 禹、启、太康、中康、相、(羿、寒浞)、少康、杼、槐、芒、泄、不降、扃、廑、孔甲、皋、發、履癸(桀) == 商代年表 == (约前1600年-约前1046年) *汤、太丁、外丙、仲壬、太甲、沃丁、太庚、小甲、雍己、太戊、中丁、外壬、河亶甲、祖乙、祖辛、沃甲、祖丁、南庚、阳甲、盘庚(盘庚迁殷前) (约前1600年-约前1300年) *盘庚(盘庚迁殷后)、小辛、小乙 (前1300年-前1251年) *武丁 (前1250年-前1192年) *祖庚、祖甲、廪辛、庚丁 (前1191年-前1148年) *武乙 (前1147年-前1113年) *太丁(文丁) (前1112年-前1102年) *帝乙 (前1101年-前1076年) *帝辛(紂) (前107" +783,"==秦朝== 公元 皇帝/君王 备注 前249 庄襄王(子楚,-前247)即位 前249 楚灭鲁 前246 始皇帝(嬴政,前259-前210) 前240 齐邹衍(約前305-前240)逝 前238 楚春申君(-前238)逝 前237 甲子年 前235 呂不韦(-前235)自杀 前233 韩非(-前233)自杀 前230 灭韩 前228 灭赵 前227 荊轲(-前227)刺秦王(前259-前210) 前225 灭魏 前225 范雎(-前225)被杀 前223 灭楚 前222 灭燕 前221 灭齐,天下統一;設郡县,收民间兵器,定币制 前220 北巡;治驰道于天下 前219 泰山封禅;派徐福东海求仙药 前218 东巡,遭张良(-前186)使人袭于博浪沙 前214 蒙恬(-前210)败匈奴;筑驪山寿陵 前214 增筑长城,西起臨洮,东迄远东 前213 李斯(前284-前208)建议焚书令 前212 筑阿房宮;受侯生,卢生譏訕,坑儒460多人 前210 南巡,于沙丘宮病逝 前209 二世(胡亥,-前207) 前209 陈胜(-前208),吴广(-208)起义 前207 秦王(子嬰,-前206)" +784,"祈连山 前119 李广(-前119)自杀 前117 甲子年 前117 司马相如( 前179-前117)逝 前116 元鼎 前110 元封 前105 乌孙公主嫁乌孙王猎骄靡 前104 太初 前104 董仲舒(前179-前104)逝 前100 天汉 前99 李陵(-前74)降匈奴 前98 司马迁(前145-前85)受宮刑 前97 《史记》问世 前96 太始 前93 東方朔(前154-前93)逝 前92 征和 前88 后元 前88 李广利(-前88)被杀 前87 昭帝(刘弗陵,前94-前74) 前87 霍光(-68)攝政始 前86 始元 前81 苏武(前140?-前60)归汉 前80 元凤 前80 桑弘羊(-前8)被杀 前74 元平 宣帝(刘询,-前49/48) 前73 本始 前69 地节 前65 元康 前61 神爵 前59 郑吉(-前49)动员渠梨,龜茲诸国数万人出迎匈奴日逐王 前57 甲子年 前57 五凤 前53 甘露 前49 黃龙 元帝(刘爽,前75-前33/32) 前49 王皇后(前71-前13)外戚勢力抬头 前48 初元 前43 永光 前38 建昭 前33 竟宁 成帝(刘骜,前51" +785,"作者班固(32-92)死狱中 97 王充(27-約97)逝,著有《论衡》 100 许慎(58-147)著《说文解字》 105 元兴 殤帝(刘隆,105-106) 105 宦官蔡伦(63-121)献纸 106 延平 安帝(刘祜,-125) 107 永初 114 元初 120 永宁 121 建光 122 延光 124 甲子年 125 少帝(刘懿,-125) 即北乡侯 125 顺帝(刘保,-144) 126 永建 132 阳嘉 136 永和 139 张衡(78-139)逝,发明候风地动仪 142 汉安 144 建康 沖帝(刘炳,143-145) 145 永嘉 质帝(刘纘,138-146) 146 本初 桓帝(刘志,132-167) 147 梁太后(-150)攝政 147 建和 147 安息(伊朗)安世高至洛阳 150 和平 151 元嘉 153 永兴 155 永寿 158 延熹 166 第一次党錮之祸 167 永康 灵帝(刘宏,156-189) 167 月氏支婁伽讖至洛阳 168 建寧 169 第二次党錮之祸 172 熹平 178 光和 184 甲子年 184 中平 184 黃巾起义 189" +786,"-234)写《出师表》 228 诸葛亮(181-234)写《后出师表》 228 马謖(190-228)被杀 229 黃龙 吳大帝孫權 229 吳大帝正式即位 232 嘉禾 233 青龙 237 景初 238 延熙 赤乌 239 魏齐王(曹芳) 240 正始 244 249 嘉平 251 司马懿(175-251)逝 251 太元 252 神凤 吴会稽王(孙亮) 252 建兴 254 正元 魏高贵乡公(曹髦) 五凤 256 甘露 太平 258 景耀 永安 吴景帝 258 (孙休,-264) 260 景元 魏元帝(曹奂) 262 竹林七贤嵇康(223-262)被杀 263 竹林七贤阮籍(210-263)逝 263 炎兴 蜀后主退位 264 咸熙 元兴 吴乌程公(孙皓) 265 魏元帝退位 甘露 265 司马昭(221-265)逝 266 宝鼎 269 建衡 272 凤凰 275 天鍹 276 天玺 277 天纪 280 吴乌程公退位 280 三国(220-280)结束 == 参考文献 == == 参见 == * 中国历史年表 * 秦汉 * 三国 Category:中国史年表 * *" +787,"thumb|250px|北京交通大學的毕业生 教育,通常有廣義和狹義兩種概念。廣義的教育泛指一切傳播和學習人類文明成果,即各種知識、技能和社會生活經驗,以促進個體社會化和社會個性化的社會實踐活動,產生於人類社會初始階段;狹義的教育專指學校教育,即制度化教育。廣義的教育則包含社會待人處事的方方面面,例如家教、禮儀等文明與社會的教育。 在学校教育中,教师直接向学生教授一系列课程,包括阅读、写作、数学、科学、历史等。与之相对地,职业教育中只教授单一的职业技能。除此之外,人们还可以从其他渠道获得非正式的教育,如博物馆、图书馆、互联网,以及生活中的经验。其他一些新的教育方式也逐渐出现。 教育者按照法律法规和行业规范,根据学校条件和职称,有目的有计划有组织地对受教育者的心智发展进行教化培育,以现有的经验、学识授人,为其解释各种现象、问题或行为,以提高实践能力,其根本是以人的一种相对成熟或理性的思维来认知对待事物。人在其中,慢慢对一种事物由感官触摸到认知理解的状态,并形成一种相对完善或理性的自我意识思维。但同时人有着自我意识思维,又有着其自我的感官维度,所以任何教育性的意识思维都未必能够绝对正确,而" +788,"实现社会地位的变迁。教育伴随着人类社会的产生而产生,随着社会的发展而发展,与人类社会共始终。对教育的定义,各国学者认识不同。如美国的杜威说:“教育即生活。”英国的斯宾塞说:“教育为未来生活之准备。”又或更具體地把學校教育也視為啟蒙,為公民參與民主選舉作準備(Michelli & Keiser, 2005)。 === 中文涵義 === 中文裡的“教育”二字都是会意字,“教”字从攴()从孝,攴是形部,孝是形部亦兼聲部。“攴”的篆体字形是用手持杖或執鞭。“育”字在甲骨文字形里像妇女生孩子。上为“母”字及头上的装饰,下为倒着的“子”字。从汉字造字之初就说明了,教育是一種強制性、引導性,使人們符合社會規範的作為。 == 功能 == 即指教育活动的功效和职能,就是“教育干什么”的问题。教育的功能大致可分为:个体发展功能与社会发展功能。教育的个体发展功能故可分为教育的个体社会化功能与个体个性化功能两方面。社会活动的领域主要包括经济、政治和文化等方面,因而教育的社会发展功能又可分为教育的经济功能、政治功能和文化功能。 * 首要功能:促进个体发展,包括个体的社会化和个性化。 * 基础功能:影响社会人才体" +789,"=== 成人教育 ===== 自谋求个人的生存技能开始,到谋求国家利益、民族利益、人类命运持续科学化、幸福安全的高科技,用以造福人类 科学的教育是立体的,它分为4个层面:学校教育、家庭教育、社会教育和自我教育。 学校教育:指对学生智商(IQ)的提高为目的教育行为。 家庭教育:指对学生情商(EQ)的提高为目的教育行为。 社会教育:指对学生德商(MQ)的提高为目的教育行为。 自我教育:个人以提升个人综合素质为目的的教育行为。 四个层面相互交融、重叠和互补,形成了完整的教育体系。 ==== 校园教育 ==== 通常所说的教育,都是指校园教育,具有通用的区域或国家标准教材,有指定的教师,进行课堂口授讲述,辅之以板书和作业,结合测验、抽查考试、期中和期末考试,加上升学考试,再进入职业生涯。互动、快乐、学校指定和学生自选课程等都是比较先进的教育技术手段,至于案例教学,则是职业教育中比较成功的手段。借助信息化,把教育中可能遇到的种种技术和管理问题,更加精确地描述,让更多的人才在先进的教育技术条件下,更快、更多、一生学习接受科学知识。 === 节奏 === 0-3岁:培养内在秩序,培养目标以“专注”为" +790,"者是通过大学教育或者其他方法。某一学科通常包括一些子学科或分支,其区分往往非常严谨,比如自然科学类、数学类、计算机科学、社会科学、人类科学和应用型科学等。在一些国家的中学教育中,往往教育单位甚至提供一些符合大学标准的课程作为选修,比如音乐、舞蹈或歌剧。 ===学前教育=== 学前教育是指提供未满学龄的孩子进行教育的学校或机构,通常是幼教学校为主。 学前教育可以提供给孩子一个尽早接触竞争性社会和教育环境的机会。孩子通过学前教育进行学习一些字母、数字、颜色与绘画等训练,往往在开始规范式教育时,领先于同龄的、未受过学前教育的孩子。但真正的学前教育,应当是提供以孩子为中心的、目的是拓展孩子的智能和自身潜力等。 ===初等教育=== 初等教育通常包括受教育者最初的五年至七年的正規教育。通常來說,初等教育通常開始於受教育者五歲或六歲時,這往往和其所在的國家規定相關。根據聯合國教科文組織統計,全球範圍的89%適齡兒童正在進行初等教育,而且這一比率呈上升趨勢UNESCO, Education For All Monitoring Report 2008, Net Enrollment Rate in" +791,"高效率,并降低雇佣者的成本。而在欧洲,中等教育的普及更多是和公民教育等理念相应而生。中国随着清朝末期的学制改革(废除科举等),也相应引进了中等教育理念。在一些国家中,中等教育仍然被视为强制性的义务教育,尽管其受到了自主学习等教育理念的冲击。 === 专科教育或技术教育 === 专科教育是指一类为某一需求或领域提供直接或专业性的培训教育,例如电脑专科学校。专科教育通常与培训、实习等相伴,其课程通常包括手工艺、农学、机械工程、药物、建筑、计算机或艺术等,以培养适应不同市场的人才。以中華民國為例,該國對職業學校的定義為:「提供專業及實習學科為主課程,包括實用技能及建教合作,強化學生專門技術及職業能力之學校。」《高級中等教育法》第5條第2款 === 改革 === 隨着社會變遷,自20世紀後期起,全球多國推行教育改革,除了傳統學科知識外,也開始重視21世紀技能。 == 其他形式 == === 另類教育 === 一些家長會為子女選擇非主流學校提供的另類教育,如民主學校。 === 在家自學 === 一些政府視在學校接受教育為義務教育的唯一合法模式,但也有一些政府容許家長安排子女在家自學。 == 历史" +792,"三《南剑州》。南宋时“都城内外,有文武两学,宗学、京学、县学之外,其余乡校、家塾、舍观、书会,每一里巷须一二所。弘诵之声,往往相闻”。叶适说:“今吴、越、闽、蜀,家能著书,人知挟册。”叶适:《水心别集》卷九《汉阳军新修学记》直到清朝末期的1905年正式廢除,並以西方教育方式取代之。1898年,清德宗下旨設立京師大學堂,是為中國史上最早的現代綜合高等教育機構之一。後戊戌政變爆發,清德宗所倡議之諸項改革盡廢,然為攏絡民心,慈禧太后推動庚子後新政時決定保留京師大學堂。民國時,政府乃將京師大學堂改組為今北京大學。1916年,時任中華民國大總統黎元洪命前教育總長蔡元培出任北京大學校長。蔡元培掌管北京大學期間,致力於推動自由、開放的學風,使該校逐漸成為新文化運動的重鎮。 === 日本 === 日本教育的起源与宗教密切相关。学校是培养想要研究佛教成为僧侣的年轻人的庙宇。后来,想要学习的孩子就要去“寺子屋”(Tera-koya,意为庙宇小屋),学习如何读写日文。 === 最近世界教育趋势 === 在过去几十年中,未受任何学校教育的文盲人口的百分比下降了。从1960年的36%下降到2000年的25%。" +793,"及,故識字率也較低。 === 未開發國家 === 未開發國家由於國庫資金不如先進國家充盈,且大多沒有完整的工業體系爲依託,導致窮困和經濟發展緩慢的問題,因此政府大多無力提供足夠的教育經費,故教育界人才流失嚴重,造成專業教育家擔任教師意願低落,連帶導致教育水平普遍較低,不少學校教育設施簡陋,乃至無法保障學生正常食宿問題。同時,部分國家民衆受傳統思維限制,或多或少對現代化教育懷有偏見。這些因素共同導致教育資源分配不均問題。近年來,已有一部分未開發國家着力解決這個問題,埃塞俄比亞即是典例之一。 ===校園暴力=== 校园暴力问题主要集中于中等教育及高等教育阶段,体现出当前教育体系的急需完善之处。 *國三學生因為被沒收手機,將生教組長打傷,相關的暴力事件呈現出現代教師在教育上的實際地位低落,在執行懲戒上也無法保障自身人身安全。為手機衝動打傷老師 國三生愧疚報警自首 | 即時新聞 | 20160923 | 蘋果日報手機被沒收 國三生把老師打到骨折 - Yahoo奇摩新聞 *参见联合国教科文组织著作《校园暴力与欺凌-全球现状报告 》 == 學校 == 學校是一個按照一定的程序、有一定的場所和時程," +794,"劳伦斯(Obeng de Lawrence) * (Caroline Middleton DeCamp) * 杜威(John Dewey) * 艾宾浩斯(Hermann Ebbinghaus) * (Charles-Michel de l'Épée) * 摩謝·費登奎斯(Moshe Feldenkrais) * 保羅·弗萊雷(Paulo Freire) * 福禄贝尔(Friedrich Wilhelm August Fröbel) * 加涅(Robert M. Gagné) * 哈沃德·加德納(Howard Gardner) * 约翰·泰勒·盖托(John Taylor Gatto) * 保罗·古德曼(Paul Goodman) * 赫尔巴特(Johann Friedrich Herbart) * 库尔特·哈恩(Kurt Hahn) * 伊万·伊里奇(Ivan Illich) * 胡戈·科万塔伊(Hugo Kołłątaj) * 约瑟夫·兰开斯特(Joseph Lancaster) * 霍勒斯·曼(Horace Mann) * 马利亚·蒙特梭利(Maria Montessori) *" +795,"n) * 孔子 * 孟子 * 陶行知 * 蔡元培 * 李家同 * 錢思亮 * 吳清山 == 理论 == * 教育哲学 * 教育基本理论 * 教育法学 * 教育行政 * 教育管理学 * 信息技术教育 * 教育技术学、教育工學 * 教育工艺学 * 教育经济学 * 教育財政學 * 教育心理学 * 教育社会学 * 教育未来学 * 学校卫生学 * 比较教育学 * 教育统计学 * 教育传播学 * 教育效益学 * 教學科技 * 教育評鑑學 * 課程與教學 * 批判教育學 * 正向教育 == 机构 == * 托兒所 * 幼儿园 * 小学 * 高級中學 * 中学 * 國民中學 * 高級職業學校 * 技術學院 * 專科學校 * 科技大學 * 大学 * 图书馆 * 培训班 * 補習班 * 教育部 == 分类 == * 幼儿教育 ** 经典教育 * 初等教育 * 中等教育 * 高等教育 ** 研究生教育 ** 本科教育、大學教育 ** 专科教育 * 义务教育 * 终身教育 * 职业教育 * 成人教育 * 干部教育 * 远程教育 ** 函授 * 工读 * 數學教育 * 語言教育 ** 閱讀教育 * 科學教育 *" +796,"ichelli, N. M., & Keiser, D. L.(2005). Teacher education for democracy and social justice. New York: Routledge Falmer. * ; 期刊文章 * Dubois, H.F.W., Padovano G. & Stew, G.(2006)Improving international nurse training: an American–Italian case study. International Nursing Review 53(2): 110–116. ; 网页 * Brief review of world socio-demographic trends shows world illiteracy trends. == 註解 == == 延伸阅读 == == 外部链接 == * Study in china * The Encyclopedia of Informal Education * The Theory Into Practice Database" +797,"計算語言學,亦稱電腦語言學()是一門跨學科的研究領域,試圖找出自然語言的規律,建立運算模型,最終讓電腦能夠像人類般分析,理解和處理自然語言。 過去,計算語言學的研究一般由專門負責利用電腦處理自然語言的计算机科学家進行。由於近年的研究顯示人類語言是超乎想像的複雜,現在的計算語言學研究多由來自不同學科的專家共同進行。一般來說,研究隊伍的成員有電腦學家、語言學家、語言專家(熟悉有關研究項目所要處理的語言的人),以至研究人工智能、認知心理學、數學、邏輯學等的專家。 計算語言學具有理論和應用的成分。理論計算語言學聚焦於理論語言學與認知科學;應用計算語言學聚焦於模擬人類使用語言的實用成果。 计算语言学协会對於計算語言學的定義是:……從計算的觀點,以科學方法研究語言的學問。計算語言學家關注於提供各種語言學現象的計算模型。 ==起源== 計算語言學始於一九五零年代的美國,是人工智能研究的開端。當時,美國希望能夠利用運算又快又準確的電腦,將大量外語材料瞬間翻譯成英語;研究重點特別放在翻譯俄文寫成的科學技術刊物上,以窺探蘇聯的科技發展。電腦既然比人類運算更快、更準確,當初認為電腦很快就可以開始處理語言。計" +798,"的主題。下面各節探討橫跨整個領域的一些現有文獻,分為四個區塊:發展語言學,結構語言學,語言產出,以及語言理解。 ===發展門徑=== 語言是個人一生中發展出來的認知能力。這個發展過程已有一些檢視的技巧,運用計算是其中一種方法。人類的對於計算方法造成一些限制,增加了以其理解語言發展的難度。例如兒童在語言習得期間,大體上只遭逢正面例證,Bowerman, M. (1988). The no negative evidence problem: How do children avoid constructing an overly general grammar. Explaining language universals .亦即在個人語言發展期間,只接觸到語言正確的形式,而無不正確的形式,這對於做簡單的假說檢定而言資料不足,Braine, M.D.S. (1971). On two types of models of the internalization of grammars. In D.I. Slobin (Ed.), The ontogenesis of grammar: A" +799,",理解語言的結構至關重要。這個目標,英語已利用計算方法周密地進行研究,以便更佳理解英語在結構層次如何運行。研究語言結構的要件之一是具備大型的語料庫或樣本,計算語言學家才能有足夠的原始資料進行模擬,並且在大量的資料裡,更佳理解任一語言的內在結構。最常引用的語料庫之一是賓大。這個語料庫來源多元廣泛,例如IBM的電腦手冊和轉寫的電話談話,包含超過四百五十萬個美式英文字。這個語料庫主要以詞類標記與句法加括(syntactic bracketing)來註釋,並且已產生關於結構的大量實證觀察。 語言結構的理論也已發展,讓計算語言學家能在架構內運用多種方法發展假說,從而增進對語言的理解。關於語法的內化與語言結構,一個原創理論提出了兩種模型,其中學習到的規則與模式,會隨著接觸的頻率而增強。這種模型也留下一個問題有待計算語言學家解答:嬰兒如何能在未學習簡單化語法的情況下,學習特定與異常的語法(喬姆斯基範式)。在這個領域的早期,此類理論的成果設定了研究的方向,對於其茁壯至關重要。 語言結構的資訊可以用於認定兩個類似的語句, 例如近來已證實,根據人類言談模式之中的語言結構,概念的可以用來模擬資料並使其圖像化" +800,"因而找出「你…我」的基本句型,再把「你」和「我」轉成「我」和「你」,然後回答「為什麼你會認為我厭惡你?」在這個例子裡,ELIZA並不理解「厭惡」這個詞,但不妨礙在這種心理分析的情境下形成一個合理的回答。 ====文字互動門徑==== 許多最早期像ELIZA這種簡單的人機互動模式,電腦接受使用者的文字輸入然後產生回答。這種方法使用「」:電腦識別使用者打入詞句的類型,再據以回答。 ====語音互動門徑==== 最近的科技比較重視語音互動系統。這些系統,例如iOS作業系統的Siri,運用的技巧類似文字系統的句型辨識,只不過使用者的輸入是經由語音辨識。這個語言學的分支,把使用者的語音當作音波來處理,並解釋音質與語言的形式,讓電腦得以辨識使用者的輸入。 ===理解門徑=== 現代計算語言學多著重於語言理解。隨著網際網路的擴張,人寫的文字隨處可見,如果有程式能夠理解自然語言,會帶來廣泛、令人振奮的機會,包括改良的搜索引擎、自動化客服、以及線上教育。 語言理解的早期成果包括應用貝氏統計進行光學字符識別。其它貝氏統計的語言分析應用包括分析《聯邦黨人文集》以決定作者是誰。 理解口語方面,最初是奠基於19" +801,"University, n.d. Web. 19 Sept. 2016.計算語言學也能應用於文件檢索與分类。當你在線上搜尋時,出現的文件與網站取決於你鍵入的文字相應的獨特標示。 ==次領域== 計算語言學可以根據語言的媒介與執行的任務區分:語音或文字;分析(識別)或合成(生成)。 ===語音合成、語音識別=== 主要包括以下几个方面:语音编码(speech coding)、语音识别(speech recognition)、语种识别(language identification)、说话人识别(speaker recognition)或说话人确认(speaker verification)、语义理解(semantics understanding)、语音合成(speech synthesis)等。 在人机交互过程中,经常用到语音识别技术和语音合成技术。 ===訊息检索=== ===訊息抽取=== ===问答系统=== ===机器翻译=== 机器翻译的主要方法包括规则机器翻译,实例机器翻译及统计机器翻译 == 参考文献 == ==延伸閱讀== * * Steven Bird, Ewan K" +802,"处理 * 統計語義學 * 计算词汇学 * 计算语义学 * 数理语言学 * 语料库语言学 == 外部链接 == * Association for Computational Linguistics (ACL) ** ACL Anthology of research papers ** ACL Wiki for Computational Linguistics * CICLing annual conferences on Computational Linguistics * Computational Linguistics – Applications workshop * * Language Technology World * Resources for Text, Speech and Language Processing * The Research Group in Computational Linguistics Category:数学分支 Category:计算机科学 Category:语言学分支" +803,"語音合成是將人類語音用人工的方式所產生。若是將電腦系統用在語音合成上,則稱為語音合成器,而語音合成器可以用軟/硬體所實現。文字轉語音(Text-To-Speech,TTS)系統則是將一般語言的文字轉換為語音,其他的系統可以描繪語言符號的表示方式,就像音標轉換至語音一樣。 而合成後的語音則是利用在資料庫內的許多已錄好的語音連接起來。系統則因為儲存的語音單元大小不同而有所差異,若是要儲存phone以及diphone的話,系統必須提供大量的儲存空間,但是在語意上或許會不清楚。而用在特定的使用領域上,儲存整字或整句的方式可以達到高品質的語音輸出。另外,包含了聲道模型以及其他的人類聲音特徵參數的合成器則可以創造出完整的合成聲音輸出。 一個語音合成器的品質通常是決定於人聲的相似度以及語意是否能被了解。一個清晰的文字轉語音程式應該提供人類在視覺受到傷害或是得到失讀症時,能夠聽到並且在個人電腦上完成工作。從80年代早期開始,許多的電腦作業系統已經包含了語音合成器了。 == 文字處理的概觀 == == 歷史 == 直到19世紀,貝爾實驗室對於電子語音合成技術的研究,才開啟近代語音合成技術的發展。貝爾實驗" +804,"語音相對的文本。 基於DNN的語音合成器正在接近人聲的自然度。該方法的缺點的例子是當數據不充分時強健性較低,缺乏可控性,自動回歸模型的性能低。一些局限性(如缺乏可控性)可以通過未來的研究得到解決。 == 相关比赛 == *Blizzard Challenge *voice conversion == 含有語音合成器的軟體 == *NVDA(开源多国语言螢幕報讀軟體) *台灣工研院資通所語音團隊開發出的mTTS *JAWS(商業付費螢幕報讀軟體) *內建於蘋果電腦相關產品的VoiceOver Utility *Android 1.6版加入支援語音合成的功能。 *微軟在1995年開始,開發了一套,微軟的一些作業系統或軟體有提供支援語音功能。 *VOCALOID 及 VOICEROID,前者偏重歌唱而后者偏重 TTS。 == 語音合成標記語言 == == 参见 == * 人机界面 * 語音识别 * 手写识别 * VOCALOID * 初音未來 * 智能儀錶 * 智能玩具 * 电子地图 * 电子导游 * 电子词典 == 参考資料 == == 外部連結 ==" +805,"第二次世界大戰,是1939年至1945年爆發的全球軍事衝突。第二次世界大战涉及全球絕大多數的國家。包括所有的大國,并最終分成兩個彼此對立的軍事同盟——同盟國和軸心國。這次戰爭是人類史上規模最大的戰爭,各国动员至少1億多名軍人參與這次軍事衝突。主要的參戰國紛紛宣布進入總體戰狀態,幾乎將自身國家的全部經濟、工業、科學技術用於戰爭之上,同時將民用和軍用的資源合併以便規劃。第二次世界大戰也是有紀錄以來最多大規模民眾死亡的軍事衝突,总死亡人数达7000萬,其中,死于交战各方刻意屠杀的平民和战俘至少有1,800多万(不包含失踪或未记录在册的),是继第一次世界大战之后人類歷史上死傷人數最多的戰爭。第二次世界大战改变了世界局势,大英帝國、法蘭西殖民帝國等欧洲殖民帝国衰落,美国、苏联則取代欧洲殖民帝国的地位成了新的超级大国并在战后形成兩強格局,直到1991年12月26日蘇聯解體。 1931年9月18日,日軍发动“九一八事变”,出兵占领中国东北,建立傀儡國家满洲国,當地中國守軍被迫撤離。1937年7月7日,日本又发动了“盧溝橋事變”,惟中國政府在這次衝突中開始積極對抗,於是中国抗日战争全面爆发。西方媒體多" +806,"本開始在一系列的海戰中戰敗,在中途島海戰損失四艘航空母舰,歐洲的義大利也陸續於北非戰役及德國在斯大林格勒戰役中敗退,這些都使軸心國暫停進攻的腳步。1943年時,義大利在西西里島戰役被同盟國部隊經突尼西亞登陸打败投降,推進至義大利南部,國內政變,總理贝尼托·墨索里尼失勢,逃往義大利北方繼續作戰,德軍在東線的库尔斯克会战戰敗後失去對於東線的主動權,同時美國在太平洋戰區中獲得了一連串的勝利,自此軸心國逐漸失去主導權並開始後撤佈署於各地的前線部隊。到了1944年時,盟軍決定登陸法國諾曼第以開闢第二戰場,而蘇聯除了收復過去被佔領的領土外,也開始轉往進攻其他軸心國。在蘇聯部隊攻入柏林後,德國元首希特勒自殺,第二次世界大戰歐洲戰區最終在1945年5月8日德國投降後宣告結束。另一方面,美國在1944年和1945年成功擊敗了日本海軍部隊並陸續佔領數個重要的西太平洋島嶼,這使得日本列島隨時面臨同盟國部隊入侵的危機。最後美軍分別於廣島市和長崎市投下原子彈並造成大量日本平民死亡。1945年8月9日蘇聯進攻日本控制下的中國東北地區,8月15日,日本宣佈接受波茨坦公告,也代表執行開羅宣言中無條件投降的條件,194" +807,"國抗日戰爭爆發之後便已經開始。 英國歷史學家安東尼·畢佛等人,則認為第二次世界大戰的起源應該從1931年9月18日日軍發動九一八事變開始計算Ghuhl, Wernar (2007) Imperial Japan's World War Two Transaction Publishers pp. 7, 30。另外也有如英國歷史學家A·J·P·泰勒等抱持著不同的想法,認為抗日戰爭和在歐洲以及其殖民地所爆發的戰爭剛開始只不過是同時發生的不同軍事衝突,兩場戰爭最終於1941年合併,此時才進入「世界大戰」階段A·J·P·泰勒. Origin Of The Second World War. 美國紐約: Simon & Schuster. 1996年4月1日 [2012年4月21日查閱]. ISBN 978-0684829479 .。 其他說法還包括1935年10月3日,因為-{zh-cn:意大利入侵埃塞俄比亚;zh-hant:義大利入侵衣索比亞}-的舉動而爆發戰爭Eliahu Ben-Horin. The middle East: crossroads of history. 美國紐約: 諾頓" +808,"rld War II are part of the same European Civil War or Second Thirty Years War : ; .。另外也有人認為第一次世界大戰和第二次世界大戰都是屬於「歐洲內戰 」(European Civil War)或者是「第二次三十年战争 」(Second Thirty Years War)的一部份Luciano Canfora. Democracy in Europe: A History of an Ideology. 美國霍博肯: Wiley-Blackwell. 2006年1月6日: 第155頁 [2012年4月21日查閱]. ISBN 978-1-4051-1131-7 .。英國歷史學家安東尼·畢沃爾將1939年5月至9月日本與蒙古和蘇聯軍隊之間的諾門罕戰役視為第二次世界大戰的開始。但也有其他人認為西班牙內戰是第二次世界大戰的開始或前奏。 战争结束的日期通常被认为是1945年8月15日日本投降。1951年,日本和盟国签署了舊金山和約。1990年,最终解决德国问题条约允许东西德统一,并解决了二战后的大多数问题。日本和苏" +809,"獨立,並以戰爭開銷與賠償為由徵收巨額賠款。條約中更進一步限制德國軍事力量的規模以及配備,包括了撤除空軍以及撤銷參謀部。而與此同時俄國內戰漸趨緩和,並且另外成立蘇聯掌管政權諾曼·戴維斯. No Simple Victory: World War II in Europe, 1939-1945. 英國西敏: 企鵝出版集團. 2008年8月26日: 第134頁至第140頁 [2012年4月21日查閱]. ISBN 978-0-14-311409-3 .。 1918年到1919年德國十一月革命爆發,導致德意志帝國瓦解並由另外建立的民主政府取代,新成立的德國政府後來又稱魏玛共和国。然而在戰間期的這段時間,剛剛成立的魏玛共和国便面臨了國內右派與左派各自支持者的權力爭奪衝突。另一方面在第一次世界大戰中期,與英國和法國簽署1915年倫敦條約的意大利以獲得領土為條件決定轉而投靠協約國集團,但是意大利的民族主義者不滿在戰爭結束後兩國並沒有遵守領土轉移的承諾,隨後因為這次領土擴展事件反而讓意大利社會爆發衝突。而從1922年到1925年開始,以本尼托·墨索里尼為首的法西斯黨成功於-{zh-cn:意大利;zh-" +810,"試推翻德國政府,出獄後競選總統輸給保羅·馮·興登堡,但在1932年保守右派前總理帕彭向政敵報仇的策劃下,透過右翼國家人民黨及德國企業家們政商合作,身為國會第一大黨的納粹不僅獲得資助、解除瀕臨破產的危機,更因此得到政商名流聯名遊說,最終說服興登堡任命他於1933年合法成為德國總理。他宣布廢除民主制度,同時藉由民族主義的風潮提出要重新修正世界秩序,並很快便開始大規模地重整軍備。與此同時法國為了​​確保意大利仍然與其合作、維持同一陣線之協議,允許意大利將埃塞俄比亚列為自己的殖民地。然而在希特勒的主導下,德國於1935年再度合併原本作為德國領土的薩爾盆地地區,而在獲得民意支持的情況下德國隨後推翻《凡爾賽條約》且加速重整軍備的計劃,甚至以大規模徵兵的方式擴充部隊兵力,而這些強調國家主權的舉動也更加加深了德國社會對於希特勒的聲望。 為了能夠抗衡納粹德國的不斷發展,英國、法國和意大利決定成立斯特雷薩陣線。另外蘇聯也懷疑德國的目標也包括占領東歐的廣大領土,蘇聯為此也與法國簽署《蘇法互助條約》;然而儘管《蘇法互助條約》已經簽署,該協議仍然堅持必須要經過國際聯盟的討論才能針對入侵行為有所因應,這使得這一條" +811,"共產國際協定》,意大利則在隔年也加入其中。這時中國發生西安事變,中國國民黨和中國共產黨軍隊同意停火並組織統一戰線來抵抗日本入侵。 ===亞洲=== 中華民國的國民黨則為了能夠清除軍閥的勢力,在1920年代中期由蔣中正領導國民革命軍開始自南向北展開北伐,在1928年奉系領袖張學良宣布歸順後,名義上統一中國。然而北伐結束後,國民黨便與原本一同合作的共產黨發生衝突,中國很快又再度陷入第一次國共內戰中彼得·普瑞斯頓(Peter Preston). Pacific Asia in the Global System: An Introduction. 美國霍博肯: Wiley-Blackwell. 1998年8月14日: 第104頁 [2012年4月21日查閱]. ISBN 978-0-631-20238-7 .。1931年大日本帝國內軍國主義正趨高漲,帝國政府開始策劃如何能夠有效掌控中國領土並且獲取自然資源,進而藉此發展出足夠的實力來掌管亞洲地區。 此時蔣中正以「先安內,後攘外」的方針回應日本的入侵行為,繼續剿共,以便穩定局勢,另一方面則期望國際聯盟協助處理日軍進佔中國領土的問題,但日本在遭到" +812,"BN 978-0-345-02462-6 .。 === 西班牙內戰(1936年—1939年) === 德國和意大利为西班牙的弗朗西斯科·佛朗哥將軍领导的国民军提供军事支持,並且協助打擊當時受到蘇聯支持、而明顯朝向左派傾斜的西班牙第二共和國政府。德國和蘇聯也藉由這次代理人戰爭作為契機,不斷拿這次衝突來測試新型武器和戰術。其中1937年4月德國禿鷹軍團蓄意轟炸格爾尼卡而受到當時社會的關注,這也使国际社会担心未來的重要戰爭將会把平民居住區也納入轟炸範圍內安東尼·畢佛. The Battle for Spain: The Spanish Civil War 1936-1939. 英國倫敦: Phoenix Books. 2007年: 第258頁至第260頁 [2012年4月21日查閱]. ISBN 978-0-7538-2165-7 .史蒂芬·布迪安斯基(Stephen Budiansky). Air Power: The Men, Machines, and Ideas That Revolutionized War, from Kitty Hawk to Gulf War II. 美國紐約" +813,"聯也開始提供軍需物資以支援中國對日作戰,而已經轉往支持日本的德國則在年底停止與中國的貿易合作關係。日本攻佔北平後,見中國並沒有屈服,決定於1937年8月中旬進攻上海。國民革命軍總司令、國民政府軍事委員會委員長蔣中正決定部署自己轄下素質最佳的部隊防衛上海。历经3個月戰鬥,上海於11月遭到日軍佔領。日軍之後不斷逼使中國軍隊往西方後退,1937年12月13日日軍成功佔領首都南京,并屠杀大量中国平民和缴械军人Levene, Mark and Roberts, Penny. The Massacre in History. 1999, page 223-4Totten, Samuel. Dictionary of Genocide. 2008, 298–9.。 1938年6月,中國軍隊為了阻止日軍繼續由河南繼續向前推進至湖北,而故意造成黃河氾濫。同時中國將拖延下來的時間拿來繼續加強武漢市的防禦工事,但是到了10月時日軍仍成功佔領武漢當地。然而日本軍事上的勝利並沒有如日軍希望般瓦解國民政府的抵抗,相反地中國政府在遷往位於內陸的重慶市後仍繼續堅持作戰,並定都當地。 === 蘇日冲突(1938年—19" +814,"=== 在歐洲,德國和意大利的舉動更加變得大膽,1938年3月德國併吞了奧地利,然而這件事只自其他歐洲列強中得到一點反應。這鼓舞希特勒希望能夠將過去作為索賠的對蘇台德地區重新納為德國領土,並且以該塊土地是在捷克斯洛伐克內作為少數民族的德意志族群主要生活地區而要求合併。法國和英國在不詢問捷克斯洛伐克政府的情形下,很快就在慕尼黑協定中承認蘇台德地區應該歸德國所有,在種種壓力之下使得捷克斯洛伐克只能接受領土移交的要求安恩·科修(Ian Kershaw). Hitler: 1936-1945: Nemesis. 美國紐約: 諾頓公司(W. W. Norton & Company). 2001年9月: 第121頁至第122頁 [2012年4月21日查閱]. ISBN 978-0-393-32252-1 .。但是不久之後,德國和-{zh-cn:意大利;zh-tw:義大利}-仍繼續迫使捷克斯洛伐克割讓更多領土,這也使得鄰近的匈牙利和波蘭也開始備受威脅安恩·科修(Ian Kershaw). Hitler: 1936-1945: Nemesis. 美國紐約: 諾頓公司(W. W. Norton & Co" +815,"[2012年4月21日查閱]. ISBN 978-0-415-27372-5 .。稍後不久英法與波蘭陸陸續續結為同盟,而德國也與意大利正式簽署了交互合作的《鋼鐵條約》The Oxford Companion to World War II. 英國牛津: 牛津大學出版社. 2002年1月24日: 第674頁 [2012年4月21日查閱]. ISBN 978-0-19-860446-4 .。希特勒控诉英国和波兰试图“包围”德国,并宣布废除《英德海军协定》和《德波互不侵犯条约》。 1939年8月,德國和蘇聯秘密簽署了《蘇德互不侵犯條約》Zachary Shore. What Hitler Knew: The Battle for Information in Nazi Foreign Policy . 英國牛津: 牛津大學出版社. 2002年12月5日: 第108頁 [2012年4月21日查閱]. ISBN 978-0-19-515459-7 .。同時雙方也秘密於條約簽署時分配各方之後在「領土與政治重新安排」中所獲得的利益,也對之後如何處理波蘭問題上達成了協議。雙方約定由德國佔領波蘭和立陶宛" +816,"(1939年–1940年) === 1939年9月1日德國和其附屬國斯洛伐克一同進攻波蘭理查·艾凡斯(Richard J. Evans). The Third Reich at War - How the Nazis Led Germany from Conquest to Disaster. 英國西敏: 企鵝出版集團. 2008年: 第1頁至第2頁 [2012年4月21日查閱]. ISBN 978-0-7139-9742-2 .。9月3日法國、大英帝國紛紛正式向德國宣戰,但實質上給予波蘭的支援則十分少格哈特·溫伯格. A World at Arms: A Global History of World War II. 英國劍橋: 劍橋大學出版社. 2008年3月28日: 第64頁至第65頁 [2012年4月21日查閱]. ISBN 978-0-521-85316-3 .,只有在法國薩爾當地發起薩爾攻勢約翰·基瑾(John Keegan). Second World War. 英國倫敦: Pimlico. 1997年11月6日: 第35頁 [2012年4月21日查閱]. ISBN 97" +817,"部隊進攻的行列史蒂芬·扎洛加(Steven Zaloga). Poland 1939: The Birth of Blitzkrieg. 美國西港(Westport): Greenwood Publishing Group. 2004年2月: 第88頁至第89頁 [2012年4月21日查閱]. ISBN 978-0-275-98278-2 .,而著名的則帶著有關破解密碼的資料撤離到法國史蒂芬·布迪安斯基(Stephen Budiansky). Battle of Wits: The Complete Story of Codebreaking in World War II. 英國西敏: 企鵝出版集團. 2001年: 第120頁至第121頁 [2012年4月21日查閱]. ISBN 978-0-14-028105-7 .。同一時間日本則對具有重要戰略意義的中國城市長沙市首次發起攻勢,但是在9月下旬被中華民國國軍擊退Philip Jowett. Japanese Army 1931-45 英國牛津: Osprey Publishing. 2002年1月25日: 第14頁 [2012年4月" +818,"才有所交戰。1940年2月時,蘇聯和納粹德國除戰前協議外另外簽署《》,由蘇聯提供各式原料來換取德國的軍事和工業設備,而德國也能夠藉此迴避盟軍對其的原料封鎖威廉·劳伦斯·夏伊勒. 第三帝国兴亡史. 美國紐約: Simon & Schuster. 1990年11月15日: 第668頁至第669頁 [2012年4月21日查閱]. ISBN 978-0-671-72868-7 .。 1940年4月9日,德國為了避免盟軍藉由來阻擋瑞典出產的鐵礦砂運往德國的路線,入侵了丹麥和挪威以確保原料的運輸。當德國要求丹麥接受保護時,丹麥很快就宣布接受並投降,成為保護國。 1940年5月,為防德國搶先攻佔,英軍佔領冰島,同時,由於英國對挪威戰役的不滿,邱吉爾於1940年5月10日取代內維爾·張伯倫,成為英國新任首相。然而,到了6月1日,挪威軍隊還是被迫和德軍簽署停戰協議,挪威随后沦陷。军隊投降。 === 軸心國推進(1940年-1941年) === ==== 西欧 (1940年–1941年) ==== 1940年5月10日,德軍開始入侵法國等西歐同盟國與中立國,德軍部隊進攻比利時與荷蘭,吸引法軍主力北上前往預" +819,"g)、維爾納·賴恩(Werner Rahn)和賴因哈德·斯圖姆夫(Reinhard Stumpf). Germany and the Second World War: Volume VI: The Global War. 英國牛津: 牛津大學出版社. 2001年11月15日: 第311頁 [2012年4月21日查閱]. ISBN 978-0-19-822888-2 .而早在7月3日,英軍就襲擊停泊在法屬阿爾及利亞的法國艦隊,以防止德國將法軍艦隊納入自己的海軍中。 同年6月中下旬,蘇聯也強行佔領了愛沙尼亞、拉脫維亞和立陶宛等國家和羅馬尼亞具有爭議性的比萨拉比亚地區但直到8月才兼併。與此同時,納粹德國也和蘇聯在政治與經濟合作上逐漸陷入僵局尼爾.佛格森(Niall Ferguson). The War of the World. 英國西敏: 企鵝出版集團. 2007年10月30日: 第367頁至第417頁 [2012年4月21日查閱]. ISBN 978-0-14-311239-6 .蒂莫西·史奈德(Timothy D. Snyder). Bloodlands: Europe Betwee" +820,"围城战 (第二次世界大战)、8月意大利佔領英屬索馬利蘭,並且在1940年9月時意大利入侵埃及。日本則在9月時開始強化對中國的封鎖,派遣部隊進駐位在法屬印度支那北部的數個基地理查·奧弗里(Richard Overy)和安德魯·惠克羅夫特(Andrew Wheatcroft). The Road to War: Revised Edition. 英國西敏: 企鵝出版集團. 2000年7月1日: 第288頁至第289頁 [2012年4月21日查閱]. ISBN 978-0-14-028530-7 .。 在此期間態度保持中立的美國決定採取新方案,即租借法案,以協助中華民國和西方盟國作戰。1939年11月美國政府就中立法案進行修訂,允許盟軍以「現購自運」的方式來購買軍事物資與裝備理查·奧弗里(Richard Overy)和安德魯·惠克羅夫特(Andrew Wheatcroft). The Road to War: Revised Edition. 英國西敏: 企鵝出版集團. 2000年7月1日: 第328頁至第330頁 [2012年4月21日查閱]. ISBN 978-0-14-028530-7" +821,"盟國的美國,1940年9月底日本、意大利和德國簽署《三國同盟條約》正式組成軸心國集團,並建立口號(歐洲新秩序)。《三國同盟條約》中規定除了因應蘇聯主動進攻而另外處理外,任何國家都必須協助任何雖未參與戰爭但仍被攻擊的軸心國國家。在這段期間,美國則繼續通過了租借法案來支援英國和中國作戰;美國也授權美國海軍保護英國所購買的物資和裝備運往大西洋中間的安全區,之後由英國艦隊接手進行管理。也因此德國和美國在1941年10月陸續在大西洋北部和中部發生數次海戰,但是美國在官方立場上仍然持續保持中立。 ==== 地中海 (1940年–1941年) ==== 1940年11月時,軸心國開始大規模擴展自身的勢力The Oxford Companion to World War II. 英國牛津: 牛津大學出版社. 2002年1月24日: 第877頁 [2012年4月21日查閱]. ISBN 978-0-19-860446-4 .。1940年10月時意大利嘗試入侵希臘,但是在幾天內便被擊退到阿爾巴尼亞地區而陷入僵局理查·克羅格(Richard Clogg). A Concise History of Gree" +822,"年4月21日查閱]. ISBN 978-0-7146-5205-4 .。意大利皇家海軍於戰爭中遭受了十分慘重的損失,皇家海軍派遣航空母艦攻擊位在塔蘭托的意大利部隊在這次的攻擊中也導致3艘意大利的戰列艦受損,而之後在馬塔潘角海戰中意大利又損失更多艘軍艦雅士利·傑克森(Ashley Jackson). The British Empire and the Second World War . 英國倫敦: Continuum國際出版集團(Continuum International Publishing Group). 2006年5月10日: 第106頁 [2012年4月21日查閱]. ISBN 978-1-85285-417-1 .。 因此德國便決定介入此戰事並協助意大利對英作戰。希特勒於2月時將部分德國軍隊派遣前往利比亞當地,並且在3月底時已經對德部隊便已經數次發動小規模進攻喬·拉蒂默(Jon Latimer). Tobruk 1941: Rommel's opening move . 英國牛津: Osprey Publishing. 2001年1月25日: 第7頁至第8頁 [2012" +823,"織政府。軸心國部隊在巴爾幹地區很快獲得進展,在5月底德軍部隊攻佔希臘克里特島後,成功迫使同盟國軍隊於此處撤退格哈特·溫伯格. A World at Arms: A Global History of World War II . 英國劍橋: 劍橋大學出版社. 1995年7月28日: 第229頁 [2012年4月21日查閱]. ISBN 978-0-521-55879-2 .。6月中旬,由德國及義大利扶植的傀儡政权克羅埃西亞獨立國加入轴心国。 不過處於劣勢的盟軍,在這段期間也有獲得少數勝利,納粹德國佔領法國及扶植維希政府後,本來希望法屬敘利亞託管地能夠在中東參與對英國的作戰,然而同盟國部隊成功破壞在維希法國空軍基地內的德軍,進而阻止敘利亞於伊拉克後方發動叛亂威廉·沃森(William E. Watson). Tricolor and Crescent: France and the Islamic World . 美國西港(Westport): Greenwood Publishing Group. 2003年6月30日: 第80頁 [2012年4月21日查閱]. ISBN 978-0-" +824,"轟炸,最終德國於英國上空的主要空襲行動在1941年5月宣告結束The Oxford Companion to World War II. 英國牛津: 牛津大學出版社. 2005年9月22日: 第108頁至第109頁 [2012年4月21日查閱]. ISBN 978-0-19-280670-3 .。 ==== 亚洲 ==== 而在亞洲戰區,儘管雙方都互有發起攻勢,但是在1940年時中國和日本之間的戰爭陷入僵局。日本為了增加對中國所施加的壓力,決定開始優先攻佔重要位置以阻止供應路線,同時與準備與西方列強展開戰爭。這一時期,日軍也繼續佔領了印度支那南部地區並給予軍事管制理查·奧弗里(Richard Overy)和安德魯·惠克羅夫特(Andrew Wheatcroft). The Road to War: Revised Edition. 英國西敏: 企鵝出版集團. 2000年7月1日: 第289頁 [2012年4月21日查閱]. ISBN 978-0-14-028530-7 .。同年8月中國共产党在華北发起百團大戰打擊日軍部隊,作為報復日本則決定在佔領區內對共產黨黨員和一般民眾實施嚴厲的三光" +825,"為此日本與蘇聯2個強盛國家在1941年4月簽署了《蘇日中立條約》John W. Garver. Chinese-Soviet Relations, 1937-1945: The Diplomacy of Chinese Nationalism. 英國牛津: 牛津大學出版社. 1988年9月8日: 第114頁 [2012年4月21日查閱]. ISBN 978-0-19-505432-3 .。但是在另一方面,德國則逐步在蘇聯邊境佈署軍事武力,準備在之後直接攻擊蘇聯格哈特·溫伯格. A World at Arms: A Global History of World War II . 英國劍橋: 劍橋大學出版社. 1995年7月28日: 第195頁 [2012年4月21日查閱]. ISBN 978-0-521-55879-2 .。 === 全球衝突(1941年-1942年) === ==== 巴巴罗萨行动和苏德战争 ==== 1941年6月22日德國發起巴巴羅薩行動,聯合其他歐洲軸心國集團的成員國以及芬蘭共同入侵蘇聯,苏德战争爆發。目標是期望藉由兵分三路的快速突擊攻佔波羅的海地區、莫斯科和烏" +826,"39-9712-5 .喬納森·斯坦伯格(Jonathan Steinberg). The Third Reich Reflected: German Civil Administration in the Occupied Soviet Union, 1941–4. 英國牛津: 《The English Historical Review》. 1995年6月: 第620頁至第651頁 [2012年4月21日查閱] .;同時德國也希望能夠攻佔蘇聯的重要地區,並且藉由當地的戰略資源進而能夠擊敗德國的其他對手Milan Hauner. Did Hitler Want a World Dominion?. 美國千橡: 《當代史期刊》(Journal of Contemporary History). 1978年: 第15頁至第32頁 [2012年4月21日查閱] .。 雖然蘇聯紅軍在戰爭之前亦有準備戰略反攻計畫,但是軸心國部隊發起的巴巴羅薩行動仍迫使蘇聯最高統帥部只能夠採取作戰辛西雅·羅伯茨(Cynthia A. Roberts). Planning for War: The Red Army" +827,"。 將近75%的軸心國部隊以及絕多數原本佈署於法國和地中海中部的空軍部隊陸陸續續轉移到東線大衛·格蘭斯(David Glantz). The Soviet-German War 1941-1945: Myths and Realities: A Survey Essay. 2001年10月1日 [2012年4月21日查閱] .,這促使英國政府重新思考其大戰略計畫。同年7月英國和蘇聯一同組成反對德國的英蘇協定,同時英國和蘇聯也一起入侵伊朗,以確保波斯走廊(Persian Corridor)和伊朗當地油田不會落入德軍佔領之下。到了8月英國和美國則一同簽署《大西洋憲章》威廉·羅傑·路易斯(William Roger Louis). More Adventures with Britannia: Personalities, Politics and Culture in Britain. 美國奧斯丁: 德克薩斯州大學出版社(University of Texas Press). 1998年: 第223頁 [2012年4月21日查閱]. ISBN 978-0-292-74708-1 .。 到了" +828,"he Soviet-German War 1941-1945: Myths and Realities: A Survey Essay. 2001年10月1日 [2012年4月21日查閱] . By 1 November it had lost fully 20 percent of its committed strength (686,000 men), up to 2/3 of its 1/2-million motor vehicles, and 65 percent of its tanks. The German Army High Command (OKH) rated its 136 divisions as equivalent to 83 full-strength divisions. 克勞斯·萊因哈特(Klaus Reinhardt). Moscow: The Turning Point?: The Failure of Hitler's Strategy in the Winter of 1941-42. 英國牛津: Berg Publishers. 1992" +829,"《Military Affairs》. 1969年10月: 第312頁 [2012年4月21日查閱] .。同時因德國開始受到阻滯,日本放棄了北進。到了12月5日開始蘇聯發了大規模的反攻作戰,對佈署戰線長達1,000公里的德軍展開攻勢,並成功往西推進100公里—250公里之處。 ==== 太平洋战争爆发 ==== 德國1940到1941年在歐洲所獲得的一連串勝利,在某些程度上也鼓勵日本對歐洲各國政府施展壓力。已流亡英國的荷蘭政府同意繼續供應日本由荷屬東印度所製造的石油,但是拒絕將該殖民地的控制權移交給日本政府。相反的維琪法國則同意日本繼續占領法屬印度支那。到了1941年7月,美國、英國和其他西方國家政府決定以日軍試圖占領中南半島為由而凍結所有來自日本的資產,而提供日本近80%石油的美國也決定嚴格實施石油禁運政策。這項石油禁運政策促使得日本可能必須放棄其在亞洲的野心計畫、並且逐一暫緩對中國所發起的戰爭或者是以武力攻佔領土的方式來獲取所需要的自然資源。 對此日本軍方完全捨棄第一個方案,並且許多軍官認為石油禁運實際上就是對日本展開的潛在「宣戰」。 對於這種情勢,日本計劃迅速以武力的方式攻佔歐洲" +830,"閱]. ISBN 978-0-8133-4346-4 .。然而蘇聯本身出自利益考慮,並沒有完全遵守《聯合國宣言》的內容,除了繼續依照過去與日本簽署的協議而對於日軍保持中立的態度,同時本身對於自決原則也沒有表達正面支持。而從1941年史達林陸陸續續要求丘吉爾以及羅斯福在法國展開「第二戰線」拉瑞·里斯(Laurence Rees). World War Two: Behind Closed Doors: Stalin, the Nazis and the West. 英國: 埃伯瑞出版公司(Ebury Publishing). 2012年4月24日: 第99頁 [2012年4月21日查閱]. ISBN 978-1-4481-4045-9 .。其中東部戰線當時已經成為歐洲戰區的主要戰場,蘇聯在這次軍事衝突中有將近數百萬的傷亡者,相反的同樣作為同盟國成員的西方國家則大約損失數十萬人。不過丘吉爾和羅斯福則表示他們需要更多的準備時間,在其他同盟國始終無法確保優勢的情況下使得蘇聯只能夠繼續單獨抵抗德軍攻勢,這也使得蘇聯在戰後不斷以此來向西方國家要求賠償拉瑞·里斯(Laurence Rees). Wo" +831,"tory of the Royal Navy. 英國牛津: 牛津大學出版社. 2002年10月17日: 第362頁 [2012年4月21日查閱]. ISBN 978-0-19-860527-0 .。這段時間中盟軍對於日本唯一的取得勝利則是中華民國於1942年1月上旬爆發的第三次長沙戰役,而獲勝主要原因在於日本過度自信能夠輕易取下勝利,而對於國軍並無完善的準備。 面對美國海軍開始大力協助英軍運補作業,納粹德國海軍決定主動對美國大西洋沿岸所有運輸船隻展開攻擊約翰·古區(John Gooch). Decisive Campaigns of the Second World War. 英國倫敦: Routledge. 1990年9月1日: 第52頁 [2012年4月21日查閱]. ISBN 978-0-7146-3369-5 .。而儘管德軍已經造成蘇聯極大的人員與物資損失,歐洲軸心國成員停止對於主要佈署在俄羅斯中部和南部地區的蘇聯軍方展開攻勢,相反的開始強化自身對於所占領領土的控制能力。而在北非戰場方面,德軍於1月時對英國展開大規模攻勢,一直到2月初英軍才勉強在加查拉戰線中抵禦德軍進攻Andr" +832,"的主力;與此同時為了能夠吸引美軍的注意,日本也另外派遣部隊佔領隸屬於阿拉斯加的阿留申群島。日本軍方將大規模的攻擊計畫設定在6月初展開,然而美國於5月下旬時成功破解日本海軍的通訊密碼,在充分了解計劃內容和部隊安排後美軍成功憑藉著資訊優勢取得決定性的勝利,大日本帝國海軍在中途島戰役後也勢力大為削減許多。 在積極策劃的中途島戰役反而損失大量航空母艦後,日本改將重點放在自陸地上發起攻勢佔領位於巴布亞領地(Territory of Papua)的莫士比港。而美軍則是計畫對位在日本索羅門群島南部的軍事基地展開反擊,陸陸續續準備佈署兵力前往瓜達康納爾島,同時美軍也希望能夠另外發起攻勢打擊日本在東南亞重要基地的拉包爾阿德里安·吉爾伯特(Adrian Gilbert). Encyclopedia of Warfare: From Earliest Times...to the Present Day. Lyons Press. 2003年8月1日: 第259頁 [2012年4月21日查閱]. ISBN 978-1-59228-027-8 .。 美軍兩項計畫於7月開始展開,然而9月中旬時日本將瓜達康納爾島" +833,"查閱]. ISBN 978-1-84176-882-3 .。另外緬甸戰區的盟軍也派遣緬甸遠征軍特種部隊(Chindits)在日軍勢力範圍處展開騷擾作戰,並在4月下旬成功獲得些許戰果Martin Brayley. The British Army 1939-45 (3): The Far East . 英國牛津: Osprey Publishing. 2002年9月18日: 第9頁 [2012年4月21日查閱]. ISBN 978-1-84176-238-8 .。 ==== 东线 ==== 而位在德國東線戰場部分,軸心國集團成功在刻赤半島和卡爾可夫中擊敗蘇聯反攻安東尼奧·里德(Anthony Read). The Devil's Disciples: Hitler's Inner Circle. 美國紐約: 諾頓公司(W. W. Norton & Company). 2004年3月: 第764頁 [2012年4月21日查閱]. ISBN 978-0-393-04800-1 .。1942年6月時,德軍決定對於蘇聯南部地區發起大規模的藍色行動,希望藉此佔領高加索地區的油田與庫班沙地,同時繼續" +834,"下在1943年2月上旬德國陸軍已經難以繼續維持戰線,其中原本佔領斯大林格勒的德軍部隊宣告投降,而軸心國集團也將前線往回撤到夏季攻勢前的位置馬丁·吉爾伯特(Martin Gilbert). The Second World War: A Complete History. 美國紐約: 亨利·霍爾特出版公司(Henry Holt and Company). 2004年6月1日: 第397頁至第400頁 [2012年4月21日查閱]. ISBN 978-0-8050-7623-3 .。不過同樣在2月下旬,隨著蘇聯部隊的撤離行動反而讓德國對卡爾可夫發起攻勢,最後軸心國部隊在庫爾斯克建立了較為突出的前線陣地Harold Shukman. Stalin's Generals. 英國倫敦: Phoenix Books. 2002年3月: 第142頁 [2012年4月21日查閱]. ISBN 978-1-84212-513-7 .。 ==== 西欧/地中海 ==== 另一方面在1941年11月時盟軍於北非發起反攻,在十字軍行動(Operation Crusader)中成功收復原本德國和義大利的佔領地區" +835,"on. 美國紐約: 諾頓公司(W. W. Norton & Company). 1992年7月1日: 第178頁 [2012年4月21日查閱]. ISBN 978-0-393-00802-9 .。而在歐洲大陸這一部分,儘管同盟國戰略性派遣突擊隊攻佔迪耶普的重要設施,但最後仍然遭到擁有優勢兵力的德軍部隊所擊潰Jane Penrose. The D-Day Companion . 英國牛津: Osprey Publishing. 2004年4月26日: 第129頁 [2012年4月21日查閱]. ISBN 978-1-84176-779-6 .。這次作戰也使得同盟國部隊了解除非有更好的計畫策劃、武器裝備以及後續規畫,否則西方同盟國並無法發動大規模的入侵行動。 1942年8月,盟軍成功擊潰德軍對阿拉曼發起的第二次攻勢約翰·基瑾(John Keegan). Second World War. 英國倫敦: Pimlico. 1997年11月6日: 第277頁 [2012年4月21日查閱]. ISBN 978-0-7126-7348-8 .,但在面臨重大損失以及需要物資補給的情況下英軍發起基座行動" +836,"日查閱]. ISBN 978-0-7146-4634-3 .。雖然對於這違反停戰條約的軍事行動法軍並沒有抵抗,然而法國仍特意破壞自己的海軍艦隊以防止納粹德國將其收歸使用。隨後德意志非洲軍開始自突尼西亞當地撤離北非戰場,到了1943年5月同盟國軍隊則成功佔領該地保羅·柯里爾(Paul Collier). The Second World War (4): The Mediterranean 1940-1945 . 英國牛津: Osprey Publishing. 2003年2月19日: 第11頁 [2012年4月21日查閱]. ISBN 978-1-84176-539-6 .。 === 盟軍反擊 (1943年–1944年) === 瓜达尔卡纳尔岛战役結束之後,盟軍針對日本於太平洋地區的部隊開始展開反擊行動。1943年5月,同盟國軍隊前往阿留申群島與占領當地的日本部隊進行交戰。之後盟軍將其目標轉往拉包爾並開始嘗試佔領周遭島嶼以阻礙日軍的補給路線,同一時間也針對太平洋中部日軍所佔領的吉爾伯特及馬紹爾群島展開反擊大衛·甘迺迪(David M. Kennedy). Freedom from Fe" +837,"32252-1 .。在這其中部分原因是由於西方同盟國於7月9日對西西里島展開入侵行動,而在這之後一個月後同盟國部隊成功擊潰義大利軍方,並且開始大規模追捕墨索里尼。 1943年7月12日蘇聯則發起自己的反攻作戰,這次行動成功消除了位於東歐的德國部隊期望能夠繼續保持僵局戰況的期望。而自库尔斯克会战是以蘇聯勝利結束後,除了意味著德軍於東線的優勢逐漸消逝外Chris Bellamy. Absolute War: Soviet Russia in the Second World War. 美國紐約: Alfred A. Knopf. 2007年10月30日: 第595頁 [2012年4月21日查閱]. ISBN 978-0-375-41086-4 .,同時也給蘇聯東線戰場的主動權地位馬克·希利(Mark Healy). Kursk 1943: The Tide Turns In The East . 英國牛津: Osprey Publishing. 1992年5月28日: 第90頁 [2012年4月21日查閱]. ISBN 978-1-85532-211-0 .。雖然德國試圖匆忙強化佈署的(P" +838,"會共和國。而在意大利本土經過數場戰鬥後,一直到11月中旬西方盟國的主要部隊才開始與德軍設立的冬季防線交戰安東尼奧·里德(Anthony Read). Fall Of Berlin. 美國紐約: 哈欽森(Hutchinson). 1992年10月27日: 第129頁 [2012年4月21日查閱]. ISBN 978-0-09-175337-5 .。 而德軍與大西洋海上的作戰也開始受到阻撓,自1943年5月開始盟軍新開發的反潛設備成功阻擋德國潛艇的攻勢,而大量德軍潛艇損失也迫使德國海軍暫停在大西洋海上的攻擊行動Peter Padfield. War Beneath the Sea: Submarine Conflict During World War II. 美國霍博肯: 約翰威立. 1998年3月: 第335頁至第356頁 [2012年4月21日查閱]. ISBN 978-0-471-24945-0 .。1943年11月,富蘭克林·德拉諾·羅斯福、溫斯頓·邱吉爾與蔣中正於開羅召開會議,在會議上首次確認了有關日本所佔領土的解決。而稍後羅斯福、邱吉爾和史達林也則召開德黑蘭會議繼續討論戰後問" +839,"1-4 .。隨後蘇聯展開列寧格勒-大諾夫哥羅德攻勢以逼使德國北方集團軍撤離波羅的海地區,但是在納爾瓦戰役後成功佔領過去德國所統治的愛沙尼亞後短暫停止進攻,並且協助愛沙尼亞流亡政府重新掌握政權,而之後針對波羅的海國家作戰時蘇聯也多以這種模式處理大衛·格蘭斯(David Glantz). The Battle for Leningrad, 1941-1944. 美國勞倫斯(Lawrence): 堪薩斯大學出版社(University Press of Kansas). 2002年11月1日 [2012年4月21日查閱]. ISBN 978-0-7006-1208-6 .。1944年5月下旬,蘇軍成功攻佔克里米亞,這成功促使位在烏克蘭的軸心國部隊紛紛開始向西方後撤;然而在同一時間蘇聯入侵羅馬尼亞時,則反遭到當地駐守的軸心國部隊所擊退。而同盟國部隊亦在義大利戰場上獲得戰略性勝利,成功促使德軍部隊往北方防線撤離,到了6月4日時成功攻佔羅馬。 然而在亞洲戰區上同盟國部隊卻始終沒有獲得更加進一步的進展,其中1944年3月時日本決定從兩個地區展開反擊。首先針對位於英國於印度邊境地區發起烏號作戰,並且一" +840,"時宣告成功解放巴黎;而在整個下半年間,西方同盟國陸續逼迫其他駐守於西歐各國的德國軍隊撤離Stephen Badsey. Normandy 1944: Allied Landings and Breakout . 英國牛津: Osprey Publishing. 1990年1月25日: 第91頁 [2012年4月21日查閱]. ISBN 978-0-85045-921-0 .。但是儘管盟軍部隊從西歐往東進攻的計畫並無遇到大規模阻擋,然而當盟軍部隊企圖空降荷蘭當地的市場花園行動卻以失敗作結束,這使得盟軍原本打算另外自德國北部攻擊的計畫只能暫止。在此之後西方盟軍逐漸往德國逼近,並在嘗試穿越盧布河(Rur)時遭到德軍較大規模的攻勢阻擋。而原本計畫從義大利往北進攻的盟軍部隊,同樣被最後一個德軍所設下的哥德防線所阻擋著。 而在6月22日時,蘇聯則發起巴格拉基昂行動來向白俄羅斯展開主動進攻,並幾乎摧毀了德國中央集團軍的主要兵力史蒂芬·扎洛加(Steven Zaloga). Bagration 1944: The Destruction Of Army Group Centre . 英國牛津: Osp" +841,"的沙皇西美昂二世被驅逐,攝政王基里尔王子則被槍決。隨後這蘇軍佔領這兩個國家便宣告加入同盟國的行列。 1944年9月蘇聯部隊提前進入南斯拉夫,這項作法一方面成功逼使佔領希臘的德國部隊迫迅速撤離外,另一方面也促使阿爾巴尼亞和南斯拉夫的軸心國部隊遭到孤立麥斯·海斯廷斯(Max Hastings). The Second World War: A World In Flames . 英國牛津: Osprey Publishing. 2004年3月25日: 第223頁至第224頁 [2012年4月21日查閱]. ISBN 978-1-84176-830-4 .。為此自1941年開始領導共產黨游擊隊的約瑟普·布羅茲·狄托開始進行更大規模的游擊行動,最終成功掌控南斯拉夫境內並且阻止德國部隊的進佔。另外蘇聯也提供少數資源協助在塞爾維亞北部的游擊隊反抗塞爾維亞救國政府部隊,最終兩者在10月20日時一同解放首都貝爾格勒。幾天過後蘇聯發動大規模進攻,並於1945年2月攻陷德國邊境並占領匈牙利的重要城鎮布達佩斯Andrew Wiest和M. K. Barbier. Strategy and Tactics I" +842,". 1995年10月1日 [2012年4月21日查閱]. ISBN 978-0-88740-806-9 .。 而自7月開始,位在東南亞的英國部隊成功擊退日本發起的大規模攻勢,同時將日軍逼往欽敦江地區駐守,而中國部隊也成功奪回密支那丹尼爾·馬斯頓(Daniel Marston). The Pacific War Companion: From Pearl Harbor to Hiroshima . 英國牛津: Osprey Publishing. 2005年6月12日: 第120頁 [2012年4月21日查閱]. ISBN 978-1-84176-882-3 .。但是在中國本土則遭到日本嚴重打擊,日軍在6月中旬時成功攻下長沙、並在8月初佔領衡陽Philip Jowett. Japanese Army 1931-45 . 英國牛津: Osprey Publishing. 2002年1月25日: 第8頁 [2012年4月21日查閱]. ISBN 978-1-84176-353-8 .。不久之後日軍南下入侵廣西一帶,在11月底時成功擊敗中國軍隊主力並且佔領桂林與柳州一帶。而到了12月底時,日本" +843,"nes Offensive, 1944-1945 ISBN 978-0-306-81391-7 .Danny Parker. Battle of the Bulge: Hitler's Ardennes Offensive, 1944-1945 . 美國劍橋: Da Capo Press. 2004年11月30日: 第68頁至第72頁 [2012年4月21日查閱]. ISBN 978-0-306-81391-7 .Danny Parker. Battle of the Bulge: Hitler's Ardennes Offensive, 1944-1945 . 美國劍橋: Da Capo Press. 2004年11月30日: 第329頁至第330頁 [2012年4月21日查閱]. ISBN 978-0-306-81391-7 .。但是在義大利戰場上,盟軍部隊仍然遲遲無法擊敗德軍所佈署的防線。1945年1月中旬蘇聯從波蘭維斯瓦河發起進攻,很快的蘇聯軍方便往前推進到德國奧德河處,並且成功佔領東普魯士。而在2月4日時,美國、英國與蘇聯領導高層展開雅爾達會議;除了同意德國投降後將採取分區統" +844,"ent)成員逮捕並擊斃。而在兩天之後希特勒則自殺身亡,德國政府則改由卡爾·鄧尼茨所繼任安恩·科修(Ian Kershaw). Hitler: 1936-1945: Nemesis. 美國紐約: 諾頓公司(W. W. Norton & Company). 2001年9月: 第823頁 [2012年4月21日查閱]. ISBN 978-0-393-32252-1 .。而軸心國部隊也開始紛紛向盟軍投降,其中在4月29日時駐意大利德軍正式投降,5月7日美英法苏四国攻入柏林后德國政府則前往漢斯簽署《德國無條件投降書》;不過正式的文件則是5月8日於柏林重新簽署,5月8日這一天也因此訂定為第二次世界大戰歐戰勝利紀念日。不過蘇聯在簽署投降書後仍然遭到德國中央集團軍反擊,並一直到5月11日才宣告結束大衛·格蘭斯(David Glantz)和強納森·侯斯(Jonathan M. House). When Titans Clashed: How the Red Army Stopped Hitler. 美國勞倫斯(Lawrence): 堪薩斯大學出版社(University Press of Kansas)" +845,"對日本本土施加壓力,除了派遣大量轟炸機炸毀日本主要城鎮外,同時美軍潛艇也在日本近海封鎖其對外的物資進口Michel Thomas Poirier. RESULTS OF THE GERMAN AND AMERICAN SUBMARINE CAMPAIGNS OF WORLD WAR II . 美國海軍. 1999年10月20日 [2012年4月21日查閱] .。 7月11日時,盟軍領導人前往德國召開波茨坦會議。其中在《波茨坦協定》中除了確認有關先前簽署的德國戰後處理外,同時也重申日本必須接受無條件投降的要求。不過在這次會議之中由於英國進行國會大選,使得英國首相改由艾德禮接替邱吉爾的職位。但是由於日本遲遲沒有正面回應《波茨坦協定》的內容,美國政府決定對廣島市以及長崎市展開核子轟炸。與此同時蘇聯則依照雅爾達會議的內容進攻日本控制的偽滿洲國,並且順利擊敗擁有日本最大規模兵力的關東軍,此外還佔領了庫頁島與千島群島等地。1945年8月15日日本宣布投降,並於9月2日在美國最後一艘戰列艦密蘇里號甲板上簽署《降伏文書》,這也意味著第二次世界大戰正式宣告結束。 == 战後 == 第二次世界大戰結束後," +846,"N 978-0-671-72868-7 .、芬蘭東部以及3個波羅的海國家中獲得部分土地Caroline Kennedy-Pipe. Stalin's Cold War: Soviet Strategies in Europe, 1943 to 1956. 英國曼徹斯特: 曼徹斯特大學. 1995年5月 [2012年4月21日查閱]. ISBN 978-0-7190-4201-0 .,但這也至少讓近2萬名波蘭民眾被迫遠離自己的住家。 參戰雙方不論戰勝或戰敗皆為戰爭已經付出極為慘痛的代價。同盟國集團為了能夠真正維持之後世界秩序的和平,於1945年10月24日正式成立聯合國聯合國歷史 . 聯合國 [2012年4月21日查閱].,並在1948年通過《世界人權宣言》來作為各個會員國的共同標準。不過於第二次世界大戰中獲得勝利的國家在聯合國獲得龐大權力,其中美國、蘇聯、中華民國、英國和法國成為聯合國安全理事會的常任理事國。雖然今日聯合國的常任理事國所提到的地區仍然沒有改變,但實際上管轄的政權則有所變化。其中中國席位則在1971年所通過的聯合國大會2758號決議中,確認由中華人民共和國代表中華民國原席" +847,"istorical Journal). 1992年9月: 第641頁至第643頁 [2012年4月21日查閱] .。很快戰後世界便分成是以美國為首的北大西洋公約組織與以蘇聯為首的華沙條約組織兩個正式的國際軍事同盟,雙方隨即陷入長期政治緊張和軍事競爭的冷戰時期,同一時間也促使了軍備競賽以及代理戰爭概念的出現理察·霍姆斯(Richard Holmes). The Oxford Companion to Military History. 英國牛津: 牛津大學出版社. 2003年8月28日 [2012年4月21日查閱]. ISBN 978-0-19-860696-3 .。冷战让全世界都笼罩在核战争的阴影下,美国和欧洲盟国经常举行核战争演习,长期的军备竞赛严重影响各国经济增长,且东西方的民间交流和往来被阻隔长达半个世纪,这些情况直到1991年苏联解体后才得以改变。 在亞洲地區方面同盟國則達成協議蘇聯能夠吞併庫頁島和千島群島,而由美國領導戰後日本的發展以及統轄過去日本所佔領的西太平洋島嶼。但另一方面過去由日本統治的朝鮮,則在盟軍託管時期以38度線劃分成蘇聯管轄的北部地區和美國統轄的南部地區。之後" +848,"2年4月21日查閱]. ISBN 978-0-14-026561-3 .約翰·達爾文(John Darwin). After Tamerlane: The Rise and Fall of Global Empires, 1400-2000. 英國西敏: 企鵝出版集團. 2008年3月6日: 第441頁至第443頁 [2012年4月21日查閱]. ISBN 978-0-14-101022-9 .約翰·達爾文(John Darwin). After Tamerlane: The Rise and Fall of Global Empires, 1400-2000. 英國西敏: 企鵝出版集團. 2008年3月6日: 第464頁至第468頁 [2012年4月21日查閱]. ISBN 978-0-14-101022-9 .。大英帝国、葡萄牙帝国等老牌欧洲殖民帝国也在这一时期逐渐瓦解。 受到戰爭的影響導致全球各國或多或少都受到影響,其中與其他國家來講美國的經濟並沒有受到嚴重威脅,也進而促使戰後嬰兒潮的出現。而在1950年時美國人均國內生產總值的發展比其他政府還要來得高,同時美國也有更多能力來主導" +849,". 1967年1月1日: 第263頁 [2012年4月21日查閱]. ISBN 978-0-8018-0530-1 .Nicholas Balabkins. Germany Under Direct Controls: Economic Aspects of Industrial Disarmament, 1945-1948. 美國皮斯卡特維: 羅格斯大學出版社(Rutgers University Press). 1964年6月: 第208頁至第209頁 [2012年4月21日查閱]. ISBN 978-0-8135-0449-0 .。一直到1948年中期西德進行貨幣改革政策時逐漸恢復其經濟發展,同時馬歇爾計劃也成功促使歐洲經濟政策的自由化Nicholas Balabkins. Germany Under Direct Controls: Economic Aspects of Industrial Disarmament, 1945-1948. 美國皮斯卡特維: 羅格斯大學出版社(Rutgers University Press). 1964年6月: 第212頁 [2012年4月2" +850,"多战争罪行,其中包括猶太人大屠殺、南京大屠殺、肅清大屠殺、卡廷大屠殺、馬尼拉大屠殺、日軍對中國軍民進行細菌戰,以及最终美国對日本首次使用原子彈也造成了大量平民伤亡。 有關戰爭所導致的總傷亡人數各方估計皆不相同,因為很多人在因故死亡後並沒有留下相關記錄。絕大多數資料估計大約有6,000萬人在戰爭中喪生,其中包括約有2,000萬名士兵和4,000萬名平民,其中許多平民分別死於傳染病、飢餓、大屠殺、轟炸和蓄意的種族滅絕政策馬修·懷特(Matthew White). Source List and Detailed Death Tolls for the Primary Megadeaths of the Twentieth Century . Historical Atlas of the Twentieth Century. 2011年2月 [2012年4月21日查閱] .。根據估計損失最大的蘇聯在戰爭中失去將近2,700萬人,其中包括有870萬名軍事人員和1,900萬名平民喪生[1941年初苏联人口统计数为1.967亿(包括新兼并地区),而1946年仅余1.67亿,因此苏联的实际死亡人数" +851,"生人數大約有15%,而剩餘的85%則是同盟國部隊的喪生人數;在這之中主要是蘇聯和中國的人民,其中許多人是由於德國和日本部隊於占領區實施的戰爭罪行所導致的。根據估計大約有1,100萬名至1,700萬名平民直接或者間接死於納粹思想的政策中,其中包括大約有600萬名猶太人因而遭遇系統性的種族滅絕式大屠殺,另外還有500萬名羅姆人、同性戀者、斯拉夫人以及其他種族與少數族群也遭到殺害艾倫·陶德(Allan Todd). The Modern World. 英國牛津: 牛津大學出版社. 2001年6月21日: 第121頁 [2012年4月21日查閱]. ISBN 978-0-19-913425-0 .。此外南斯拉夫烏斯塔沙則與軸心國集團合作殺害大量作為少數民族的塞爾維亞人、羅姆人和猶太人,而在戰爭結束後克羅埃西亞平民則對此展開報復。另外大約有300萬名到1,000萬名中國平民則是在日本佔領地區及双方交战区域因各种原因非正常死亡,在這之中最有名的事件則是南京大屠殺,其中有數十萬名中國军民因而遭到殺害張純如. 《南京暴行:被遺忘的大屠殺》(The Rape Of Nanking: The Forgot" +852,"y. 美國聖塔芭芭拉: ABC-CLIO. 2004年12月17日: 第319頁 [2012年4月21日查閱]. ISBN 978-1-57607-999-7 .,而大日本帝國陸軍則在與蘇聯的早期衝突、入侵並佔領中國時也使用生物或化學武器協助作戰斯賓塞·塔克(Spencer C. Tucker). The Encyclopedia of World War II: A Political, Social, and Military History. 美國聖塔芭芭拉: ABC-CLIO. 2004年12月17日: 第320頁 [2012年4月21日查閱]. ISBN 978-1-57607-999-7 .。此外德國和日本也針對平民施放藥劑來測試武器效果羅伯特·沙貝拉(Robert Sabella)、劉中達和林培瑞. Nanking 1937: Memory and Healing. 美國阿蒙克: M. E. Sharpe. 2001年11月: 第69頁 [2012年4月21日查閱]. ISBN 978-0-7656-0816-1 .,在某些情況​​下也於戰俘身上進行實驗。 雖然許多軸心國" +853,"盟國總共造成至少160個城市遭到轟炸,也導致超過60萬名德國平民因而死亡。 === 集中营、奴役和屠杀 === 在納粹大屠殺中主要有600萬名阿什肯納茲猶太人遭到殺害,另外在蓄意滅絕計劃中也導致200萬名波蘭人民以及400萬名包括身心障礙、精神病患、蘇聯戰俘、同性戀者、共濟會成員、耶和華見證人與羅姆人等「不值得活的生命」遭到殺害。另外以東歐為主有1,200萬名民眾被迫成為勞工,並且強迫在德國各家工廠內進行勞動。 而除了納粹統治下設立的集中營導致大量人員遭到殺害,而古拉格於各地設立的勞動營也導致德國戰俘以及包括波蘭、立陶宛、拉脫維亞與愛沙尼亞等占領區公民喪生,甚至一些蘇聯公民也因為被懷疑與納粹主義有關而遭到迫害。根據統計在戰爭期間有60%的德國戰俘於蘇聯喪生,而理查·奧弗里(Richard Overy)則表示有將近570萬名戰俘喪生,其中有大約有57%、約360萬人因故喪生或者遭到擊斃理查·奧弗里(Richard Overy). The Dictators: Hitler's Germany and Stalin's Russia. 美國紐約: 諾頓公司(W. W. Norton & C" +854,"爾德: 西景出版社(Westview Press). 1997年12月17日: 第2頁至第3頁 [2012年4月21日查閱]. ISBN 978-0-8133-2718-1 .。然而在日本投降後所有獲釋的囚犯中,有37,583名來自英國、28,500來自荷蘭以及14,473人來自美國,但中國囚犯僅有56人未被殺害而成功獲釋赫伯特·畢克斯(Herbert P. Bix). 《裕仁天皇與近代日本的形成》(Hirohito and the Making of Modern Japan). 美國紐約: Harper Perennial. 2001年9月4日: 第360頁 [2012年4月21日查閱]. ISBN 978-0-06-093130-8 .。根據統計在1935年到1941年間,在興亞局的規畫下有至少500萬名來自中國北部和滿洲國的平民被迫勞動,這包括在礦山以及軍工廠內勞動;而在1942年以後,日軍強迫中國民眾勞動的人數更迅速增加到1,000萬人之譜Zhifen Ju. 「Japan』s Atrocities of Conscripting and Abusing North Chin" +855,"搬離夏威夷等重要地區Roger Daniels. Two Reports on Japanese Canadians in World War Two. 加拿大渥太華: 總理辦公室(Office of the Prime Minister). 1979年6月: 第23頁 [2012年4月21日查閱]. ISBN 978-0-405-11266-9 .。隨後美國與加拿大政府陸續下令拘留150,000名日裔美國人,另外有14,000名意大利裔和德裔美國人同樣為了安全理由而被迫拘留。此外在戰爭即將結束之際,同盟國集團在雅爾達會議上允諾蘇聯能夠要求數百萬名戰俘與平民強制勞動,甚至匈牙利一直到1955年仍被迫繼續為蘇聯進行勞動Tamás Stark. 「Malenki Robot」 – Hungarian Forced Labourers in the Soviet Union (1944–1955) . Minorities Research Group [2012年4月21日查閱] .。 === 軍事佔領 === 在歐洲大陸,軸心國集團的佔領行動分成兩種非常不同的形式。在包括法國、挪威、丹麥" +856,"織出現,但一直到1943年底以前他們並沒有顯著阻礙德國的軍事行動Thomas R. Christofferson和Michael S. Christofferson. France during World War II: From Defeat to Liberation. 美國布朗克斯: 福坦莫大學出版社(Fordham University Press). 2006年5月15日: 第156頁 [2012年4月21日查閱]. ISBN 978-0-8232-2563-7 .。 另一方面在亞洲地區,日本不斷向其所佔領的國家宣傳有關大東亞共榮圈的構想,並且將日本的霸權主義塑造成是為了能解放居住於殖民地的人民Aiko Ikeo. Economic Development in Twentieth-Century East Asia: The International Context. 英國倫敦: Routledge. 1997年1月15日: 第107頁 [2012年4月21日查閱]. ISBN 978-0-415-14900-6 .。但儘管最初許多遭到歐洲殖民統治的地區將日軍作為解放者" +857,",其國內生產總值卻僅僅只高出日本89%左右。而隨著日本對外擴展殖民地後中國人口优势下降到只剩三倍,同一時間日本的經濟成長速度使得中國僅高出38%之譜。 不過儘管同盟國有著經濟和人口的優勢,德國和日本最早展開的快速襲擊行動成功降低這些因素的影響力。一直到1942年美國和蘇聯陸續加入同盟國集團後,優勢性的經濟實力與人口數量成為戰爭的關鍵,隨著戰爭的發展開始雙方開始走向消耗戰的趨勢。此外盟軍最終能夠贏過軸心國部隊的原因,除了同盟國能夠比起軸心國獲得更多自然資源外,其他如德國和日本不願投入婦女作為勞動力、盟軍戰略性轟炸以及德國戰爭後期經濟瓦解也影響到勝負結果馬舍爾·曉士(Matthew Hughes)和克里斯·曼(Chris Mann). Inside Hitler's Germany: Life Under the Third Reich. Potomac Books. 2000年10月: 第151頁 [2012年4月21日查閱]. ISBN 978-1-57488-281-0 .查爾斯·格里菲思(Charles Griffith). The Quest: Haywood Hansell a" +858,"各地強迫將近1,800萬人投入生產行列。 === 科技與戰爭的發展 === 第二次世界大戰的爆發促使許多軍事科技的快速發展,這包括有航空、船艦以及陸上武器的發展與應用改革。其中在航空武器方面,各式軍機開始陸續擔負偵查、戰鬥、轟炸以及地面支援等任務,同時隨著任務角色的確定促使飛機開始專業化發展。同時對於飛機於戰場的任務性質更加多元,例如開始投入運輸機協助快速運輸高價值性的物資、裝備以及人員等斯賓塞·塔克(Spencer C. Tucker). The Encyclopedia of World War II: A Political, Social, and Military History. 美國聖塔芭芭拉: ABC-CLIO. 2004年12月17日: 第76頁 [2012年4月21日查閱]. ISBN 978-1-57607-999-7 .,或者是針對平民區與工業區的戰略轟炸來嘗試瓦解對方的士气等。而隨著針對地面設施的轟炸漸趨頻繁,相關的防空武器也陸陸續續改進或者開發,這包括有以雷達、高射炮與面對空飛彈為主的防禦系統架構,或者是德軍88毫米高射炮的開發等。此外噴射機也在第二次世界大" +859,"RS: THE WORLD'S GREATEST NAVAL VESSELS AND THEIR AIRCRAFT.. Silverdale Books. 2004年: 第7頁 [2012年4月21日查閱]. ISBN 978-1-84509-079-1 .H. Avery Chenoweth和Brooke Nihart. Semper Fi: The Definitive Illustrated History of the U.S. Marines. 美國紐約: 史特靈出版社(Sterling Publishing). 2005年10月3日: 第180頁 [2012年4月21日查閱]. ISBN 978-1-4027-3099-3 .。而大西洋護航航空母艦的出現證實是盟軍運輸船隊能夠有效抵禦德國海軍的一部分,除了能夠擴展四周的保護半徑外,也能夠有效封鎖整個中大西洋海域的通行Ian Sumner. The Royal Navy 1939-45 . 英國牛津: Osprey Publishing. 2001年10月25日: 第25頁 [2012年4月21日查閱]. ISBN 978-1" +860,"從第一次世界大戰所採取的靜態前線,改往更加強調移動性能和兵種合作的戰鬥模式。而在第一次世界大戰作為火力支援的坦克,則在第二次世界大戰中演變成為主要作戰武器之一斯賓塞·塔克(Spencer C. Tucker). The Encyclopedia of World War II: A Political, Social, and Military History. 美國聖塔芭芭拉: ABC-CLIO. 2004年12月17日: 第125頁 [2012年4月21日查閱]. ISBN 978-1-57607-999-7 .。其中從1930年代後期到整個第二次世界大戰期間,坦克的設計相較過去更為先進許多,同盟國與軸心國不斷嘗試為自己的坦克改良速度、裝甲和火力等重點項目崔佛·迪皮伊(Trevor N. Dupuy). Evolution of Weapons and Warfare. 英國倫敦: 詹氏資訊集團. 1982年3月18日: 第231頁 [2012年4月21日查閱]. ISBN 978-0-7106-0123-0 .。而在戰爭剛爆發時大多數指揮官認為遭遇具有完整配備的坦克時應該要避免作" +861,"99-7 .。儘管在整個戰爭期間大多數步兵的配備仍然與第一次世界大戰並無重大差別,但是戰爭促使如能夠便利攜帶的MG42通用機槍等機槍設計、以及適合城鎮或叢林作戰的衝鋒槍類型的快速發展。而戰爭後期所發展的突擊步槍則結合了步槍與衝鋒槍的許多功能,並且在第二次世界大戰結束成為許多武裝部隊的標準武器之一Oliver Sprague和Hugh Griffiths. The AK-47: the world's favourite killing machine . Control Arms Briefing Note. 2006年6月26日 [2012年4月21日查閱] .。 另外在第二次世界大戰期間為了能夠獲得優勢,陸陸續續投入自身的科技協助作戰。例如大多數主要交戰國家開始不斷試圖破解對方複雜的密碼,並且嘗試透過密碼本等設備強化自己的通訊安全。例如在()的技術協助下英國成功破解恩尼格瑪密碼機的密碼ULTRABen Macintyre. Bravery of thousands of Poles was vital in securing victory. 英國瓦平(Wapping): 《泰晤士" +862,"進行分析的作業研究以及協助物資運輸的桑椹碼頭(Mulberry harbour, 人工港口)與冥王作戰(Operation Pluto, 英吉利海峽下的輸油管道)等KONRAD ZUSE (1910-1995) . IDSIA [2012年4月21日查閱] . Konrad Zuse builds Z1, world's first program-controlled computer. Despite certain mechanical engineering problems it had all the basic ingredients of modern machines, using the binary system and today's standard separation of storage and control. Zuse's 1936 patent application (Z23139/GMD Nr. 005/021) also suggests a von Neumann architecture (re-invented in 1945) wit" +863,"3-32252-1. * * * * Jeffery Record. Appeasement Reconsidered: Investigating the Mythology of the 1930s . DIANE Publishing. 2005年. ISBN 978-1-58487-216-0. * * * Philip Jowett. Japanese Army 1931-45. 英國牛津: Osprey Publishing. 2002年1月25日. ISBN 978-1-84176-353-8. * == 外部連結 == * Aerial Propaganda Leaflet Database * Source List and Detailed Death Tolls for the Primary Megadeaths of the Twentieth Century * The Art of War * The Soviet-German War 1941-1945: Myths and Realities: A Survey Essay * The World W" +864,"tegory:智利战争 Category:哥倫比亞戰爭 Category:哥斯大黎加戰爭 Category:克羅埃西亞戰爭 Category:古巴战争 Category:捷克斯洛伐克戰爭 Category:丹麥戰爭 Category:多明尼加戰爭 Category:厄瓜多戰爭 Category:埃及戰爭 Category:薩爾瓦多戰爭 Category:爱沙尼亚战争 Category:衣索比亞戰爭 Category:芬兰战争 Category:法国战争 Category:纳粹德国战争 Category:希臘戰爭 Category:瓜地馬拉戰爭 Category:海地戰爭 Category:宏都拉斯戰爭 Category:匈牙利战争 Category:冰島戰爭 Category:印尼战争 Category:意大利戰爭 Category:伊朗戰爭 Category:伊拉克戰事 Category:日本战争 Category:哈薩克斯坦戰爭 Category:寮國戰爭 Category:拉脱维亚战争 Category:黎巴嫩战争 Category:賴比瑞亞戰爭 Category:立陶宛战争 C" +865,"辽宁省,简称辽,舊稱奉天省,是中华人民共和国东北地区的省份,省會位于沈阳市。省政府駐沈阳市皇姑区北陵大街45号。 辽宁省东北与吉林省接壤,西北与内蒙古自治区为邻,西南与河北省毗连,是连接华北地区与中国东北地区的要道。辽宁省东南以鸭绿江为界河与朝鲜隔江相望,南临黄海和渤海。辽宁在自古以來便是通往关外的軍事要隘,也是將辽东地区聯繫内蒙古通向歐亞國家為交通上所必經之要道。是环渤海经济区的重要组成部分。 == 历史 == thumb|left|喀左县东山嘴遗址出土的红山文化孕妇陶塑像,学者认为体现了女神崇拜 === 史前至秦汉 === 营口出土的金牛山文化表明早在28万年前,辽宁省就有人类在此活动。辽宁在春秋战国时期为燕地。秦始皇统一六国之后,又在此地建立了辽东、辽西、右北平郡。西汉时隶属于幽州,增设玄菟郡。东汉增设辽东属国。 === 魏晋南北朝 === 三国时襄平城公孫延家族镇守辽宁和朝鲜半岛。经过公孫延、公孫度、公孫康、公孫恭、公孫淵五代人的经营辽东,不斷開闢疆土,東伐高句麗,西擊烏丸,北伐扶余,越海攻東萊。領土擴張後分割遼東郡,辽东王公孙家族238年春被司馬懿和高句麗王高位宫降服,纳入魏" +866,"督战。唐军攻下辽东后,接着攻打安市城(今辽宁海城),自六月二日至九月十八,历时三月没有攻下。唐高宗時期,668年高句丽被唐朝灭后,唐分其境为九都督府、四十二州、一百县,并于平壤设安东都护府以统之,任命右威卫大将军薛仁贵为检校安东都护,领兵二万镇守其地,试图控制朝鲜半岛。但遭到了统一新罗的反击。新罗拥立投降的渊净土的儿子安胜为高句丽王(史称报德王),赏赐金姓。并协助高句丽移民策动叛乱。经过数年的反唐战争, 新罗最终控制朝鲜半岛大同江以南地区。大同江以北则由唐和后来崛起的渤海国占据。唐玄宗天宝十四载十一月甲子日(755年12月16日星期二),身兼范阳、平卢、河东三节度使的安禄山在范阳起兵,史稱安史之亂。 五代时辽宁地渐渐被南下的契丹占领,后唐后成为辽朝的核心部分及发源地。 === 辽金元 === * 辽朝分属于东京道、上京道、中京道、下京道。 * 金朝时大部分地区属东京路统辖,其余分属咸平路、上京路、北京路。 * 元朝东部地区属辽阳行省的辽阳路、沈阳路;北部地区属开元路咸平府;西部地区的广宁路隶属辽阳行省;锦州、朝阳地区属大宁路,隶属于中书省。 === 明清 === * 洪武年间,明朝在辽" +867,"一,战争期间日军攻陷旅顺,执行旅顺大屠杀,对城内进行4天3夜的抢劫、屠杀和强奸,超过二万人遇害,屠杀过后全市仅剩三十六人被留下埋葬居民尸体,遇难者葬于白玉山东麓的安葬岗,今称“万忠墓”。 === 中华民国及滿洲國 === 1922年5月11日,徐世昌颁布大总统令,解除张作霖东三省巡阅使、蒙疆经略使、奉天督军兼省长。然而5月12日,张作霖宣布东三省独立。随后奉天省议会推举张为东三省总司令兼奉天省长,孙烈臣、吴俊陞为副总司令,张作霖开始全面控制奉天省。1929年东北易帜后归顺南京国民政府。由於考量到「奉天」取自「奉天承運」,故而提出改名,並列出了五个候选名称:辽东省、沈阳省、安奉省、关东省、辽宁省。1929年2月5日,南京国民政府颁布第91号训令,取「辽河流域永远安宁」之意,将「奉天省」改名为「辽宁省」,自3月1日起实行。同年4月,在签发「司令长官咨为本省改辽宁二字」為題的公文中,东北边防司令长官张学良寫道:「东北边防军司令长官公署为咨复事,案准政府咨开查本省改名辽宁业经通令布告在案。」 此为辽宁得名的开始,但当时民間仍习惯稱「奉天省」。當時該省省會瀋陽,全省辖五十九县。 1931年,九一" +868,"及清原、兴京、抚顺一部、铁岭等县,组成新的辽宁省分委、辽宁军区。 1945年10月12日,东北局宣布组建中共辽宁省工委、安东省委、沈阳市委,并任命了主要领导干部:辽宁省工委书记为陶铸,副书记兼组织部长白坚,组织部副部长饶斌,民运部长张逢时,辽宁省政府党团书记兼省政府副主席朱其文,辽宁省政府主席兼辽宁省保安司令张学思,辽宁省工业管理委员会主任兼党团书记吕东;辽宁省工委委员为陶铸、白坚、张学思、朱其文、饶斌、张逢时、吕东、孔原、陈郁、汪金祥、邓华。1945年11月苏联红军要求中共党政军势力撤出沈阳等大城市,移交给国民政府东北行营。1945年11月25日,东北局决定陶铸和邓华带部分干部和部队去法库,组成辽西省委和辽西省军区;张学思和辽宁省工委书记白坚带另一部分干部和部队到本溪,组成辽宁省委、辽宁省政府和辽宁省军区,张学思任辽宁省委常委、辽宁省政府主席、辽宁省军区司令员,白坚任辽宁省委书记和辽宁省军区政委。11月26日,张学思率领辽宁省政府机关向本溪出发,转而在本溪办公。1945年12月18日辽宁省政府在本溪发布通令,公布了辽宁省行政区划:全省设置1个行署区(辽西行署区,驻法库,辖锦州市和新民" +869,"省委、辽北省政府(驻东丰)和中共通化省委、通化行署的辖区,以及清源、兴京、抚顺、铁岭部分地方,划为新的辽宁省辖区,在梅河口成立中共辽宁省分省委,隶属于中共辽东省委(即南满分局)。辽宁省府、辽宁省军区驻梅河口;辽宁省分委委员为白坚、张学思、刘汉生、刘惠农、解沛然、万毅、周赤萍、王铮、刘东元、连柏生、杜者蘅等11人,白坚、张学思、刘汉生、解沛然、刘东元为常委;其中,张学思任辽宁省军区司令员,解沛然任副司令员,白坚任辽宁省军区政委,刘惠农任省军区副政委兼政治部主任。1946年5月16日正式成立辽宁省政府。1946年5月4日驻地由梅河口迁至通化、临江,1947年6月又迁回梅河口。1948年7月辽宁省政府与辽南行署合并,辖区划归辽北省和安东省。辽宁省下辖: *第一专署(通化):1946年5月,通化行政公署改建为通化专署,领导通化县、临江、长白、靖宇、抚松、辑安、柳河、辉南等县与通化市。1946年7月21日,辽宁省政府兼通化专署。1946年11月,在临江重建通化专署,改称辽宁一专署,领导通化县、通化市和辑安、柳河县政府。1947年6月迁驻通化。1948年3月撤销。 *第二专署(西安):1946年5" +870,"大连市合并改旅大直辖市)、沈阳直辖市、鞍山直辖市、抚顺直辖市、本溪直辖市,1954年合并恢复辽宁省至今,以沈阳市为省会。 * 1955年 苏军撤离大连,苏联将大连及旅顺的驻军、防务等主权归还中华人民共和国。 * 1956年将原热河省东部朝阳等六个县划归辽宁。 * 1969年,昭乌达盟并入辽宁省,1979年再次划回内蒙古自治区。 * 1975年,海城、营口县一带发生7.3级大地震,死亡1328人,重伤4292人,轻伤12688人,是人类历史上迄今为止,在正确预测地震的基础上,由官方组织撤离民众,明显降低损失的唯一成功案例。 * 1981年,旅大市改名为大连市。 == 地理 == 地理坐标位于东经118°53′至125°46′北纬38°43′至43°36′之间,东西之间宽约550KM,南北之间宽约550KM。 辽宁省的陆地面积大约是14万平方公里,占中国陆地面积的1.5%。陆地面积的59.8%是山地,占8.72万平方公里,平地面积为4.87万平方公里,占33.4%,水域面积1万平方公里,占总面积的6.8%,其余为海域,其中黄海面积为7.19万平方公里;渤海面积为7.83万平方公里。 海岸线" +871,"成海拔50米的狭长平原,与渤海相连,其间为辽西走廊。西部山地丘陵面积约为4.2万平方公里,占全省面积29%。 # 中部平原。由辽河及其30余条支流冲積而成辽河平原,面积为3.7万平方公里,占全省面积25%。地势从东北向西南由海拔250米向辽东湾逐渐倾斜。辽北低丘区与内蒙古接壤处有沙丘分布,辽南平原至辽东湾沿岸地势平坦,土壤肥沃,另有大面积沼泽洼地、漫滩和许多牛轭湖。 === 河流 === 本省境内有大小河流390多条,总长约16万公里。主要有辽河、浑河、大凌河、太子河、绕阳河以及中朝两国共有的界河鸭绿江等,形成本省的主要水系。境内大部分河流自东、西、北三个方向往中南部汇集注入渤海。 === 水库 === 本省有多个大型水库,较为著名的有抚顺大伙房水库、辽阳参窝水库、铁岭清河水库、盖州石门水库等。 === 海洋 === 本省海域广阔,辽东半岛的东侧临黄海,西侧环渤海,是中国水温最低,纬度最高的海域。海域面积15万平方公里,沿海滩涂26.8万公顷。陆地海岸线东起鸭绿江江口、西至山海关老龙头。 本省近海岛屿506个,面积1921平方公里,占中国海洋岛屿总面积的 0.24%,岛岸线全长628公" +872,"市昌图县三江口镇 东 本溪市桓仁满族自治县二棚甸子镇 南 大连市旅顺口区铁山街道 西 朝阳市凌源市大河北镇 中心 盘锦市盘山县沙岭镇 == 行政区划 == 辽宁省现辖14个地级市,下设59市辖区、16县级市、17县、8自治县;1354个乡级行政单位,包括513街道,640镇,147乡,54民族乡。 * 地级市:沈阳市(行政级別为副省级)、大连市(行政级別为副省级)、鞍山市、抚顺市、本溪市、丹东市、锦州市、辽阳市、营口市、阜新市、盘锦市、铁岭市、朝阳市、葫芦岛市 辽宁省行政区划图 辽宁省行政区划图 辽宁省行政区划图 辽宁省行政区划图 辽宁省行政区划图 辽宁省行政区划图 辽宁省行政区划图 辽宁省行政区划图 辽宁省行政区划图 辽宁省行政区划图 区划代码 区划名称 汉语拼音 面积(平方公里) 常住人口(2020年普查) 政府驻地 县级行政区划 县级行政区划 县级行政区划 县级行政区划 区划代码 区划名称 汉语拼音 面积(平方公里) 常住人口(2020年普查) 政府驻地 市辖区 县级市 县 自治县 210000 辽宁省 Liáoníng Shěng 148,379.18 42,591,407 沈" +873,"39.99 2,703,853 太和区 3 2 2 210800 营口市 Yíngkǒu Shì 5,399.80 2,328,582 站前区 4 2 210900 阜新市 Fùxīn Shì 10,326.99 1,647,280 细河区 5 1 1 211000 辽阳市 Liáoyáng Shì 4,735.78 1,604,580 文圣区 5 1 1 211100 盘锦市 Pánjǐn Shì 4,065.40 1,389,691 兴隆台区 3 1 211200 铁岭市 Tiělǐng Shì 12,984.51 2,388,294 铁岭县 2 2 3 211300 朝阳市 Cháoyáng Shì 19,697.78 2,872,857 双塔区 2 2 2 1 211400 葫芦岛市 Húlúdǎo Shì 10,408.68 2,434,194 龙港区 3 1 2 == 资源物产 == === 植物 === 本省有各种植物161科2200余种,具有经济价值的1300种以上。药用类830多种,如人参、细辛、五味子、党参、天麻等;野果、淀粉酿造类70余种,如山葡萄、猕猴桃、山里红" +874,"孤山杏梅、盖州苹果、柞蚕丝绸、盖州绒山羊、本溪天女木兰、桓仁白人参、宽甸柱参、山丁子、软枣猕猴桃、大连榛子、大连冰葡萄酒、刺嫩芽、板栗、大扁杏、朝阳山杏仁、丹东杜鹃、大连贝雕画、盘锦河蟹、辽阳软核杏、文蛤、大连鲍鱼、盖州尖把梨、鞍山南果梨、北镇鸭梨、大连湾魁蚶、盖州海蜇、香螺、梭子蟹等 == 人口 == 截至2022年底,全省常住人口4197万人。其中,城镇人口3064万人,占常住人口的比重为73.00%;乡村人口1133万人,占27.00%。 根据2020年第七次全国人口普查,全省常住人口为42,591,407人。同第六次全国人口普查的43,746,323人相比,十年共减少了1,154,916人,下降2.64%,年平均增长率为-0.27%。其中,男性人口为21,263,529人,占总人口的49.92%;女性人口为21,327,878人,占总人口的50.08%。总人口性别比(以女性为100)为99.7。0-14岁的人口为4,737,939人,占总人口的11.12%;15-59岁的人口为26,899,001人,占总人口的63.16%;60岁及以上的人口为10,954,467人,占总人口的" +875,"人口为642.18万人,占15.08%。在遼寧省人口超过10万的少数民族有:满族(508.6万,是全国满族人口最多的省份)、蒙古族(67.78万)、回族(21.64万)、朝鲜族(22.92万)、和锡伯族(12.76万)。与2010年第六次全国人口普查相比,汉族人口减少933,557人,下降2.52%,占总人口比例增加0.11个百分点;各少数民族人口减少221,359人,下降3.33%,占总人口比例下降0.11个百分点。其中,满族人口减少250,911人,下降4.7%,占总人口比例下降0.26个百分点;蒙古族人口增加19,891人,增长3.02%,占总人口比例增加0.09个百分点;朝鲜族人口减少10,379人,下降4.33%,占总人口比例下降0.01个百分点;回族人口减少29,419人,下降11.97%,占总人口比例下降0.05个百分点;锡伯族人口减少4,870人,下降3.68%,占总人口比例不变。 由于各民族长期生活在一起,形成了大杂居小聚居的特点。早在1950年代就建立的阜新和喀左两个蒙古族自治县,而在改革开放之后又建立了新宾、清原等8个满族自治县。由於歷史原因,遼寧省是全國滿族人的" +876,"加值9397.8亿元,增长3.2%;第三产业增加值12362.1亿元,增长5.0%。全年人均地区生产总值54745元,比上年增长4.3%。 辽宁省GDP逐渐衰落,2016甚至成为全国唯一一个出现GDP负增长的省份,不过2018年以来已逐渐恢复。 === 工业 === 辽宁省工业的发展,开始于19世纪末。 辽宁省中部城市产业带主要有:沈阳的机械、电子、医药、化工、汽车、航空、纺织、轻工、石化,抚顺、阜新的轻工、电力、石化、钢铁,辽阳的化纤,鞍山、本溪的钢铁、建材、纺织,铁岭的电力、煤炭等等。 ==== 汽车 ==== 1929年5月,中国第一辆国产汽车“民生”牌75型载货汽车于沈阳民生工厂问世。这辆车采用65马力6缸汽油发动机,液压制动,装载量为1816千克,最高时速64千米。除少数重要部件委托国外依图纸代制以外,其余均为自制。 ==== 航空 ==== 位于沈阳的中航工业沈阳飞机工业集团及其下属的沈阳飞机设计研究所,沈阳飞机工业公司,沈阳航空发动机研究所,沈阳黎明航空发动机公司等是中国航空工业飞机设计和制造的摇篮。 ==== 钢铁 ==== 本省素有「钢铁大省」之称,省内既有鞍钢、本钢" +877,"机场 * 丹东浪头机场(DDG) * 锦州小岭子机场(JNZ) * 朝阳机场(CHG) * 营口机场(YKH)民用机场 另外有沈阳东塔机场、沈阳于洪全胜机场、沈阳新民农用机场、沈阳苏家屯红宝山机场、沈阳辽中机场、大连长海大长山岛机场等。 === 公路 === 沈大高速公路北起沈阳,南至大连,是全国第一条高速公路,2004年全线升级,成为国内第一条双向八车道高速公路。全线依次贯穿沈阳、辽阳、鞍山、营口、大连五个工业大中城市,沟通大连港、营口港和鲅鱼圈港,是辽宁地区重要的运输动脉。沈丹高速公路北起沈阳,南至丹东,全长222公里。 === 水运 === 主要港口有大连、营口、丹东、庄河、锦州、旅顺羊头洼,国家级有大连港、营口港(均位列全国十大港口)。 == 文化 == === 曲艺戏剧 === * 京剧 颇具水平的有沈阳京剧院和大连京剧院,前者是国家十大重点京剧院团之一。 “南麒北马关外唐”,沈阳唐派是京剧的重要流派。 * 评剧 评剧在流传过程中产生了一大批表演艺术家和众多的表演流派,其中有3大流派韩、花、筱,都出自辽宁沈阳,评剧表演艺术家韩少云创立了韩派、花淑兰创立了花派、筱俊亭创立了筱派" +878,"官话和东北官话三个区。 === 民风民俗 === * 大秧歌,是明清时在继承唐代以来东北大秧歌传统特色而形成的地方乡土艺术。开始时,是少数民间闲散艺人在街头表演的谋生手段,逐渐变成了节庆或庙会期间的娱乐活动。 * 婚俗: 三天回酒当天走,七天回酒住一宿 ,新娘子拜完天地后,在要进房门的时候,由两个几辈老人都健在的所谓 全科 人或漂亮的小姑娘或媳妇在一左一右递来两个盛酒的酒壶,这叫 宝贝壶 ,新娘子接了必须把它放在新房的窗台上,婚后把它带回娘家去,叫 回酒 。婚后三天的回酒,不能在娘家住,必须当天回婆家,过七天的回酒,可以在娘家宿上一夜。 * 一般满族婚娶旧俗,子女成年,男女方父母首先要给儿女对照八字,如若合婚,双方父母才能由媒人带领 相看 ,也叫 相亲 ,俗称 看门户 。如双方相看中意才能定婚。 === 其他 === 中國首艘航空母艦以遼寧省為名──遼寧號航空母艦。 == 人物 == == 媒体 == === 报纸 === 清末沙俄在旅顺创办俄文报纸《新边疆报》,是为东北地区第一张近代报刊。日本人也相继出版《营口新闻》《满洲日报》等日文报纸。日俄战争沙俄战败后,日本又创立了《辽东新报》" +879,"》《沈阳今报》等都市报以及《球报》等专业报纸相继出现。现存主要报纸有: * 辽宁报刊传媒集团(辽宁日报社):辽宁日报、辽沈晚报、半岛晨报、辽宁法治报、辽宁朝鲜文报、辽宁老年报、友报、幸福老人报 * 沈阳日报报业集团:沈阳日报、沈阳晚报、地铁第一时间、晚晴报 * 大连新闻传媒集团:大连日报、大连晚报、老友时代报 * 其他:各地市党报、千山晚报、抚顺晚报、本溪晚报、鸭绿江晚报、锦州晚报、阜新晚报、辽河晚报、燕都晨报、葫芦岛晚报等 === 广播电视 === 辽宁是中国广播电视事业起步较早的地区。1925年,日本当局在关东州设立的大连放送局开始播音,是为中国东北地区最早的广播电台。1928年,奉系创办的中国第四家官办电台奉天广播无线电台开播,是当时中国境内发射功率最大且最早采用水冷广播技术的广播电台。满洲事变后,日本全面控制并整合东北地区的广播及通信网络,在今辽宁省境内设立了满洲电信电话株式会社奉天、大连两座中央放送局及安东、锦县、营口三座放送局。1945年日本战败后,原满洲电电各放送局停播,沈阳广播电台、关东广播电台等相继成立。1948年12月,东北新华广播电台迁往沈阳,后于1950年在沈阳" +880,"历史文化名城、35处全国重点文物保护单位 * 国家重点风景名胜区:千山、鸭绿江、金石滩、兴城海滨、大连海滨—旅顺口、凤凰山、本溪水洞、青山沟、医巫闾山、红海滩、苇海、望儿山、仙人岛、大青山、赤山、团山国家级海洋公园 * 国家历史文化名城:沈阳、辽阳 * 世界文化遗产:清沈阳故宫及盛京三陵 === 著名建筑 === 瀋陽故宮、福陵(东陵)、昭陵(北陵)、興城古城 === 重点景区 === *沈阳 **沈阳故宫:中国目前仅存最完整的两大古代宫殿建筑群之一,里面藏有大量明清时期的珍贵文物。 ***大政殿:大政殿俗称八角殿,始建于1625年,是清太祖努尔哈赤营建的重要宫殿,是盛京皇宫内最庄严最神圣的地方。初称大衙门,1636年定名笃恭殿,后改大政殿。 **张氏帅府博物馆:迄今东北地区保存最为完好的名人故居。 **北陵:沈阳市最大的公园,中部坐落着皇太极与孝端文皇后的陵墓。 **九·一八历史博物馆:为纪念震惊中外的九一八事变而建。 **沈阳世博园 **棋盘山 **沈阳天主教堂 **东陵 **辽宁博物馆 **太原街商圈 * 大连 **星海广场:亚洲最大的城市广场,大连地标,可欣赏到巨型华表(已拆除" +881,"辽宁省的主要高等学校有:东北大学、大连理工大学、辽宁大学、大连海事大学、中国医科大学、东北财经大学、辽宁石油化工大学、沈阳药科大学、辽宁师范大学、大连民族大学、中国刑事警察学院、沈阳音乐学院、鲁迅美术学院、辽宁工程技术大学等。 === 体育 === 职业队: * 足球 ** 中甲联赛:辽宁沈阳城市、大连英博 ** 女甲联赛:大连人 * 篮球 ** CBA:辽宁本钢 * 排球 **男排超级联赛 :辽宁鸭绿江晚报 **女排超级联赛 :辽宁华君 == 政治 == thumb|280px|辽宁省人民政府 辽宁省四大机构现任领导人 机构 中国共产党辽宁省委员会 辽宁省人民代表大会常务委员会 辽宁省人民政府 中国人民政治协商会议辽宁省委员会 职务 书记 主任 省长 主席 姓名 郝 鹏 郝 鹏 李乐成 周 波 民族 汉族 汉族 汉族 汉族 籍贯 陕西省凤翔县 陕西省凤翔县 湖北省监利市 上海市 出生日期 } 就任日期 2022年11月 2023年1月 2021年10月 2021年1月 == 注释 == == 参考文献 == == 外部链接 == * 辽宁省人民政府 Category:中华人民共和国省" +882,"華僑華人(英語: 或 Chinese diasporas),包括華僑和華人两个概念。其中,华侨一詞普遍作為寄居海外中国人的稱謂。後指僑居海外,具中华民国国籍或中华人民共和国国籍的公民。华裔指华侨华人的后代,具有侨居国国籍。 关于海外华人一词,在中国大陆方面,根据《中华人民共和国出境入境管理法》,定居境外的华侨应当注销户口,而当华侨加入或取得住在国国籍后,就丧失了中华人民共和国国籍,就成为外籍华人,也称海外华人,简称华人,在不强调国籍或法律内涵的情况下,华侨、华人有时候会混用,或使用“华侨华人”之综合称谓。在台湾方面,因中华民国法律不否认双重国籍,因此中華民國國民加入或取得住在国国籍仍可保留中华民国国籍,被视为华侨或台侨。中華民國僑務委員會以「中性包容」為由於2018年在行政規定中開始使用“僑民”來取代“華僑”一詞,但表示與傳統僑社來住時仍維持「華僑」稱呼。中華民國僑務委員會對海外華人的定義為「兩岸三地以外之所有旅居海外的華人(包含第一代移民及其後代)」。 中國移民史可以上溯到2000多年以前。明清两朝执行“海禁”,出洋谋生被视为非法的行为,因此海外移民被朝廷冠以“逃民”、“罪民”等的消" +883,"侨华人目前生活在中国以外的国家和地区。 === 华侨 === “侨”是寄居、客居之意。1878年,清驻美使臣陈兰彬在奏章中把寓居国外的中国人称为“侨民”。1883年郑观应在给李鸿章的奏章中使用了“华侨”一词。1904年,清政府外务部在一份奏请在海外设置领事馆的折子里提到“在海外设领,经费支出无多,而华侨受益甚大。”此后,“华侨”一词就普遍作为寄居海外的中国人尤其是东南亚的中国移民及其后裔的称谓了。 === 華人 === 華人(英語:)除了泛指具有中华民族血统的人士之外,还特指旅居海外不具有中國國籍(清朝國籍、中华民国国籍或中华人民共和国国籍)的华裔,也称外籍华人或海外华人,例如印尼华人、泰國华人、馬來西亞华人、美國华人、加拿大华人、澳大利亚华人等。华裔(英语:Overseas Chinese Descendants)指華人或者華侨在海外(非祖籍國)出生的后代。 == 認定 == 根据中华民国《華僑身分證明條例施行細則》,华侨身分需满足的条件是: # 僑居國外,指居住於臺灣地區、大陸地區、香港及澳門以外之國家或地區。 # 具有中華民國國民身分或華裔證明文件。 根据《中华人民共和国归侨侨眷" +884,"政,逃至屈都昆(今马来西亚登嘉楼)。唐朝贞观年间,僧人孟怀业到佛逝国取经,后留恋佛逝不回故乡。五代时苏门答腊就有中国人耕种,其中巨港华人最多。他们是黄巢起义失败后逃亡至苏门答腊的华侨。宋嘉佑年间,安南虏走大批中国人到安南。中世纪时,有三名中國婦女在東歐定居,並與阿什肯納茲猶太人結婚。 元朝航海家汪大渊所著《岛夷志略》记载单马锡有華僑定居。唐人到真腊国居住经商会娶真腊女子为妻。洪武三十年(1397年),時旅居三佛齊的華僑一千多人擁戴廣東南海人梁道明爲三佛齊国王。嘉靖年间,潮州江洋大盗林道乾聚集二千余人攻占北大年,因助暹罗王破安南,受暹罗王嘉奖,赐其女与之成婚。 清雍正年间,广东澄海华富里(今汕头市澄海区上华镇)人郑达,南渡暹罗,其子郑昭为暹罗王。婆罗洲戴燕国王吴元盛、昆甸国王罗大均是广东嘉应人。乾隆年间華人羅芳伯在婆羅洲島上(今加里曼丹西部)成立了「蘭芳共和國」。 === 清朝中後期 === 第二次鸦片战争后,清朝的国门被迫打开,加上航运也比古代提高了不少,华人人口开始较大规模地迁徙到世界各地。清代錢莊為長途匯款的便利,需要派駐職員長居海外。19世纪,華人開始移居东南亚從事金融貿易生意。" +885,"持。中華民國《國籍法》接受双重國籍身分,將華裔定義为只要血統上具有華人血緣者,皆為華裔,接受海外華人入籍為中華民國國民,造成了印尼華人國籍問題。 === 中華人民共和國 === 1955年,首任中华人民共和国国务院总理周恩来在参加第一届亚非会议(万隆会议)期间,中国和印度尼西亚签订了《》以期解决印尼華人國籍問題,条约容许持有双重国籍的华人,在条约签署后20年内,成年时选择印尼国籍或中国国籍刘新生(原中国驻文莱大使),周总理与万隆会议 。1980年頒佈的《中華人民共和國國籍法》規定,中华人民共和国不承认中国公民具有双重国籍。1984年起香港前途問題引發香港移民潮。1989年的天安門事件也引發了移民潮。移民潮在1997年香港特別行政區成立之後逐漸平息,而不少移民外國的香港人已回流香港。許多香港原居民和香港人為了改善生活而移居至英國、荷蘭等国。 == 數量及分布 == 目前的海外華人主要生活於东南亚、欧洲、北美地区。东南亚因邻近中国,就成为中国移民的目的地。當地為相對多數民族的新加坡及在當地為相對少數民族的馬來西亞、泰國、菲律賓、印度尼西亞與越南。這些地區的海外華人,又被称为南洋华侨、南侨," +886,"歐洲和北美洲,華人人口稀少的澳大利亞、紐西蘭也成了今天的熱門移民地,文化相對近且經濟發展的東南亞也有部分移民。而前往遙遠非洲、南美洲(如巴西)的也在有所增多。 === 統計數據 === 洲/國家 當地對海外華人的稱呼 海外華人人口 佔當地人口比例 佔全球海外華人比例 亞洲 30,000,000 (2013) 泰國華人 696~836萬 (2019) 10~12% 馬來西亞華人 6,900,000 (2020) 23.2% 印尼華人 2,832,510 (2010) 1.2% 新加坡華人 居住新加坡的中國國民 2,571,000 (2015) 451,481 (2019) 76.2% 沒有可用數據 菲律賓華人 1,500,000 (2014) 1.5% 緬甸華人、果敢族 1,220,000 (2014) 2.3% 朝鲜半岛华人 1,075,566 (2018) 2.1% 日本華人 813,675 (2020.3) -- -- 蒙古華人 11,323 0.4% 朝鲜半岛华人 50,000 (2003) 0.2% 越南華人、明乡人 1,030,000 (2014) 1.1% 柬埔寨華人 15" +887,"人 -- -- -- 哥斯达黎加华人 9,000 (2008) -- -- 多明尼加華人 50,000 (2010) -- -- 蓋亞那華人 -- -- 歐洲 2,150,000 (2011) 法國華人 600,000 (2018) 0.9% 俄羅斯華人、東干族、塔兹族 490,000 (2014) 0.3% 英籍華人 460,000 (2014) 0.7% 義大利華人 288,923 (2020) 0.5% 華裔德國人 212,000 (2015)https://web.archive.org/web/20171209232407/http://www.bib-demografie.de/DE/Aktuelles/Presse/Archiv/2017/2017-03-01-zuwanderung-aussereuropaeische-Laender-fast-verdoppelt.html 0.25% 西班牙華人 197,704 (2021) 0.4% 愛爾蘭華人 16,533 (2006) 0.39% 奧地利華人 25,000 (2008) 荷蘭華人 44,713 (2006) 0" +888,"角華人 -- -- -- 波札那華人 -- -- -- 喀麥隆華人 -- -- -- 阿尔及利亚华人 -- -- -- 賴索托華人 -- -- -- 奈及利亞華人 -- -- -- 肯尼亞華人 -- -- -- 莫三比克華人 -- -- -- 留尼旺華人 25,000 (1999) -- -- 马达加斯加华人 -- -- -- 模里西斯華人 36,830 (2007) 2.9% -- 塞席爾華人 -- -- -- 統計數據使用當地政府數據或可信度最佳的估計。百分比可能因人口普查結果或估計結果而有所不同。 === 前二十名海外華人人口 === 統計數據來自中華民國僑務委員會2005年及2014年海外華人前二十大排名國家人口數 中華民國僑務委員會,2014年底的統計結果。 國家 2014年 2014年 2005年 2005年 2005-2014年 2005-2014年 國家 人口 排名 人口 排名 人口增減 增減率 8,360,000 1 7,566,200 1 789,800 10.4% 7,000,000 2 7,053,240 2 53,240 0.8% 6,580,000 3 6," +889,"0,000 18 / / / / 210,000 19 / / / / 170,000 20 / / / / / / 343,855 14 / / / / 185,765 18 / / / / 144,928 20 / / === 美加澳-{zh-cn:新; zh-tw:紐; zh-hk:新}-四国华人人口都会区排名 === 美国数据为2015年资料,澳大利亚数据为2016年估算资料,新西兰为2013年普查数据,加拿大为2016年统计数据。 都会区主城市 华裔人口 所占比例 所属国 纽约 739,144 3.7% 多伦多 631,045 10.8% 洛杉矶 528,248 4.0% 悉尼 488,102 10.1% 温哥华 474,655 19.6% 三藩市 460,252 10.2% 墨尔本 356,530 7.9% 圣荷西 169,026 8.8% 波士顿 133,241 2.8% 奥克兰 112,290 6.8% 芝加哥 109,046 1.1% 华府 105,462 1.8% 布里斯本 99,632 4.4% 珀斯 99,293 5.1% 西雅图 98,949 2.7% 卡加利" +890,"政要 === 近代以来,不乏有华人登上各国政治舞台。国家元首或總理,如圭亚那总统锺亚瑟、苏里南总统陈亚先、新加坡总理李光耀和李显龙父子,柬埔寨首相洪森和泰國首相盈拉·欽那瓦和達新·欽那瓦等。政府高官有日本內閣府特命擔當大臣(消費者及食品安全擔當)蓮舫、马来西亚交通部长林良实、美国交通部部长赵小兰、商务部部长骆家辉、能源部部长朱棣文和美國在台協會台北辦事處處長梅健華等。 === 富商 === 部分知名海外華裔富商 籍贯 名称 浙商 張尊三(日) · 吳錦堂(日) · 胡嘉烈(新) 蘇商 王安(美) 閩商 郭鹤年(馬) · 林绍良(印) · 施至成(菲) · 李光前(新) · 陳嘉庚(新) 粵商 黃馨祥(美) · 叶焕荣(英) · 谢国民(泰) · 陳豐明(柬) · 佘有進(新) · 蘇旭明(泰) 台商 蔡崇信(加) · 陳士駿(美) === 艺术家 === 奥斯卡金像奖华人获奖者可追溯至1956年和1964年美籍华人黄宗霑两度斩获“最佳黑白片摄影奖”。美籍华人金国威導演的《徒手攀岩》獲得2019年奧斯卡最佳紀錄片獎 。 == 各地華社及僑商商會 == === 华团 === 亚洲 * 英属" +891,":曾祥忠 马来西亚华人组织 名称 创立时间 说明 马来西亚华人公会 1949年 一个代表马来西亚华人的马来西亚政党 砂拉越人民联合党 1959年 马来西亚政党 马来西亚华校教师会总会 1951年 马来西亚华文教育组织 马来西亚华校董事联合会总会 1954年 马来西亚华文教育组织 ---- 美洲 * 美国 ** 美国亚裔社团联合总会(亚总会) ** 百人会 美国华人组织 名称 创立时间 说明 致公堂 1848年 秉公堂 1874年 源於洪門致公堂,是舊金山華埠五大僑團之一。 中华公所 1882年 安良堂 1893年 在全美20餘個城市設有分會,有超过三万名注册成员,大多数具有商业或工业背景。 布禄仑华人协会 1988年 位于紐約布鲁克林唐人街的一个非牟利機構,有300多名员工。 百人会 1990年 由贝聿铭创立成員來自商界、政界、學術界及文化界。 美国香港总商会 1997年 原名“美国香港旅美华人总商会”,美国籍公民成员数量增加,2014年经决议后改称为“美国香港总商会”。 ---- 欧洲 * 英国 ** 英国华侨华人专业人士联合总会 * 意大利 ** 意大利旅意福建华侨华人同乡总会,会" +892,"西亚华校董事联合会总会、马来西亚华校教师会总会、美国亚裔社团联合总会、百人会、美國香港總商會等。 === 同化 === 英属印度和欧洲此外,泰國的一些混血华裔多认同泰國人的身份,沒有華人的味道。在緬甸,華人很少與當地人通婚(甚至不與講其它方言的華人通婚),在接受緬甸文化的同时維持華人傳統文化。馬來西亞華人則維持公開的華人身分,並且接受華文教育和保有民間信仰。換句話說,馬來西亞華人始終堅持自己的根源,並無被當地人同化。雖然他們有極少數因轉變信仰(伊斯蘭教)而受同化,但此情況並不常見。 與馬來西亞僅有一水之隔的新加坡,華人在當地佔大多數。新加坡華人語言以英語和華語為主,並與馬來西亞華人一樣,保留不少中華文化。 在菲律賓,很多年輕的海外華人被徹底同化,然而老一輩的華人則傾向被視為「外國人」。 華人也為一些國家帶來中華文化的影響力,如保留部分中華文化的越南;越戰結束後,大批人口逃往海外,未返中國。 === 語言 === 海外華人使用的語言除了官话及各種漢語外,也會使用居住國的當地語言。西方國家早期的華人移民通常使用粵語,而近期的華人新移民則使用官話居多。在東南亞,南方漢語(主要為閩南語、潮汕語" +893,"。中华全国归国华侨联合会是由中華人民共和國归侨、侨眷组成的全国性人民团体和非政府組織,是参加中国人民政治协商会议的人民团体。 === 侨汇 === 僑匯是指国际移民将其在国外所得的部分收入向来源国家庭或社区进行的财务或实物转移。侨汇收入曾经是中国外汇的重要来源渠道之一。華侨在1911年为辛亥革命供應了絕大部分革命資金。据海关关册统计,1868~1936年中国外贸入超额累计50亿美元,而同期侨汇总数为24.4亿美元,接近外贸入超额的一半。二战时期,海外华人也提供了中華民國大量财力,支持抗日事业。如1937年侨汇4.74亿元国币,外贸入超1.15亿元,侨汇占入超411%;1938年侨汇6.44亿元,外贸入超1.24 亿元,侨汇占入超521%。这种状况持续到1941年底太平洋战争爆发南洋沦陷前。国民政府依靠侨汇弥补了外贸入超,还利用这些外汇购买军火等战略物资用于抗日。然而由于国共内战,恶性通货膨胀,外汇价格一日数变华侨汇款回国,常遭25%~50%的损失。据统计,1946年中国银行和广东省银行收入侨汇总数约3000万美元,1947年降至1000多万美元,1948年不足500万美元,1949年侨" +894,"例來分配(僑選立委席次計入全國不分區,兩者共選出34席)。大多數當選人具有雙重國籍,惟於上任前必須放棄外國國籍,成為完全中華民國國民。 1950~70年代,东南亚华侨为躲避当地排华政策的危害,纷纷返回中国。中国政府成立“华侨事务委员会”,协调与海外华人关系密切的广东、福建、广西、云南等地,新建、扩建了多个华侨农场以紧急安置华侨。 归侨在中华人民共和国占有重要地位,实业家陈嘉庚曾出任中華人民共和國政协副主席一职。不过,中華人民共和國到了文革时期,与海外华人的联系受到严重影响,当时有海外关系甚至会受批斗,许多归侨都受到不公正的待遇。改革开放后,對海外華人的政策恢复到原有水准并得到加强。在1980年代,中華人民共和國嘗試積極地在其它方面尋求海外華人的支持,尋求他們的技術與資金來幫助發展。現在,很多海外華人投資中國大陸,提供了包括財政來源、社會與文化網路、互相交流與機會。今日在中華人民共和國,全國人民代表大會設有歸國華僑的席次。 === 僑眷 === === 僑生 === 僑生指回到祖國就學的華僑學生。从1949年至1958年,先后回中国大陆的侨生有5万人左右。侨生集中的北京、集美、广州、汕头" +895,"人民共和国成立后,组织了多次海外撤侨。其中1960年从印尼接回6万多华侨。2011年利比亚撤侨行动中,撤侨3.6万多人,包括所有在利比亚持中国护照的官员、学生、商人、中资企业人员、外派劳工、旅客、游客,以及主动要求回国避难的利比亚籍华人。 == 知名华人 == * 海外华人国王列表 * 华裔政要列表 * 华人诺贝尔奖得主列表 * 华人沃尔夫奖得主列表 * 奥斯卡金像奖华人获奖者 == 参见 == * 唐人街 == 注释 == == 参考文献 == === 引用 === === 来源 === ; 书籍 * 陳烈甫:《華僑學與華人學總論》(臺北:台灣商務印書館,1986) * 李安山:《中國華人華僑學》〈中國華人華僑學的科學定位與研究對象〉(北京:北京大學出版社,2006) * 顏清湟:《海外華人的社會變革與商業成長》(廈門:廈門大學出版社,2005) == 外部連結 == * 王柯:〈華僑與他們的公共空間〉 (2012) * 王赓武:〈中国革命与海外华人 〉。 * 王赓武:〈从历史中寻求未来的海外华人 〉。 * 王赓武:〈移民地位的提升:既不是华侨,也不是华人 〉。 * 海外華僑華人剪報" +896,"人工智能(,缩写为),亦稱機器智能,指由人製造出來的機器所表現出來的智慧。通常人工智能是指用普通電腦程式來呈現人類智能的技術。該詞也指出研究這樣的智能系統是否能夠實現,以及如何實現。同时,通過醫學、神經科學、机器人学及統計學等的進步,常態預測則認為人類的很多職業也逐漸被其取代。 人工智能於一般教材中的定义领域是“智慧主体(intelligent agent)的研究与设计”,智慧主体指一个可以观察周遭环境并作出行动以达致目标的系统。约翰·麦卡锡于1955年的定义是「制造智能机器的科学与工程」。安德烈亚斯·卡普兰和迈克尔·海恩莱因(Michael Haenlein)将人工智能定义为“系统正确解释外部数据,从这些数据中学习,并利用这些知识-{zh-hans:通过; zh-hant:透過;}-灵活适应实现特定目标和任务的能力”。 人工智能可以定義為模仿人類與人類思維相關的認知功能的機器或計算機,如學習和解決問題。人工智能是計算機科學的一個分支,它感知其環境並採取行動,最大限度地提高其成功機會。此外,人工智能能夠從過去的經驗中學習,做出合理的決策,並快速回應。因此,人工智能研究人員的科學目標是通" +897,"定的应用程序。 AI的核心问题包括建構能夠跟人類似甚至超卓的推理、知识、計划、学习、交流、感知、移動 、移物、使用工具和操控机械的能力等。通用人工智能(GAI)目前仍然是该领域的长远目标。目前弱人工智慧已經有初步成果,甚至在一些影像辨識、語言分析、棋類遊戲等等單方面的能力達到了超越人類的水平,而且人工智慧的通用性代表著,能解決上述的問題的是一樣的AI程式,無須重新開發算法就可以直接使用現有的AI完成任務,與人類的處理能力相同,但達到具備思考能力的統合強人工智慧還需要時間研究,比较流行的方法包括统计方法,计算智能和传统意义的AI。目前有大量的工具应用了人工智能,其中包括搜索和数学优化、逻辑推演。而基於仿生學、認知心理學,以及基于概率论和经济学的演算法等等也在逐步探索當中。 == 概論 == 人工智能的定義可以分為兩部分,即「人工」和「-{zh:智能; zh-hans:智能; zh-hant:智能; zh-tw:智慧}-」。「人工」即由人设计,为人创造、制造。 關於甚麼是「-{zh:智能; zh-hans:智能; zh-hant:智能; zh-tw:智慧}-」,较有争议性。這涉及到其它諸如" +898,"Public Perceptions from Australia and Hong Kong, Government Information Quarterly, 40(3), 101833。 最近,由於人工智能減少了行政成本和時間,許多政府開始將人工智能用於各種公共服務。例如,移民流程的機器人自動化減少了處理時間並提高了效率。人工智能為地方政府服務帶來技術突破。人工智能代理協助城市規劃者基於目標導向的蒙特卡羅樹搜索進行場景規劃。目標推理人工智能代理提供最佳的土地利用解決方案,幫助我們制定民主的城市土地利用規劃。人工智能利用在線數據來監控和修改環境威脅政策。在2019 年水危機期間,潛在狄利克雷分配方法確定了Twitter (X) 中討論最多的主題,這是一種樸素的推文分類方法,對乾旱的影響和原因、政府響應和潛在解決方案等主題進行了分類。人工智能工具與司法部門的人類法官相輔相成,提供客觀、一致的風險評估(2023) Artificial Intelligence in Local Government Services: Public Perceptions from Australi" +899,",他们还开拓了未来成为人工智能领域一部分的多个研究分支, AI's immediate precursors: * * * * 比如1943年沃伦·麦卡洛克与沃尔特·皮茨设计的“人工神经元”,以及图灵于1950年发表的具有深远影响的论文《》,其中提出了“图灵测试”,展示了“机器智能”的可行性。 人工智能研究领域正式成立于1956年在达特茅斯学院举行的“达特茅斯会议”。 Dartmouth workshop: * * * The proposal: * 与会者后来在20世纪60年代成为该领域的先驱者。 他们及其学生研发出的程序被媒体誉为“令人惊叹”: 计算机不仅能学会国际跳棋策略,还能解决代数文字难题、证明逻辑定理,甚至能进行英语对话。 Successful programs the 1960s: * * * * 在20世纪50年代末至60年代初,许多英国和美国的大学纷纷建立了人工智能实验室。 20世纪60年代至70年代,研究人员坚信他们的方法最终能够成功创造出具有通用智能的机器,并将此视为他们研究领域的最高目标。赫伯特·西蒙(Herbert Simon)曾预言:“在未来二十年内,机器能" +900,"目推动英美两国恢复学术研究的资助。 Funding initiatives in the early 1980s: Fifth Generation Project (Japan), Alvey (UK), Microelectronics and Computer Technology Corporation (US), Strategic Computing Initiative (US): * * * * * 但从1987年Lisp机器市场的衰退开始,人工智能再次发展挫折,第二个较长的低迷期开始了。 Second AI Winter: * * * * * 在此之前,人工智能的主要资金用于高级符号项目,用以表征如计划、目标、信念等概念性对象。20世纪80年代,一些研究人员开始怀疑,这种方法能否模仿人类认知的所有过程,特别是感知、机器人、学习和模式识别,并开始研究“次符号”方法。罗德尼·布鲁克斯(Rodney Brooks)普遍不认同“表征”这一概念,并将研究重心转向了设计能够移动和自持生存的机器工程。 朱迪亚·珀尔、卢特菲·泽德等学者发展了多种方法,这些方法基于合理推断处理不完备或" +901,"习开始主导行业标准,并迅速成为该领域内广泛采用的方法。 Deep learning revolution, AlexNet: * * * 在多种场合,替代性方法被淘汰,深度学习得到优先采用。深度学习的突破性成果,既得力于硬件的显著进步(如计算速度更快的电脑、 Moore's Law and AI: * 图形处理单元以及云计算技术),也依赖于广泛的数据可用性 Big data: * (包括精心策划的数据集,譬如ImageNet)。深度学习的成果引发了公众对于人工智能的浓厚兴趣并促使资金投入的大幅度增加。2015年至2019年期间,机器学习领域的出版物数目上升了50%。 2016年,在机器学习会议上,与技术滥用成为突出话题;相关论文发表数量急剧增加,研究经费随之提供,众多研究人员转而聚焦这些议题。对齐问题逐渐成为学术探讨的重要议题。 2010年代末至2020年代初,AGI公司推出引发广泛关注的程序。2015年,由DeepMind研发的“阿尔法狗”战胜了世界围棋冠军。该程序仅被输入了游戏规则并自主形成了策略。GPT-3是OpenAI在2020年推出的一款强大的语言模型,它能生成高质量、类人的" +902,"化Agent研究强调感知运动的重要性。神经网络研究试图以模拟人类和动物的大脑结构重现这种技能。 === 知識表示法 === 知识表示是人工智能领域的核心研究问题之一,它的目标是让机器存储相应的知识,并且能够按照某种规则推理演绎得到新的知识。有许多需要解决的问题需要大量的对世界的知识,这些知识包括事先存储的先验知识和-{zh-hans:通过; zh-hant:透過;}-智能推理得到的知识。事先存储的先验知识指:人类用某种方式告诉给机器的知识。-{zh-hans:通过; zh-hant:透過;}-智能推理得到的知识指:结合先验知识和某种特定的推理规则(逻辑推理)得到的知识。首先,先验知识可以指描述目标,特征,种类及物件之间的关系的知识, 也可以描述事件,时间,状态,原因和结果, 以及任何知识你想要机器存储的。比如:今天没有太阳,没有太阳就是阴天。那么以命题逻辑语言,这些知识可以被表示为:今天 → 没有太阳,没有太阳 → 阴天。这些知识是先验知识,那么推理可以得到新知识:今天 → 阴天。由此例子可以看出,先验知识的正确性非常重要,这个例子中没有太阳就是阴天,这个命题是不严谨的、比较笼统的,因" +903,"演算法和群体智慧可以达成一个整体的突现行为目标。 === 機器學習 === 机器学习的主要目的是为了让机器从使用者和输入数据等处获得知识,从而让机器自动地去判断和输出相应的结果。这一方法可以帮助解决更多问题、减少错误,提高解决问题的效率。对于人工智能来说,机器学习从一开始就很重要。1956年,在最初的达特茅斯夏季会议上,雷蒙德·索洛莫诺夫写了一篇关于不监视的概率性机器学习:一个归纳推理的机器。 机器学习的方法各种各样,主要分为监督学习和非监督学习两大类。监督学习指事先给定机器一些训练样本并且告诉样本的类别,然后根据这些样本的类别进行训练,提取出这些样本的共同属性或者训练一个分类器,等新来一个样本,则通过训练得到的共同属性或者分类器进行判断该样本的类别。监督学习根据输出结果的离散性和连续性,分为分类和回归两类。非监督学习是不给定训练样本,直接给定一些样本和一些规则,让机器自动根据一些规则进行分类。无论哪种学习方法都会进行误差分析,从而知道所提的方法在理论上是否误差有上限。 === 自然語言處理 === 自然語言處理探討如何處理及運用自然語言,自然語言認知則是指讓電腦「懂」人類的語言。自然" +904,"技術的未來公開表示憂心,人工智慧若在許多方面超越人類智慧水平的智能、不斷更新、自我提升,進而取得控制管理權,人類是否有足夠的能力及時停止人工智慧領域的「軍備競賽」,能否保有最高掌控權,現有事實是:機器常失控導致人員傷亡,這樣的情況是否會更加擴大規模出現,歷史顯然無法給出可靠的樂觀答案。特斯拉電動車馬斯克(Elon Musk)在麻省理工學院(MIT)航空航天部門百年紀念研討會上稱人工智能是「召喚惡魔」行為,英國發明家Clive Sinclair認為一旦開始製造抵抗人類和超越人類的智能機器,人類可能很難生存,蓋茲同意馬斯克和其它人所言,且不知道為何有些人不擔憂這個問題。 DeepMind的人工智慧(AI)系統在2016年「AlphaGo」對戰南韓棋王李世乭獲勝,開發商表示在內部設立倫理委員會,針對人工智慧的應用制定政策,防範人工智慧淪為犯罪開發者。 科技進步,人工智慧科技產生「自主武器」軍備競賽已悄悄展開,英國、以色列與挪威,都已部署自主飛彈與無人操控的無人機,具「射後不理」(fire-and-forget)能力的飛彈,多枚飛彈還可互相溝通,分享找到攻擊目標。這些武器還未被大量投入,但很快" +905,"非這些創新發明本身,而是發明對社會上許多人的生活方式衝擊處理不當,新科技在社會上產生新工作也取代舊工作,產生了新的輸家和贏家,若是輸家的人數太多將造成一股社會不穩的能量而這股能量被有心人利用可能導致各種事件。他認為各國應該強制訂定規定AI機器只能用於人類不能做的工作,避免短時間大量人類被取代的失業大潮,但馬雲沒有提出這種世界性規定將如何實現並確保遵守的細節方案。 数据科学和人工智能被哈佛商業評論称为《二十一世纪最Sexy的職业》,人工智能需求量大,鼓励了不少大学诸如伯克利大学专门成立数据科学系。硅谷和纽约为主的《》公司於2012年成立,焦点是数据科学,大数据,和人工智能企业培训,提供国际大数据培训服务。 === AI對人類的威脅 === 此議題目前分成兩個學派: ==== 悲觀學派 ==== 此學派的代表是天文物理學家史蒂芬·霍金,以及特斯拉執行長伊隆·馬斯克。霍金認為AI對人類將來有很大的威脅,主要有以下理由: # AI會遵循科技發展的加速度理論。 # AI可能會有自我改造創新的能力。 # AI進步的速度遠遠超過人類。 # 人類會有被滅絕的危機。 ==== 樂觀學派 ==== 主要是" +906,"一個定義指人工智能是人造機器所表現出來的智能。總體來講,目前對人工智能的定義大多可劃分為四類,即機器「像人一樣思考」、「像人一樣行動」、「理性地思考」和「理性地行動」。這裡「行動」應廣義地理解為採取行動,或制定行動的決策,而不是肢體動作。 === 強人工智能 === 強人工智能觀點認為「有可能」製造出「真正」能推理和解決問題的智能機器,並且,這樣的機器將被認為是具有知覺、有自我意識的。強人工智能可以有兩類: * 人類的人工智能,即機器的思考和推理就像人的思維一樣。 * 非人類的人工智能,即機器產生了和人完全不一樣的知覺和意識,使用和人完全不一樣的推理方式。 === 弱人工智能 === 弱人工智能觀點認為「不可能」製造出能「真正」地推理和解決問題的智能機器,這些機器只不過「看起來」像是智能的,但是並不真正擁有智能,也不會有自主意識。 弱人工智能是對比強人工智能才出現的,因為人工智能的研究一度處於停滯不前的狀態下,直到類神經網路有了強大的運算能力加以模擬後,才開始改變並大幅超前。但人工智能研究者不一定同意弱人工智能,也不一定在乎或者了解強人工智能和弱人工智能的內容與差別,對定義爭論不休。" +907,"3, 1980) 關於強人工智能的爭論,不同於更廣義的一元論和二元論的爭論。其爭論要點是:如果一台機器的唯一工作原理就是轉換編碼數據,那麼這台機器是不是有思維的?希爾勒認為這是不可能的。他舉了個中文房間的例子來說明,如果機器僅僅是轉換數據,而數據本身是對某些事情的一種編碼表現,那麼在不理解這一編碼和這實際事情之間的對應關係的前提下,機器不可能對其處理的數據有任何理解。基於這一論點,希爾勒認為即使有機器通-{}-過了圖靈測試,也不一定說明機器就真的像人一樣有自我思維和自由意識。 也有哲學家持不同的觀點。丹尼爾·丹尼特在其著作《》(Consciousness Explained)裡認為,人也不過是一台有靈魂的機器而已,為什麼我們認為:「人可以有智能,而普通機器就不能」呢?他認為像上述的數據轉換機器是有可能有思維和意識的。 有的哲學家認為如果弱人工智能是可實現的,那麼強人工智能也是可實現的。比如(Simon Blackburn)在其哲學入門教材Think裡說道,一個人的看起來是「智能」的行動並不能真正說明這個人就真的是智能的。我永遠不可能知道另一個人是否真的像我一樣是智能的,還是說她/他僅僅" +908,"色的老式人工智能)的概念,也提议人工智能应归类为,这个概念后来被某些非GOFAI研究者采纳。 === 控制论与大脑模拟=== 20世纪40年代到50年代,许多研究者探索神经学、信息理论及控制论之间的联系。其中还造出一些使用电子网络构造的初步智能,如的烏龜(turtle)和約翰霍普金斯野獸。 这些研究者还经常在普林斯顿大学和英国的Ratio Club举行技术协会会议。直到1960,大部分人已经放弃这个方法,尽管在80年代再次提出这些原理。 === 符号处理 === 当20世纪50年代,數位计算机研制成功,研究者开始探索人类智能是否能简化成符号处理。研究主要集中在卡内基梅隆大学,斯坦福大学和麻省理工學院,而各自有独立的研究风格。約翰·豪格兰德(John Haugeland)称这些方法为GOFAI(出色的老式人工智能)。60年代,符号方法在小型证明程序上模拟高级思考有很大的成就。基于控制论或人工神经网络的方法则置于次要The most dramatic case of sub-symbolic AI being pushed into the background was the devas" +909,"难问题,需要专门的方案:他们主张不存在简单和通用原理(如逻辑)能够达到所有的智能行为。罗杰·尚克描述他们的“反逻辑”方法为“scruffy”。常识知识库(如道格拉斯·莱纳特的Cyc)就是“scruffy”AI的例子,因为他们必须人工一次编写一个复杂的概念。 *基于知识:大约在1970年出现大容量内存计算机,研究者分别以三个方法开始把知识构造成应用软件。这场“知识革命”促成专家系统的开发与计划,这是第一个成功的人工智能软件形式。“知识革命”同时让人们意识到许多简单的人工智能软件可能需要大量的知识。 === 子符号方法 === 1980年代符号人工智能停滞不前,很多人认为符号系统永远不可能模仿人类所有的认知过程,特别是感知、机器人、机器学习和模式识别。很多研究者开始关注子符号方法解决特定的人工智能问题。 *自下而上、接口agent、嵌入环境(机器人)、行为主义、新式AI:机器人领域相关的研究者,如罗德尼·布鲁克斯,否定符号人工智能而专注于机器人移动和求生等基本的工程问题。他们的工作再次关注早期控制论研究者的观点,同时提出了在人工智能中使用控制理论。这与认知科学领域中的表征感知论点是一致的:" +910,"一的方法。一个解决特定问题的agent可以使用任何可行的方法-一些agent用符号方法和逻辑方法,一些则是子符号神经网络或其他新的方法。范式同时也给研究者提供一个与其他领域沟通的共同语言--如决策论和经济学(也使用abstract agents的概念)。1990年代智能agent范式被广泛接受。 *代理架構和認知架構:研究者设计出一些系统来处理多agent系统中智能agent之间的相互作用。一个系统中包含符号和子符号部分的系统称为混合智能系統,而对这种系统的研究则是人工智能系统集成。分级控制系统则给反应级别的子符号AI和最高级别的传统符号AI提供桥梁,同时放宽了规划和世界建模的时间。 == 基本應用 == 人工智慧基本的應用可分為四大部分: === 感知能力(Perception)=== 指的是人類透過感官所收到環境的刺激,察覺訊息的能力,簡單的說就是人類五官的看、聽、說、讀、寫等能力,學習人類的感知能力是AI目前主要的焦點之一,包括: * 「看」:電腦視覺(Computer Vision)、圖像辨識(Image Recognition)、人臉辨識(Face Recognition)、" +911,"ity) === 指的是人類產生新思想,新發現,新方法,新理論,新設計,創造新事物的能力,它是結合知識、智力、能力、個性及潛意識等各種因素優化而成,這個領域目前人類仍遙遙領先AI,但AI也試著急起直追,主要領域包括:AI作曲、AI作詩、AI小說、AI繪畫、AI設計等。 === 智慧(Wisdom) === 指的是人類深刻瞭解人、事、物的真相,能探求真實真理、明辨是非,指導人類可以過著有意義生活的一種能力,這個領域牽涉人類自我意識、自我認知與價值觀,是目前AI尚未觸及的一部分,也是人類最難以模仿的一個領域。 ==实际应用== 机器视觉、指纹识别、人脸识别、视网膜识别、虹膜识别、掌纹识别、专家系统、自动规划、無人載具等。 == 學科範疇 == 人工智能是一門邊緣學科,屬於自然科學和社會科學的交叉。 === 涉及学科 === * 认知科学 * 数学及统计学 * 物理学 * 逻辑学 * 控制论及決定論 * 社会学 * 犯罪學及智慧犯罪学 === 研究範疇 === * 自然語言處理 * 知識表現 * 智能搜索 * 推理 * 規劃 * 機器學習 * 增強式學習 * 知識獲取 * 感知問題 * 模式" +912,"* 人工生命 * 机器学习 * 人工智能哲學 * 認知神經科學 * 强人工智能 * 超人工智能 * 圖靈測試 * 合成智能 * 電腦圍棋 * 電腦象棋 * 電腦將棋 * 恐怖谷理論 * 電腦科學 * 認知科學 * 意識 * 語義學 * 技术奇异点 * 集体智慧 * 控制論 * 心理學 * 生物化学计算机(例:人脑) * 國際人工智能聯合會議 * 网络本体语言(OWL) * 遊戲樹 * 自動駕駛汽車 * 電子世界爭霸戰 * 联结主义 * 人工智能法案 * 人工智能热潮 == 参考文献 == === 引用 === === 来源 === ; 中文書 * 李開復、王詠剛;《人工智慧來了》 * 李開復;《AI·未来》 ; 教材 * * * * * * * * * * * ; 人工智能历史 * * * ; 其他 * * BibTex Internet Archive * . * * * * * * * * * * * * * * . * * * * * * . * * * * * . * * * * * * * * * * . * , Presidential Address to the Ass" +913,"AI , MIT Press * Myers, Courtney Boyd ed. (2009). The AI Report . Forbes June 2009 * * Sun, R. & Bookman, L. (eds.), Computational Architectures: Integrating Neural and Symbolic Processes. Kluwer Academic Publishers, Needham, MA. 1994. == 外部連結 == * What Is AI?—An introduction to artificial intelligence by AI founder John McCarthy. * * AITopics—A large directory of links and other resources maintained by the Association for the Advancement of Artificial Intelligence, the leading organization of a" +914,"中华人民共和国国旗,即五星红旗,是中华人民共和国的国旗(国家象征之一);左上角镶有五颗黄色五角星的红色旗帜,旗帜图案中的四颗小星环绕在一颗大星右侧呈半环形,大五角星象征着中国共产党,四颗小五角星象征着工人阶级、农民阶级、城市小资产阶级和民族资产阶级。 == 概要 == thumb|250px|悬挂在长城的五星红旗 该旗的设计者是来自浙江瑞安的普通公民曾联松。随着中国共产党在国共内战中取得胜利,1949年7月新政治协商会议筹备会发出了征集国旗图案的通告,曾联松设计并提交了他的国旗样稿。在2,992幅(一说为3,012幅)应征国旗图案中,曾联松的设计被选入38幅候选草图。经过多次讨论和少量修改,他的设计被选为了中华人民共和国国旗。1949年9月27日中国人民政治协商会议第一届全体会议通過中華人民共和國國旗為五星紅旗,以後歷次憲法均作同樣規定。在1949年10月1日的开国大典上,中国共产党主席毛泽东宣告了中华人民共和国中央人民政府成立,并按动电钮在天安门广场上升起了第一面五星红旗。 1990年6月28日,第七届全国人民代表大会常务委员会第十四次会议通过了现行的《中华人民共和国国旗法》,于同日" +915,"开始设计国旗图案。他想到中国共产党领导的工农红军是以五角星作为自己的标志,而且中国共产党又是中国人民的大救星,进而决定用五角星来象征它。而毛泽东在《论人民民主专政 》一文则指出当时人民由四个社会阶级(工人阶级、农民阶级、城市小资产阶级和民族资产阶级)组成,因此他决定用四颗小五角星来象征由四个社会阶级组成的人民。在确定了五颗金星的位置和大小后(他曾设想在旗面的中心置放五颗金星,但因其在视觉上过于局促、凝滞而放弃),他於八月中旬将自己的设计“红地五星旗”寄给了筹备会。 截至8月20日,国旗国徽评选委员会共收到了2992幅(一说为3012幅)国旗图案,郭沫若、陈嘉庚等筹备会成员也提交了他们设计的样稿。这些应征图案在临时选阅室内进行了展示,评选委员会从中精选出38份草图,并将之汇编入《国旗图案参考资料》,提交给新成立的中国人民政治协商会议讨论。曾联松的方案最初并未入选,后来在田汉的主张下才被收为“复字32号”“红地五星旗”,并根据小组讨论的意见去除了类似苏联国旗的锤子镰刀的标志。 9月23日上午,政协全体会议代表分组讨论了国旗方案,但并未形成定论。23日当晚,彭光涵向周恩来推荐了32号图案;周" +916,"接圓直徑為旗高十分之三,居左;四星較小,其外接圓直徑為旗高十分之一,環拱於大星之右,並各有一個角尖正對大星之中心點。國旗旗面之紅色象徵革命。星用黃色是為了在紅地上顯出光明。旗上的五颗五角星及其相互关系,象征中国共产党领导下的革命人民大团结。四颗小五角星各有一尖正对大五角星的中心,代表着围绕一个中心的团结。大五角星象征着中國共產黨,四颗小五角星象征着工人阶级、农民阶级、城市小资产阶级和民族资产阶级。 在曾联松最初对国旗的描述中,大五角星象征着中国人民,四颗小五角星象征着毛泽东在《论人民民主专政》中提出的中国人民四个阶级(工人阶级、农民阶级、城市小资产阶级和民族资产阶级);五颗星呈椭圆形状,乃是当时中国国土(包括外蒙古)的秋海棠叶形状;五角星使用黄色则是因为中华民族是黄色人种。 1949年通過的《关于中华人民共和国国都、纪年、国歌、国旗的决议》规定,中华人民共和国的国旗为“红地五星旗,象征中国革命人民大团结。”这也是国旗目前最为准确的官方定义。 为什么建国时会选择五颗星,有说法认为:“作为历史学家、军事家的毛泽东经常阅读记载有“五星出东方利中国”的《史记》《汉书》等书,中国传统的数术学和天" +917,"== === 规格 === 中国人民政治协商会议第一届全体会议主席团在1949年9月28日公布了国旗的制法。根据《中华人民共和国国旗法》第四条规定,制作国旗的企业由省、自治区和直辖市的人民政府指定。国旗的长宽之比为3:2。国家质量监督检验检疫总局和中国国家标准化管理委员会颁布的GB 12982-2004:国旗标准中亦给出了国旗的制法说明。 绘制方法 标准 #先将旗面划分为4个等分长方形,再将左上方长方形划分长宽15×10个方格 #大五角星的中心位于该长方形上5下5、左5右10之处。大五角星外接圆的直径为6单位长度 #四颗小五角星的中心点,第一颗位于上2下8、左10右5,第二颗位于上4下6、左12右3,第三颗位于上7下3、左12右3,第四颗位于上9下1、左10右5之处 #每颗小五角星外接圆的直径均为2单位长度。四颗小五角星均有一角尖正对大五角星的中心点 === 尺寸 === 《国旗法》还给出了国旗的五种尺度规格。其中,一号国旗主要用于大型运动会的开幕或闭幕仪式,以及作为大型会议场的背景旗。二号国旗适用于15米以上高度的旗杆上;三号国旗一般悬挂于10至13米高的旗杆;四号国旗用于9至10米" +918,"\Delta E_{ab}^{\,\bullet}\le 2.0 丝绸 黄色 32.4 0.450 0.463 均为\Delta E_{ab}^{\,\bullet}\le 2.0 棉布 红色 9.2 0.595 0.328 均为\Delta E_{ab}^{\,\bullet}\le 2.0 棉布 黄色 33.0 0.467 0.463 均为\Delta E_{ab}^{\,\bullet}\le 2.0 旗杆套 白色 78.0 - - 刺激值Y10不得低于78 根据《中华人民共和国国旗法》第九条规定,网络使用的国旗图案标准版本在中国人大网和中国政府网上发布。网络使用的国旗图案标准版本的色彩如下(PNG源文件中的sRGB色彩): * 红色 * 黄色 == 礼仪 == thumb|左|250px|中国大陆学校之升旗仪式。 === 升旗儀式 === thumb|250px|悬挂在江门新会供电局门口的中华人民共和国国旗和中国南方电网公司旗帜。 在举行升旗仪式时,应奏唱中华人民共和国国歌,参加者应面向国旗肃立并行注目礼,军人敬军礼,少先队员敬少先队礼。国旗应该早升晚降,在恶劣天气时可以不升挂" +919,"定人士逝世时,应下半旗志哀。其中包括在任的中华人民共和国主席、国务院总理、全国人大常委会委员长、国家军委主席、全国政协主席等正国级领导人,对中华人民共和国作出杰出贡献的人、对世界和平或人类进步事业作出杰出贡献的人等。下半旗的日期和场所由国家成立的治丧机构或国务院决定。此外,根据《国旗法》第14条第2款和第3款规定,在不幸事件或严重自然灾害导致重大伤亡时,亦可由国务院决定下半旗志哀。 下半旗时,应当先将国旗升至杆顶,然后降至旗顶与杆顶之间的距离为旗杆全长的三分之一处;降下时,应当先将国旗升至杆顶,然后再降下。据统计,截至2014年,中华人民共和国国旗在建国后曾下半旗约50次,其中47次为中外政要。另据《人民日报》历年报道,从1953年至2012年,天安门广场不少于17次为外国元首和政党领袖降半旗。此外,为悼念汶川大地震、青海玉树地震、甘肃舟曲特大山洪泥石流中的遇难者,全中国及驻外机构也降下过半旗。另外,中华人民共和国的国家公祭日期间,纪念地点也会下半旗志哀。 香港特别行政区政府除了在全国性的志哀活动中下半旗之外,还曾为香港旅行团在马尼拉被挟持事件、香港南丫海難及大埔公路雙層巴士翻側事故中" +920,"(降)国旗仪式》正式实施。1992年12月,在原来国旗班的基础上,扩建成立国旗护卫队,即中国人民武装警察部队北京市总队第二师十四支队二大队六中队。2018年1月1日起,中国人民解放军陆海空三军仪仗队和军乐团取代中国人民武装警察部队国旗护卫队,开始担负国旗护卫任务。2018年1月1日7时36分,天安门广场升国旗仪式,首次由中国人民解放军陆海空三军仪仗队执行。中国人民武装警察部队国旗护卫队不再负责升降旗任务,番号同时撤销。 在武警国旗护卫队时期,按照2001年3月天安门地区管理委员会颁布的《天安门广场升(降)国旗仪式组织管理办法》规定,天安门广场应当每天升挂国旗。分为节日升旗仪式和平日升旗仪式。每年元旦、春节、五一、十一和每月1日实施节日升旗仪式。节日升旗仪式由武警国旗护卫队38人、军乐团62人实施,行进时吹奏《歌唱祖国》,升旗时吹奏《义勇军进行曲》。平日升旗仪式由国旗护卫队38人实施,升旗时播放《义勇军进行曲》录音。在武警国旗护卫队执行时期升国旗时,《义勇军进行曲》共演奏3遍,时长2分7秒,与太阳经过地平线的时长对应。降旗由国旗护卫队单独实施,没有音乐。 自2018年1月1日解放军接替武" +921,"8分钟。降国旗则全年按66人编队方案实施,列队形式和行进动作与平日升旗仪式相同,不安排伴奏。每日升降旗时间以北京天文台提供的北京每日日出、日落时间为准。 ===其它场合=== 《中华人民共和国国旗法》对国旗的升挂场所或机构作出了详细的规定。其中,北京天安门广场、新华门,全国人民代表大会常务委员会、国务院、中国人民政治协商会议全国委员会、中央军事委员会、最高人民法院、最高人民检察院、外交部,出入境机构、港口、火车站和其他边境口岸、边防海防哨所属于每日应升挂的场所或机构。国务院各部门、地方各级人民代表大会常务委员会、人民政府、人民法院、人民检察院、中国人民政治协商会议地方各级委员会则属于在工作日升挂的场所或机构。全日制学校除寒暑假和星期日外,都应升挂国旗。此外,《国旗法》的第七、八条亦对其他情况下国旗的升挂作出了规定。第七条规定,国庆节、国际劳动节、元旦和春节,各级国家机关和各人民团体应当升挂国旗。第八条规定:举行重大庆祝、纪念活动,大型文化、体育活动,大型展览会,可以升挂国旗。 中华人民共和国交通部根据《国旗法》制定了《船舶升挂国旗管理办法》,规定:中国籍船舶中50总吨及以上的船舶,航行" +922,"红旗。而有台灣旅館為了对大陸遊客表现友好,也開始升起了中華人民共和國國旗。而國立成功大學也多次將中華人民共和國國旗與中華民國國旗並列懸掛,以迎接中國大陸訪客。中华民国共产党以五星红旗为党旗。台湾的极左派团体中华爱国同心会也经常在台北101等公众场合展示五星旗。2017年10月1日,中华统一促进党成员在張安樂帶頭下身穿印有中華人民共和國五星红旗,並標示為「我的國旗」的外衣,在台北街頭遊行示眾。 === 其它華人地區 === 隨著改革開放,很多中華人民共和國公民移民海外,改變了當地華人的結構;很多華人團體開始慶祝十一國慶,並在海外的中國城和華人眾多的地方升起五星紅旗。 === 太空和月球 === 1994年,美国华裔宇航员焦立中执行STS-65任务时将中国国旗带入太空。神舟五号任务中,中国人首次将五星红旗带入太空;嫦娥三号首次将五星红旗带至月球,国旗展示系统是中国探月工程的项目之一。 == 法律保護 == 《中华人民共和国刑法》第299条:「在公众场合故意以焚烧、毁损、涂划、玷污、践踏等方式侮辱中华人民共和国国旗、国徽的,处3年以下有期徒刑、拘役、管制或者剥夺政治权利。」《中华人民共和国国" +923,"不尊重。第九條也規定,以言詞、動作或散佈文書、又或以其他與公眾通訊的工具,公然侮辱國家象徵,又或對之不尊重者,處最高3年徒刑,或科最高360日罰金。依據第11條,違反第5條規定者,可罰澳門幣5千元至五萬元罰款。 == 类似旗帜与标志 == 五星红旗的颜色、布局及内涵与苏联国旗相仿,五角星为社会主义国家(如-{zh-cn:朝鲜;zh-hant:北韓;}-、越南等)常用标志。但苏联和朝鲜国旗的长宽比例为2:1,中华人民共和国和越南国旗采取3:2比例。李光耀在其回忆录中称,新加坡国旗的五角星是为了照顾华族民众希望效仿中华人民共和国国旗,在国旗上加五颗五角星,惟代表意義不同。 中国红十字基金会会标的整体造型也与五星红旗相似。中华人民共和国海关曾经使用的关旗为中华人民共和国国旗的右方下角加饰关徽的旗帜。中国人民轮船总公司曾使用在中华人民共和国国旗上添加原招商局旗上水波纹的旗帜。香港与澳门区旗上也有五星图案。 == 参见 == * 中国人民政治协商会议第一届全体会议 *《关于中华人民共和国国都、纪年、国歌、国旗的决议》(1949年9月27日) * 《中华人民共和国国旗法》 * 下半旗 (中华人民共" +924,"旅遊或旅遊业()是指个人的旅行遊览活动和為旅客提供休閒設施與服務產業藝術與建築索引典—旅遊業 ——於2011年4月11日查閱。是一种复杂的社会现象,涉及政治、经济、文化、历史、地理、法律等各个社会领域。 旅遊也是一种休闲娱乐活动,旅游业也是休闲产业的一种,具有移動性、异地性和暂时性等特征。旅遊人士稱為一般旅客、遊客。跟團旅遊時,共同旅伴稱為同團團員;自由行之旅客,亦稱為背包客。一般而言,旅游具有观光和游历两个不同的层次,前者历时短,体验较浅,后者反之,也称为深度游。 ==旅遊理論== 與旅行的概念不同,旅遊是指為了消費、休閒或公幹或其他原因,離開其慣常居住地與活動場所,到達其目的地停留不多於一年的遊覽活動,它是一種複雜的社會現象,旅遊要涉及社會的政治、法律、經濟、文化、歷史、地理、教育等各個社會領域。旅遊也是一種娛樂活動。世界旅遊組織對於旅客的定義是某人出外最少離家55英里(88.5千米)。 ===旅遊地點=== *自助旅遊:最近幾年新起的一種通過旅友互相組織,傳播的旅遊方式,更多存在於戶外探險、登山、自駕、徒步、自助類型的旅遊,過程更自由,更艱險,能更好的體會旅遊的樂趣。 *戶外旅遊" +925,"園、餐馆、賭場、商場、、剧院)。当然还有卖给游客的商品包括纪念品。 ==历史== ===古代=== 在古代,到一个人居住地以外的地方旅游休闲仅限于富裕阶层。他们偶尔会去遥远的地方,欣赏宏伟的建筑和艺术品,学习新的语言,体验新的文化,品尝不同的料理。 最早的旅游可以追溯到乌尔国王舒尔吉,虽然国王自诩是为了修路和建造驿站,给其他旅行者提供方便之用。 最早的休闲旅游可以追溯到公元前1500年的古埃及。 在罗马共和国时期,富人流行去这种地方做SPA和海滨度假。保萨尼亚斯在公元2世纪写书《希腊描述》。在中国古代,孔子花费14年周游列国,到过泰山,有孔子“登东山而小鲁,登泰山而小天下”的名句,而歷朝中,皇帝為了避暑、避寒,會離開皇宮去到行宮避暑避寒。 ===中世纪=== 到了中世纪,和佛教都有朝聖的传统。乔叟的坎特伯雷故事集和吴承恩的西游记,分别是英語文學和中国文学的经典之作。 10世纪到13世纪的宋朝涌现出了一些旅游作家,有苏轼、范成大。明朝有徐霞客。意大利的彼特拉克在1336年登旺度山后写了游记赞扬这次旅行,但批评他缺乏好奇心。勃艮第公爵诗人在1430年游览侏罗山后写了恐怖回忆录。 ===壮游" +926,"一直活跃到约1660年,直到1840年代大规模的鐵路運輸的出现,形成了标准的旅游行程。虽然这主要限于和有钱的,但是北歐新教徒有类似的旅行,并从18世纪中叶开始有南美、美国和其他海外年轻人加入。铁路和蒸汽船的出现使得旅游变得简单,中產階級也因此受益,这其中有,并流传了一句谚语叫“库克的旅行”(意为走马看花式的旅行)。 到了18和19世纪,壮游成了上流社会学生的象征,约翰·约阿希姆·温克尔曼的关于经典文化的霸权理论很受欧洲学术界的欢迎。艺术家、作家、旅行家(如歌德)肯定了意大利、法国、希腊在传统艺术领域的霸权地位。因为这些原因,壮游的主要目的地便是前往这些地方。 ===休闲旅游的兴起=== 休闲旅游可以关联到英国第一次工业革命时期,人们的闲暇时间增加。 起初,受益的群体是生产机器的拥有者、寡头政治、工厂主、贸易家。这其中也包含新出现的中產階級。 于1758年创办的是第一家正式的旅游公司。 旅游业这项产业起源于英国,这在很多地名中得以体现。在法国蔚藍海岸的度假胜地尼斯,一条著名的海滨步行道叫盎格鲁街(意为“英国人步行道”)。在歐洲大陸有许多历史名胜,宫殿酒店取名为“”、卡尔顿酒店、 美琪酒店" +927,"zgin and Medet Yolal, , p. 74。 旅游交通到20世纪早期开始有汽车,后来又有了飞机。交通的发展缩短了人们在旅途中花费的时间。 歐洲大陸,最早的海水浴場为1793年开发的海利根达姆,位于波罗的海。 到了20世纪中叶,地中海沿岸成为了大众旅游的热门目的地。 === 二十世纪社会主义国家中的疗养制度与疗养院 === 在苏联等社会主义国家中,出于政府提倡的社会主义道德和计划经济的影响,旅游活动一般会被疗养制度所代替。疗养制度和疗养院建立在与资本主义社会完全不同的价值观之上:是一种健康、效率和有组织的集体体验,而非享乐和放纵。一般来讲,工人与其它劳动者在一年内会拥有两到三次在全国其它疗养院休息的机会,其餐旅费用完全由国家政府支付。但在施行了经济改革政策的社会主义国家中,疗养制度往往式微。 ===利基旅游=== 利基旅游指的是近年来出现的形形色色的特殊旅游形式。比较普遍的利基旅游市场有: * 农业旅游 * 生育旅行 * 美食旅游 * 文化觀光 * 黑暗觀光 * 生態旅遊 * * * * LGBT觀光 * 醫療旅遊 * 影视旅游 * * 朝聖 * * 性旅遊 * 贫困旅游" +928,"nnis Tito成为首位进入太空的游客。2011年5月,维珍银河推出了其太空船2号飞机,允许人们以每座20万美元的广告价格进行2小时的太空旅行。 ==旅游業的問題== ==参见== *世界旅游排名 *自助旅遊 *户外运动 *旅遊地理学 *旅遊醫學 *旅遊景點 *自由行 *背包客 *遊學團 *旅行社 *旅行团 *领队 *导游 *旅遊稅 ==参考资料== == 延伸阅读 ==" +929,"鄧麗君(,),本名鄧麗筠,臺灣女歌手、演員及慈善家。她是20世紀後期華語流行音樂具代表性的人物,也是當代華語流行音樂的先驅之一。時至21世紀,她仍被視為全球華語及華人社群文化的代表人物之一。 鄧麗君出道於1960年代前期。1970至1980年代,她的歌曲演出等在全球各地大中華等社群中廣受歡迎;她的歌曲演出等因而將不同地區華語社群的當代流行文化相連結。 同時,亞洲許多地區也因她以多種語言演唱日本流行歌曲及多個地區的民謠等,在流行文化上相互產生連結與融合。 1990年代初,她逐漸退出流行音樂領域。 1995年5月8日,因哮喘引發呼吸衰竭,送醫途中貽誤最佳搶救時機,於泰国清邁猝逝,終年42歲。 她曾於臺灣及香港流行音樂界,分別獲頒金曲獎特別貢獻獎、金針獎等獎項,以紀念其事業成就。她也曾被中国网票選為「新中國最有影響力文化人物」,並被馬來西亞《南洋商報》評為「20世紀最具代表性華人歌手」。2010年,CNN將她列為過去50年最具影響力的20位音樂家之一。 此外,由於她在演藝生涯中經常為中華民國國軍官兵提供愛國歌曲演出等,她又被稱作「永遠的軍中情人」等。 == 生平 == === 家世 ===" +930,",其歌唱天分漸嶄露頭角。此時,鄧麗君7歲,是為歌唱生涯的萌芽期。由於分心歌唱,她沒有考入心中理想的公立中學,因而轉而考進私立金陵女中。 1964年,年僅11歲的鄧麗君參加中華廣播電台舉辦的黃梅調歌唱比賽,以一曲〈訪英台〉奪得冠軍;翌年以〈採紅菱〉在金馬獎唱片公司舉辦的歌唱比賽奪冠。其後,鄧麗君利用課餘時間參加正聲廣播公司舉辦的歌唱訓練班,學習歌唱技巧,以第一名成績結業。初中階段的鄧麗君,在課餘時間參加晚會演出,歌藝被「國之賓」歌廳老闆賞識,邀請在歌廳駐唱,以清純活潑形象定位,招牌歌曲正是〈一見你就笑〉。 隨着鄧麗君展開走唱生涯,漸無法兼顧演唱及求學,在1967年選擇休學展開歌唱事業。不到1年時間,鄧麗君成為歌廳、夜總會、餐廳及飯店等爭相邀請演出的歌手,更成為台北市西門町七重天豪華大歌廳的駐場歌手,接拍電視廣告,亦經常到參與勞軍義演。1967年,鄧麗君加盟宇宙唱片,發行個人第一張專輯《鄧麗君之歌第一集·鳳陽花鼓》。1968年,於台北中山堂參加賑濟菲律賓震災的演出,捐款新台幣一萬一千元。1969年中國電視公司啟播,鄧麗君獲邀主持晚間黃金時間播出的節目《每日一星》,並為中視首部電視連續劇《晶" +931,"7月,在越南海南醫院看望孤寡老人;1972年,再度參加白花油義賣,當選「慈善皇后」;6月,參加新加坡「歌樂飄飄慈善晚會」,為殘廢兒童救濟基金募款;1973年,於新加坡國家劇場出席遠東十大巨星慈善晚會。期間還出演電影《歌迷小姐》、《天下一大笑》等,在臺灣買下一座別墅,與馬來西亞富商林水成之子林振發相戀。各地華人對鄧麗君的慈善義舉熱情響應。 返台後,被台北美國學校錄取為插班生,主修英文香港鄧麗君歌迷會(2000)《鄧麗君逝世5週年紀念-香港故居開放展覽》特刊,第12-13頁。1974年主演喜歌劇《唐伯虎點秋香》、《西廂記》與舞台劇《女記者》。是年1月12日,為響應中華民國警察電台「雪中送炭」運動,與雙親赴警察電台,捐新台幣一萬元。此後幾乎每年的冬天,鄧麗君如果不在台灣,總會托人致送一筆捐款給《雪中送炭》節目,救濟貧困民眾;如果在台灣的話,必定親自參與和呼籲。 1976年,首次在香港利舞台舉行個人演唱會,門票銷售一空;後連續兩年在此地開辦個人演唱會。 === 赴日發展 === 1970年代初期,日本寶麗多唱片公司指派佐佐木幸男在香港尋找女歌手前往日本發展,並在東方歌劇院發現鄧麗君。隨後,舟木" +932,"救國基金。 1979年2月14日,鄧麗君持印尼護照入境日本,遭日本拘留。當時,鄧麗君因中華民國政府與日本政府已無官方邦交關係與雙方出入境管制程序之障礙(持中華民國護照入境日本須申請當地官方核發之渡航證明書,臺灣當地又有警備總部嚴加查核),而決定持印尼護照自香港搭乘中華航空CI116班機由東京羽田機場入境日本。同月22日,日本入國管理局東京事務所公佈調查結果,結果證明鄧麗君所持印尼護照並非偽造護照,她亦無為他人出售偽造護照,最後無罪開釋。同月24日,日本法務省裁決鄧麗君一年內不得再度入境,而持著蓋有美國入境簽證的中華民國護照前往美國,並在該國履行表演和唱片合約。 === 北美回臺 === 鄧麗君先到美國舊金山落腳,然後到洛杉磯的南加州大學進修英文,之後轉學到加州大學洛杉磯分校,學習日文、生物及數學。期間完成了〈甜蜜蜜〉和〈小城故事〉的錄製。4月,在舊金山、洛杉磯和溫哥華舉行演唱會。12月,與因《醉拳》一片大紅的成龍在美國相遇,兩人相互愛慕,但因成龍的不成熟,失望透頂下的鄧麗君於是離去,此後兩人便維持朋友身份。次年5月,受邀在林肯表演藝術中心演唱,獲紐約市長贈「金蘋果」胸針。7月,在紐約、" +933,"主持金钟奖颁奖典礼。1984年1月,在台北中華體育館先後兩天举办《十亿个掌声》演唱会,3月青年節,救國團總團部頒發給鄧麗君「十大傑出女青年」獎。 蔣經國擔任總統晚期,中國大陸方面邀請鄧麗君演唱,蔣派宋楚瑜與鄧聯絡,請其勿去,鄧麗君思考後應允,事後宋本想宴請鄧麗君以示歉意,但反被鄧麗君請喫飯。 === 人氣飆升 === 70年代後期,鄧麗君的歌聲已經傳入中國大陸並受到熱烈歡迎,但主流文化一直批評其「黃色」、「反動」、「靡靡之音」,並認為《何日君再來》是「漢奸歌曲」,1980年大陸音樂協會還召開西山會議予以嚴厲批判。1983年後,對鄧樂的批評有所減弱。 儘管如此,民眾還是反覆用卡帶錄下台灣電台所播放、偷偷地聽著鄧麗君的歌曲。1979年12月,大陸女排在亞洲排球錦標賽中戰勝日本,奪得冠軍,鄧麗君受霍英東之邀,在宴會中為隊員們演唱了兩首歌曲。1980年,載有17名大陸船員的荷蘭籍貨船停靠在基隆,船員們特地跑到台北購買鄧的錄音帶;次年,又有因故迫降中正國際機場的大陸桌球隊員,到免稅店搶購錄音帶;甚至有大陸飛行員駕機相投。1980年代初,在中國大陸,鄧麗君的盜版卡帶充斥市面,擄獲了民眾的心,《何日" +934,"幽情》。5月,宝丽金公司宣布邓丽君唱片自1975年起销量累计达500万张,创華語唱片销量纪录;發行第二張粵語大碟《漫步人生路》;到訪馬來西亞残障人协会与安老院,慰問孤獨廢疾者。10月,在馬尼拉演出。 1983年12月29日起至1984年初,鄧麗君於香港、台灣、新加坡、馬來西亞舉辦“鄧麗君15週年巡回演唱會”,開創多項先河,巡迴演出共有十萬餘人次觀賞,其中台灣站“十億個掌聲”由台灣電視公司首次面向大陸地區進行實況廣播。時至今日,大中華地區所銷售的鄧麗君在1982年至1984年於台港演出的DVD,銷量猶佳。同年,香港寶麗金和歌林唱片在《淡淡幽情》發行後結束合作關係,後續鄧麗君的華語專輯改由台灣金聲唱片和台灣寶麗金唱片在台灣地區代理發行。 === 風行日本 === 1984年,唱片公司也從寶麗多(Polydor)轉籍到金牛宮(Taurus)唱片,以荒木豐久作詞、三木剛作曲的〈つぐない(償還)〉再次於日本出道。該曲經由有線放送,一點一滴地匯集人氣後,一發不可收拾地大受歡迎,頒獲日本有線大賞等好幾個大獎。 1985年所發行的〈愛人〉一樣與荒木、三木合作,大受歡迎。鄧麗君藉此曲登上第36回NHK紅" +935,"重心逐漸轉移到英國和法國。 1988年,鄧麗君以700萬港元購入香港赤柱一棟雙層高西式別墅,並在裝修一年後於1989年正式入住定居香港,直至1995年逝世之前,香港亦成為鄧麗君最後一個家。 1989年,鄧麗君在六四事件前後表態支持參與學生;她在香港不顧周遭親友反對而參加了抗議集會。5月27日,她在跑馬地《民主歌聲獻中華》活動中懸掛手書“反對軍管”的牌子並演唱〈我的家在山的那一邊〉。6月21日晚,她又在電視直播中悲慟地演唱新曲〈香港〉。 1991年3月,儘管暫住巴黎,鄧麗君仍返回臺灣參與勞軍演出,並在金門前線馬山觀測所向大陸同胞喊話。5月,華東地區發生嚴重水災,鄧與一眾演藝界人士在香港舉行賑災演出。 1992年,鄧麗君出席在巴黎的六四事件紀念活動。 1993年,連續第三年返台參加勞軍演出。 隨著氣喘發作情況日益嚴重,鄧麗君在1994年12月底與男友保羅前往當時以空氣清新著稱且較少華人聚居的清邁調養度假,並開始著手寫作復出歌壇的歌曲。 1995年1月1日,鄧麗君因氣喘病發引致呼吸不順並前往當地蘭朗醫院求醫,但診斷結果顯示並無大礙。 === 泰國病逝 === 1995年4月底,鄧麗君與保羅入" +936,"」與她。時任臺灣省省長的宋楚瑜擔任她的治喪委員會主委並親自為其墓園「筠園」題名,行政院長連戰、前任行政院長郝柏村、時任國防部長蔣仲苓、時任臺北市長陳水扁等政治人物也親臨致哀。中國大陸中央電視台及台灣、香港、日本地區的大眾媒體也紛紛在新聞中發出哀悼她過世的訊息。 5月28日,眾人於臺北市第一殯儀館為她舉辦出殯儀式,她的遺體隨後在眾多歌迷目送下,安葬於金山的金寶山筠園墓園;她父母與二哥的遺體也先後安葬於該墓園。 == 個人生活 == 邓丽君18歲那年在馬來西亞演出,與造紙廠董事長林振發交往,兩人感情很好,但林振發突然心臟麻痺猝死,無緣結為連理。 1979年鄧麗君在美國留學時,曾與成龍交往,兩人和平分手,並維持好友關係。 1980年與馬來西亞「糖王」郭鹤年之子郭孔丞交往,1981年10月28日訂婚,並預定在1982年3月17日与郭孔丞举行婚礼,却因为男方的祖母郑格如(郭鹤年母亲)阻撓,就此分手了。据香港媒体报道,由于郑格如的观念比较保守,所以她对身为当红明星的邓丽君感到不悦。于是,她在谈论婚礼细节的时候,对邓丽君提出了三个苛刻的条件:“糖王”郭鹤年长子郭孔丞曾与这位歌坛天后订婚!但却因为TA" +937,"邓丽君早年信奉羅馬天主教,後為工作事業而改信佛教。 ==政治立場== 在台灣土生土長的鄧麗君,從小受出生於軍旅家庭的影響,與國民黨有著深厚的關係。出生時父親鄧樞是國軍中尉,鄧麗君非常直白地表達政治立場是在1980年台北國父紀念館演唱會上,主持人向鄧麗君求證關於大陸地區邀約她前往演唱之事情,她表示曾在報紙上看見相關訊息但無人前來接洽,並隨後說道:「如果,我去大陸演唱的話,那麼,當我在大陸演唱的那一天,就是我們三民主義在大陸實行的那一天。」1981年,鄧麗君在全台灣勞軍一個月,並為將士演唱,深得台灣國防部的表彰。而台灣民間稱她為「愛國歌手」、「軍中情人」。黃色歌曲:偷聽鄧麗君的日子 ,香港教育局,中國改革開放總論相關課題參考資料 1985年2月9日,鄧麗君在新加坡接受香港記者長途電話訪問時,公開表示:「身為一個藝人,有這麼多中國人喜歡聽自己的歌,心裡難免會有點兒想面對面唱給他們聽的衝動,但是我生長在臺灣,我一定會堅持我的立場,不可能去大陸演唱...」1985年香港記者專訪,鄧麗君鄭重聲明不會去大陸演唱! ,2021年7月13日,恒久的溫柔 永遠的巨星 鄧麗君部落格 1989年六四事件前後," +938,"月13日,吳燕玲,BBC中文網 == 語言能力 == 鄧麗君父母的家鄉方言均係中原官話,父母的家鄉雖分屬河北與山東,實則只距140公里不到,方言相近。父系一方,爺爺與父親都是河北省大名縣人,處河北省最東南角,毗鄰山東省界與河南省界,方言屬中原官話鄭曹片中國社會科學院和澳大利亞人文科學院(1987),《中國語言地圖集》,B3頁。。母系一方,外公與外婆是山東省靠河南省界上的東平縣人與梁山縣人,兩縣毗鄰,方言都是帶冀魯官話過渡性質的中原官話,母親成長於河南開封,開封方言屬中原官話鄭曹片。不管如何,鄧麗君的母語國語帶北方口音。 鄧麗君因練唱臺語歌曲而學習臺語,日常生活通話不成問題,但遇到生難詞彙或俗語就不行了。 鄧麗君在香港與日本長期發展歌唱事業,亦常上綜藝節目,能流利使用香港粵語與日語。又在居香港時,跟上海籍的香港演藝朋友學會了上海話。 其他方面,英語程度能在美國大學聽課,電視上曾接受葉麗儀的英語訪問。,法語因旅居法國的關係,能基礎溝通。還有一些只能背誦歌詞,但不足以口說的,如印尼語等。 == 大事年表 == * 1958年,至屏東市仙宮戲院附近學芭蕾舞。 * 1960年,開始公開歌唱表演。" +939,"」。 * 1976年,赴香港利舞台舉辦首次個人售票演唱會。 * 1977年,成為「香港第一屆金唱片頒獎禮」金唱片得主。這年發表〈月亮代表我的心〉、〈香港之夜〉和〈何日君再來〉等歌曲。其中最後一曲傳入中國大陸,空前轟動。 * 1978年,個人大碟《鄧麗君Greatest Hits》及《島國之情歌—第三集》同時獲香港第三屆金唱片。於日本以〈東京夜景〉獲得「正頑張中賞」。發表日語版〈何日君再來〉和〈夜來香〉等歌曲。 * 1979年,「香港第四屆金唱片頒獎禮」上,同時有3張大碟獲白金唱片獎,另有2張大碟獲金唱片獎。赴美國洛杉磯南加州大學(USC)進修兩年,學習日文、英文、生物及數學,4月首次在加拿大溫哥華舉行演唱會。發表〈小城故事〉和〈甜蜜蜜〉等歌曲,其中〈甜蜜蜜〉是翻唱印尼民謠,深入人心,歷久不衰。 * 1980年,榮獲台灣第15屆金鐘獎「最佳女歌星獎」,這次該獎項首次頒發。在美國紐約林肯中心、洛杉磯音樂中心登台。鄧麗君身在美國,然而其歌聲卻響遍神州大地,大江南北的民眾為鄧麗君歌聲而醉倒。據悉,連鄧小平也甚為欣賞鄧麗君獨特的演繹方式。在香港推出第一張粵語大碟——《勢不兩立》,瞬即達到白金唱片" +940,"唱片流行榜,停留榜內接近一年,刷新日本樂壇歷史記錄。鄧麗君也因此獲得眾多獎項,其中包括「年度有線大賞」、「最受歡迎歌曲賞」,也被提名角逐「日本唱片大賞」之「最優秀歌唱賞」。 * 1985年,在日本憑藉新歌《愛人》連續14周蟬聯日本有線點播榜冠軍,並再奪「有線放送大賞」,同時在日本樂壇創下兩項歷史記錄。《愛人》一曲不費吹灰之力便入選日本「第36回紅白歌合戰」。首次參與電視劇演出,〈愛人〉一曲更成為該劇主題曲(「愛人」在日文中為第三者之意)。8月返台與張菲主持「反盜錄、反仿冒」義演晚會。12月在日本NHK大會堂舉行演唱會,命名《唯一一次》,不幸的是,那次真的成為鄧麗君在日本『唯一一次』大型演唱會。 * 1986年,主持台視春節特別節目「與君同樂」。單曲〈時の流れに身をまかせ〉(〈我只在乎你〉日文版)連續三年拿到日本有線大獎和全日本有線放送大賞,創下外國藝人在日本歌壇最輝煌的紀錄。再度順利入選日本「第37回紅白歌合戰」。 * 1987年,繼續穿梭於香港、台灣、美加及法國等地,但已處於半退休狀態,除參與慈善演出外,甚少於公眾場合露面。赴紐約參加「國際公益金」義演。 * 1988年,名作詞家慎芝" +941,"婉約,深得風人意旨。高潔沁潤眾心,勇奪最佳女歌星榜首;英發楷模當代,榮登十大女青年班頭。美譽傳乎四裔,清歌騰於國際。參與勞軍活動,鼓舞士氣,柳營傳千古絕唱;推廣民族歌聲,伸張正義,神州享超鄧高名。乃復義不帝秦,行止弗入中土;忠以作孝,僑居永愛宗邦。風骨嶙峋,不讓鬚眉。平居熱心公益,頻為善行,藉舒時艱;每值局勢邅迍,輒捐巨款,蔚為國用。大節凜然,輝耀千古。先聖有言:志道、據德、依仁、遊藝,斯人有之。歎以英年遽逝,悼惜良深,應予明令褒揚,以資矜式。」(總統李登輝、行政院院長連戰) 中國國民黨中央委員會榮譽狀:「鄧麗筠同志一生忠黨愛國,奉行三民主義,熱心服務。在演藝事業中享譽世界,為國爭光,尤對國軍宣慰工作始終不渝,特追贈國光一等獎章,藉表敬忱。」(主席李登輝) 中華民國立法院院長王金平:「由於鄧麗君的天賦和努力,加上她傲人的情操,當年在台灣物資缺乏的時候,用她的歌聲和用她各方面的表現來振奮人心,跟國人一起打拚,鄧麗君在各方面的貢獻,包括經濟、文化等,對國家的影響極大。」 中華民國教育部部長和中央研究院副院長曾志朗表示非常喜歡鄧麗君的歌,聽到她的歌聲會讓人聯想到和平、純真、人間有愛,他最愛哼" +942,"在華人世界,根本無可取代。或許有人可得其甜,卻難得其潤;也許有人音質同樣柔美,卻不能有如此寬闊的音域。『靡靡之音』其實原沒有貶損的意思,中秋團圓賞月;千里懷人獨酌;或是兩情相悅;或是過年圍爐,鄧麗君獨特的嗓音,飽含著喜氣,正是全世界的中國人對於幸福的一種想望。」(出自溫柔雙城記中之文章:聽說幸福曾來過) === 中國大陸 === 她從1979年開始成為中國大陸流行音樂的標誌,擁有廣泛的歌迷,連當時中华人民共和国文化部部長劉忠德都是其中一員CCTV-4 2007年5月8日 《天涯共此時》播出紀念鄧麗君逝世十二周年專題節目《永遠的鄧麗君》。她柔和的歌聲穿透了兩岸。鄧麗君和當時的中國共產黨領導人鄧小平(老鄧)一起成為老百姓熟悉的名字,並流傳著「白天聽老鄧,晚上聽小鄧」、「只愛小鄧,不愛老鄧」等語。https://news.ltn.com.tw/news/world/breakingnews/2381849 兩韓關係回暖 南韓電視台播送鄧麗君《月亮代表我的心》 1980年4月,一些音乐人在北京召开了一场专门针对流行音乐取向的讨论会。在会上,一些人批判以邓丽君为代表的港台音乐,批評邓丽君的歌曲是" +943,"1979至1982年擔任耿飚秘書期間,習近平與耿飚的司機楊希連,「把那盤《小城故事》的磁帶都聽壞了」。 李谷一:「邓丽君是中国乐坛独树一帜、不可磨没的杰出艺术家。」 崔健:「邓丽君是代表一种自由创作的声音,流行歌曲开始真正是一种来自非政治性、非传统性的自由创造。」 于丹:「对于中国文化的意蕴来讲,她已经不是一个歌手,其歌艺成就已超越流行音乐的范畴与层次。」 陳光標:「我們大陸可以這麼說,每個人都喜歡鄧麗君的歌。這麼多年來,我只要聽歌……我就聽鄧麗君的歌。」 董文華:「鄧麗君是華語樂壇永遠無法逾越的高峰。」 白岩松:「如果有一个声音能让全世界的华人安静下来,那就是邓丽君的歌声。」 === 香港 === 黃霑:「這種嗓子一百年不見一個,所以大陸的中國人都那麼喜歡她是有道理的。她音樂感非常好,耳朵很好,你聽她沒有樂隊伴奏清唱的時候。這種功夫很難有,她音準非常非常地好」 成龍:「邓丽君是兰花,是最后的古典。作为一个艺人,能如此走红,有这么大成就,实在已是生荣死哀,值得大家爱戴与尊敬,流芳百世,永垂不朽了!」 王菲表示鄧麗君是她的偶像和啟蒙老師,「我从没想过也不认为自己可以超越邓丽君,她是我唯一" +944,"有合唱歌曲(但的確也有一些後期合成的合唱,比如與五木宏的合唱〈〉,以及〈〉)。2010年1月發行的《》是一張後期合成的合唱和重新編曲的鄧麗君日文歌曲精選集,在這張專輯中與鄧麗君跨越時空合唱的歌手有夏川里美、秋元順子、村上哲也、BEGIN等等。 日本歌手德永英明,於2010年4月特別發行《vocalist 4》翻唱專輯,緬懷鄧麗君逝世十五週年。此張專輯獲得連續四週冠軍,及單月冠軍,打破日本唱片史上翻唱專輯的紀錄。 === 韩国 === 在韓國,鄧麗君的代表作〈月亮代表我的心〉、〈甜蜜蜜〉是家喻戶曉的名曲。2018年南韓藝人姜山恚就曾赴北韓演唱〈月亮代表我的心〉,表達他出生於北韓的父親,在韓戰後無法再回鄉的思念之情。 === 马来西亚 === 1999年,马来西亚《南洋商报》邀请中文流行曲的专家,对20世纪具影响力的歌星一一评估,鄧位列榜首,《南洋商报》稱其為「最耀眼的星星」。 歌手梁静茹形容:没有邓丽君,就没有今天的梁静茹!邓姐可以说是我们大家永远最崇拜的一个巨星。 == 紀念 == === 紀念設施 === * 筠園——鄧麗君墓園,位於新北市金山區的金寶山。 * 麗君小鎮——位於大名縣" +945,"熊,及她每天出門必摸的幸運熊。 * 甜蜜蜜新生咖啡店——位於香港九龍尖沙咀梳士巴利道港鐵尖東站35號。甜蜜蜜新生咖啡店,是香港首間以鄧麗君為主題的咖啡店,由新生精神康復會開辦,「鄧麗君文教基金會」及社會福利署「攜手扶弱基金」資助。咖啡店2009年1月29日開業,聘請精神病康復者為服務生,提供就業及工作訓練機會。 * 鄧麗君音樂生活館——位於北京市前門台灣文化商務區內。 * 鄧麗君衣冠塚與紀念像——位於上海市青浦區福壽園。東方新聞:鄧麗君衣冠塚與紀念像落戶上海 2003年4月11日。 * 重庆杜莎夫人蜡像馆——位於重庆市南岸区南滨路长嘉汇购物公园,擺設邓丽君蜡像。 * 皇家美萍酒店——位於泰國清邁。鄧麗君生前最後居住的皇家美萍酒店套房第1502號如今已經業主改裝並命名為「Teresa Teng Suite」,為低調的紀念型景點。當沒人入住此房間時,遊客需付費購買配套方可進入套房參觀。內有擺放當年鄧麗君光臨酒店的照片,並播放鄧麗君的歌曲。 缩略图|右|250px|清迈皇家美平酒店1502房「Teresa Teng Suite」 === 紀念活動 === * 2010年5月14日,來自台灣及" +946,"11年1月7日,由香港電台主辦的「第33屆十大中文金曲頒獎音樂會」,設有緬懷逝世15年的鄧麗君環節,由車淑梅旁白,播放鄧麗君生前的片段及金曲。 * 2012年9月12日至17日,日本東京三越劇場陸續上演紀念鄧麗君誕辰60週年的舞台劇「戀人們的神話」() * 2013年1月26日於北京萬事達中心舉行了紀念鄧麗君60週年誕辰演唱會。。而位於臺北的中正紀念堂亦於同日起舉行了“追夢--永遠的鄧麗君”特展,至4月21日。追夢 永遠的鄧麗君特展 , 國立中正紀念堂管理處網頁。 * 2013年5月12日,臺北小巨蛋舉辦了「鄧麗君 追夢 何日君再來」紀念演唱會。鄧麗君 追夢何日君再來 紀念演唱會 臺北小巨蛋網站相關活動頁面 * 2013年5月19日,北京首都體育館舉辦了「追夢-巨星耀北京 紀念鄧麗君60周年演唱會」。王菲獻唱鄧麗君紀念會 稱:她是我啟蒙老師 大公網 * 2013年,台灣知名歌手周杰倫在台北和香港等地舉辦 「Opus Jay Chou 周杰倫 魔天倫世界巡迴演唱會」。並邀請鄧麗君當神秘嘉賓並跨時空合唱3個歌曲。 * 2015年5月9日,由財團法人鄧麗君文教基金會、鄧麗君文化事業有限公司共" +947,"月亮代表我的心〉為電影主題曲。 2003年 《絕種好男人》 王晶導演,张柏芝、任賢齊主演,〈月亮代表我的心〉為電影主題曲。 2000年 《站台》 賈樟柯導演,王宏偉、趙濤、梁景東、楊天乙主演,片中80年代場景使用鄧麗君的〈美酒加咖啡〉。 1999年 《半支煙》 葉錦鴻導演,曾志偉、謝霆鋒、馮德倫、舒淇等出演,鄧麗君的〈我只在乎你〉貫穿全片。 1998年 《千言萬語》 許鞍華導演,李麗珍、李康生、謝君豪主演,鄧麗君的〈千言萬語〉為該作主旋律,獲評第36屆金馬獎最佳劇情片、第19屆金像獎最佳電影。 1996年 《甜蜜蜜》 本片為紀念鄧逝世而作,陳可辛執導,張曼玉和黎明主演。故事發生的年代正是鄧麗君在日本乃至整個東亞最為活躍的巔峰時期,最後以她不幸因病去世這一事件為全片故事結尾的契機,榮獲第34屆金馬獎最佳劇情片和最佳女主角獎,及第16屆香港電影金像獎最佳電影、最佳導演等9項大獎,同時獲美國《時代周刊》評為1997年度十大佳片第二名。 1981年 《吾家有女初長成》 蘇月禾導演,譚詠麟、彭雪芬主演。鄧麗君配唱兩首插曲。 1981年 《馬騮過海》 張堅庭導演,陳友、鍾鎮濤、彭雪芬主演。鄧麗君演" +948,"開花》 本片由鄧麗君客串,楊道導演,林鳳嬌、秦祥林主演。 1977年 《望春風》 徐進良执导,嘉凌、白鹰、杨丽花主演,獲哥伦比亚第18届国际影展加达利那银像奖。1972年中華民國与日本断交,台湾仇日情绪大为提高。中影公司于1974年推出《英烈千秋》造成票房轰动,于是把抗战电影作为一年一度的招牌巨制,陆续拍摄了《八百壮士》、《梅花》、《笕桥英烈传》、《望春风》,鄧麗君演唱主題曲〈望春風〉。 1976年 《海韻》 本片取材自徐志摩的同名新詩,由李行導演,蕭芳芳、秦漢、胡燕妮、柯俊雄、崔福生主演,蕭芳芳在西班牙第五屆國際海洋影展憑此片奪得最佳女主角鏗魚獎,鄧麗君演唱主題曲〈海韻〉。 1975年 《一簾幽夢》 白景瑞導演,甄珍、謝賢、秦祥林、汪萍演出,鄧麗君演唱劉家昌創作的插曲〈失意〉。 1974年 《近水樓台》 李融之導演,林鳳嬌、鄧光榮、秦漢、徐楓主演,鄧麗君演唱插曲〈小詩〉。 1974年 《海鷗飛處》 本片是1970年代琼瑶电影重创辉煌的奠基作品,由李行導演,甄珍、鄧光榮、秦漢、謝賢主演,鄧麗君演唱插曲〈把爱埋在心窝〉與〈你愛哪一朵〉。 1973年 《彩雲飛》 本片是鄧麗君配唱的第一部琼" +949,"楊謹華、莊凱勛等演出。 2018年 《一代芳华·邓丽君》 大陸中央電視台音樂頻道製作,鍾鎮濤、张靓颖、吴彤、黄绮珊、程琳等演出,借助全息影像技术,展現他們與鄧麗君的同台表演。 2014年 《原鄉》 張國立、陳寶國、奚美娟主演,講述因國共內戰,而在台灣眷村發生的種種故事,彰顯了榮民們的原鄉之思、家國之愁,多次引用鄧麗君歌曲。本片獲第30屆中國電視劇飛天獎。 2007年 《甜蜜蜜》 鄧超、孫儷主演,故事發生於上世紀80年代,主角由於喜愛遭禁聽之鄧麗君歌曲而越走越近,伴隨著〈甜蜜蜜〉的歌聲,歷經坎坷,終成眷侶。 2007年 《台灣歌姬·鄧麗君》(日語原名《鄧麗君物語〜我的家在山的那一邊》/ 《》) 朝日電視台報導鄧麗君逝世消息,全世界30個國家和地區,在該年6月2日,以故鄉台灣為首,香港、新加坡等鄧麗君活躍的東南亞各個地區與國家,澳大利亞、日本,全歐洲、中東和近東的一部分,加上美國、加拿大,在各自的黃金時段同時播放。這是日本電視史上的首次嘗試。 2005年 《何日君再來》 2005年, 凤凰卫视与邓丽君文教基金会合作, 拍摄完成了一套纪念邓丽君的大型纪录片, 并在凤凰大视野中播出, 在当年引" +950,"鄧麗君 / 張立娜 編 / 北方文藝出版社 2013年5月15日 * 鄧麗君畫傳 / 師永剛 編 / 作家出版社 2013年5月1日 * 絕響—永遠的鄧麗君 / 策劃 鄧麗君文教基金會 /作者 姜捷 / 出版 時報文化出版企業股份有限公司 2013年1月25日 * 邓丽君全传 / 师永刚,楼河 着/北京联合出版公司/2012-08-01 * 何日君再来:邓丽君 / 卜梵 着/湖南师范大学出版社/2011-07-01 * 邓丽君画传(修订版) / 师永刚 等着/作家出版社/2010-01-01 * 我的家在山的那一邊—鄧麗君第十年的真相(附CD) / [日]有田芳生 著 郭麗蘭 譯 / 普金传播 2006年1月 * 十億個掌聲 鄧麗君傳 / 師永剛 昭君 方旭 編 / 鄧麗君文教基金會 / 鄧麗君文化事業有限公司 / 普金传播 2006年 * 鄧麗君 夢的延續 (Teresa Teng Love and Peace) / 鄧麗君文教基金會 / 普金传播 2005年12月10日 * 鄧麗君逝世十周年紀念特輯 / 鄧麗君文教基金會 / 貴州文化音像出版社 / 2006年4月 * 情迷邓丽君" +951,"997年8月 * 鄧麗君之夢 -- [日] 平野久美子 著 張正薇 高琦智 譯 / 尚登出版有限公司 1997年 * * * * * ==== 小說和散文 ==== * 最甜最美鄧麗君 / 陳彧 / 時代華文書局 / 2019/03/01 * 恰似你的温柔:永远的邓丽君 / 岸芷汀兰 / 北京联合出版公司 (2013年5月) * 歌迷小姐:鄧麗君成名史 正翔、 瓊雄 / 作家出版社 (2005-09出版) * 怎能遺忘鄧麗君 / 莊奴 口述,楊曦冬 編著/中國文聯出版公司/2005-09-01 * 何日君再來 / 平路 /天地圖書有限公司/2002-07-10 * 香草美人鄧麗君 / 竇應泰 / 北方文藝出版社 (1998年) * 鄧麗君的真實世界 / [日] 宇崎真 / 渡邊也寸志 著 魏裕梅 譯 / 臺灣先智 1997年 * 聽說幸福曾來過/張曼娟 * 溫柔雙城記/大田出版 * == 註釋 == == 参见 == 邓丽君 - 新芭网 :关于邓丽君的相关报导合集 == 參考文獻 == == 外部連結 == * * * * 鄧麗君文教基金會 * * * * Category:台灣女歌手" +952,"游戏,既可以指人的一种娱乐活动,也可以指這種活動過程。游戏的道具可以为玩具。在英語,體育比賽()亦是遊戲的一種,而體育運動亦是由遊戲演變出來。游戏是一種有組織的玩耍,一般是以娛樂為目的,有時也有教育目的。游戏不同於會有對應金錢報酬的工作,也不同於呈現美學或是概念元素的藝術。不過彼此之間的分界不一定很明確,像職業運動員的游戏和工作可能是一體,而像拼圖遊戲則同時具有游戏和藝術的成份在內。 游戏的主要成份有目的、規則、挑戰及互動。遊戲一般會有心理或是身體上的刺激,許多遊戲可以培養相關技巧,有體能性、教育性、模擬性或心理上的意義。 從西元前2600年起,遊戲就是人類經驗的一部份,出現在所有文化中,像烏爾王族局戲、塞尼特及播棋都是其中歷史相關悠久的遊戲。 ==定義== ===路德维希·维特根斯坦=== 路德维希·维特根斯坦可能是第一個試圖為遊戲下定義的哲學家,在其著作《哲学研究》中,维特根斯坦提出遊戲的幾個要素,像是玩耍、規則及競爭,但這些都無法適當定義遊戲。维特根斯坦認為人類用「遊戲」來稱呼許多不同的活動。不過隨著之後遊戲定義的提出,現在的哲學家認為维特根斯坦提出的不是最終的結論,像就認為Be" +953,",若玩家只能超越對手,但無法用影響其表現的方式攻擊對手,這稱為競爭(競爭包括賽車及花样滑冰),但若在遊戲中允許攻擊對手,這就是遊戲。 ===其他定義=== *在2008年獲頒雨果獎最佳互動電玩遊戲獎的Kevin Maroney曾說:「遊戲是一種具有目標和結構的娛樂形式。」 *國際遊戲者協會紐約分會會長曾說:「遊戲是一種藝術形式,其中參與者(或稱玩家)要作出各種決策,以便在追求目標的過程中通過遊戲令牌管理資源。」 *遊戲公司的行政總裁埃里克‧齊默爾曼(Eric Zimmerman)指遊戲是一種「為了得到某種結果而進行的有規則的活動」。 *遊戲哲學家伯納德‧舒茲(Bernard Suits)認為:「玩遊戲就是參與一個朝向特定狀態的活動,只能使用符合規則的特定方式,由於規則會使得可用方式的選擇性更少,接受這些限制唯一的理由是這樣才能讓遊戲進行。」又說:「玩游戏就是一种把时间花在无谓挑战上的自愿举动。」 ==遊戲元素及分類 == 遊戲可以依「玩家要做什麼?」分類,一般會稱為游戏性,遊戲的主要元素也包括道具及規則。 ===道具=== 遊戲常常會依玩遊戲時需要的道具來分類(例如、球、卡片、棋盤及棋" +954,"代表物(例如地產大亨),或是雙方的代表物有某種特定的關係(如西洋棋的將死)。 ===技巧、策略及運氣=== 游戏的道具及規則會影響游戏在技巧、策略及運氣的不同程度,也有可能是上述的組合。 包括動作上的技巧,如摔跤、拔河、跳房子及射擊運動,也包括心智上的技巧,例如西洋跳棋及西洋棋。策略遊戲在西洋跳棋、西洋棋、印度鬥獸棋、圍棋及井字棋中都會用到,一般會需要配合特殊的裝備。博弈遊戲強調其運氣成份,像賭博類遊戲(廿一點、麻將、輪盤)和蛇梯棋、剪刀、石頭、布等,多半需要牌或是骰子。 不過大部份的遊戲會用到技巧、策略及運氣中的二種甚至全部。例如美式足球及棒球都會用到技巧及策略,而、撲克及地產大亨會以策略及運氣為主。許多棋版類遊戲會用到技巧、策略及運氣,大部份吃磴遊戲的運氣成份較小,像戰國風雲、卡坦岛及卡卡頌等策略式遊戲也是如此。 ==游戏行為理论== 许多学者发现,游戏不仅存在于人类当中,在许多哺乳類動物裡也存在着大量的游戏行为。游戏历史非常悠久,但是作为理论进行研究是在近代才开始的。 对游戏的本质的研究还正在发展,目前还没有一个最终性的认识结论。 游戏理论主要有: ===本能说=== 德国诗人和" +955,"= *席勒,德国诗人和剧作家 *赫伯特·斯宾塞,英国哲学家 *谷鲁斯,德国生物学家 *约翰·赫伊津哈,荷兰学者 ==游戏著作== *《游戏的人》 ==遊戲的種類== 遊戲有很多種,在不同的媒體會以不同型式出現,使用的道具或參與的人數不一樣也會衍生出不同類型的遊戲。其中包括: ===人手游戏=== 人类用人體(如雙手、雙腳)或使用玩具娱乐自己。較為常見的有「石頭、剪子、布」,有時體育活動,甚至武術比試也被人們視為遊戲。常用的玩具有布偶、球、冷兵器等。 ===桌上遊戲=== 桌上遊戲用來描述任何在桌子或平面上玩的遊戲,其中包括版圖遊戲、紙筆遊戲、紙牌遊戲、骰子遊戲,近年來桌上遊戲的設計者越來越有把許多動作、聲音、節奏、道具、圖片、公仔、故事背景,甚至是如動畫、影視、音樂、舞蹈等藝術、文化及多媒體等素材加入遊戲的趨勢,提供玩家或玩家們更多感官的刺激和不同於以往的經驗,以提升遊戲的樂趣與品質。 ====版圖遊戲==== 版圖遊戲用來描述將圖文符號畫在硬板上作為紀錄工具的遊戲,也就是需用棋盘及棋子进行的游戏,有包括常見的棋類遊戲,象棋是其中一種。參與人數可以是一人,二人,甚至多人。飞行棋、围棋等" +956,"、智力类游戏、难题游戏 此类游戏要求玩家运用自己的智慧,来解决游戏中的难题,以达到游戏过关的目的。 最新的商业游戏通常添加动作要素,使得益智类游戏可以通过游戏的方式锻炼游戏者的脑、手、眼的协调性,有一定维持智力,开放思维的效果。 ===多人在线战斗竞技场游戏=== 简称为moba类游戏,起源于 魔兽争霸III:混乱之治 的一个叫做《遗迹保卫战》(英语:Defense of the Ancients)的模式,因其瞬息万变的游戏局势与多元的游戏因素而备受欢迎。这类游戏多通过与队友合作以达到击败敌对阵营的目的。 当前比较知名的moba类游戏有英雄联盟,DotA ===其他=== 其他的一些遊戲類型包括戶外遊戲、有好玩的遊戲及數學遊戲等等。 ==參見== *桌游 * 电遊 * * 博弈论 * 遊戲者 * * * * 游戏化学习 * * 玩具 * 遊戲研究 * 賭博成癮 ==参考文献== *Homo Ludens, Beacon Press, ISBN 978-0-8070-4681-4 (英文版) *《游戏的人》,中国美术学院出版社,1997年, ISBN 978-7-81019-529-4" +957,"自然数(natural numbers)按ISO 80000-2和ISO 2382定义,指非负整数 ( 0, 1, 2, 3, 4, \ldots);此定义相同于集合论和计算机科学领域中,认为0属于自然数。但在数论领域中,认为0不属于自然数,因而按数论描述,自然数会同义于正整数。为免歧义,可直接以术语“非负整数”代替自然数称之。 数学中,一般以\mathbb{N}代表以自然数组成的集合。自然数集是一個可數的,無上界的無窮集合。非零自然数即指正整数( 1, 2, 3, 4, \ldots)。 自然数可用于计数(如:桌子上有“三”个苹果)和定序(如:国内“第三”大城市)。 == 符号 == 数学家们使用N或\mathbb{N}来表示所有自然数的集合。较早的教科书也有使用J来表示这一集合的情况。 为了消除是否包含0的歧义,有时通过上、下标的形式表示集合中是否包含0: * 自然数:\mathbb{N}_0 = \mathbb{N}^0 = \{0, 1, 2, \dots\} * 非零自然数:\mathbb{N}^* = \mathbb{N}^+ = \mathbb{N}_1 = \mathb" +958,"m的子集。 == 性质 == === 无限性 === 自然数的集合是无限集。根据定义,这种无限称为可数无限。可以与自然数建立双射关系的所有集合都具有这种无限性,称作,这个集合的势为\aleph_0。 === 可加性 === 自然数加法可经a+0=a及a+(b+1)=(a+b)+1递归定义而成。因而得出交换幺半群(N,+),是由1生出的自由幺半群,其中幺元为0。此幺半群服从消去律,可嵌入一群内:最小的是整数群。 === 可乘性 === 同理,自然数乘法\times可经a \times 0=0及a \times (b+1)=ab+a 得出。 === 加乘关系 === 而(N, \times)亦是交换幺半群;\times和+符合分配律: :a \times (b+c)=ab+ac。 === 有序性 === 我们说a \le b当且仅当有自然数c使得a+c=b。(N, \le)是一个良序集,即每个非空子集都有一个最小的自然数。 此序也和加法及乘法兼容,即若a,b和c都是自然数且a \le b,则a+c \le b+c及ac \le bc。 === 可除性 === 给定两个自然数a和b,其中b" +959,"数列”王好民,《谈谈中学数学中的“0”》。曲阜师院学报(自然科学版),1979年03期。。在2000年左右之后的新版中小学教材中,普遍将0列入自然数。(沧州市第一中学)李元星,潘峰,《关于0是自然数的探讨》。教育实践与研究,2004年01期。(江苏省连云港市墟沟实验小学)傅海洋,《“0是自然数”引发的教学问题》。现代中小学教育,2007年08期。 認為自然數不包含零的其中一個理由是自然數所指為自然界中存在的數,例如一棵大樹、兩條魚、十億個細胞等等,而鮮少有人說零個物品。 国际标准《量和单位 第十一部分:物理科学和技术中使用的数学标志与符号》(已被取代)中,从集合论角度规定:符号 \mathbb{N} 所表示的自然数集是包括正整数和0。 中国大陆于1993年制定的强制性国家标准《物理科学和技术中使用的数学符号》(GB 3102.11-93)参照国际标准ISO 31-11規定:\mathbb{N}表示“非负整数集;自然数集”,\mathbb{N}=\{0,1,2,3,\ldots\}。但自2017年3月23日起,该标准转化为推荐性标准,不再强制执行。 == 推广 == 自然数用于计数时称之" +960,"代数数域是数学中代数数论的基本概念,数域的一类,有时也被简称为数域,指有理数域\mathbb{Q}的有限扩张形成的扩域。任何代数数域都可以视作\mathbb{Q}上的有限维向量空间。 对代数数域的研究,或者更一般地说,对有理数域的代数扩张的研究,是代数数论的中心主题。 ==定义== ===预备知识=== 代数数域是域的一类。域是装备了两个二元运算(通常称之为“加法”、“乘法”)的代数系统。这两种运算各自满足结合律与交换律,完全可逆,同时乘法对加法满足分配律(详细定义参见域)。域的一个重要的例子是有理数域\mathbb{Q}。 ;域的扩张 域的扩张研究各类域之间的关系,最早的应用包括多项式方程一般求根公式问题等。在给定的域中加入不属于此域的元素(一般以集合记录),规定相互间的运算法则后,“最小的”将它们都包含在内的域“最小的”指所有同时包含和的域的交集。称为“(添加中元素得到)的扩域”。称是的子域。一般将“到的域扩张”记作⊂或/。 ;向量空间 另一个基础概念是向量空间。向量空间,特别是有限维向量空间的概念是三维空间以及其中向量概念的推广(具体定义参见向量空间条目)。以某个域为系数域的向量" +961,"bb{Q} 的数构成的集合。可以证明,\mathbb{Q}(i)是域,而且是\mathbb{Q}向量空间,以\{1, i \}为基,空间维数是2。所以\mathbb{Q}(i)是\mathbb{Q}的二次扩张,[\mathbb{Q}(i) : \mathbb{Q}] = 2. 给定不是完全平方数的正整数或相反数不是完全平方数的负整数,二次域\mathbb{Q}(\sqrt{d})在\mathbb{Q}中添加的平方根而得的扩域。与高斯有理数域类似,可以证明\mathbb{Q}(\sqrt{d})是\mathbb{Q}向量空间,以\{1, \sqrt{d} \}为基,空间维数是2,即[\mathbb{Q}(\sqrt{d}) : \mathbb{Q}] = 2. 考虑多项式方程x^n = 1的个复根\xi_1, \xi_2, \cdots , \xi_n,它们被称做次单位根,具体可以写作: ::\xi_i = e^{\frac{2i\pi}{n}}, \; \; i \in \{0, 1, \cdots, n-1 \}. 在\mathbb{Q}中添加\xi_1, \xi_2, \cdots" +962,"假设大于1。。将看作是维\mathbb{Q}向量空间,在中任意选一个不属于\mathbb{Q}的数,它可以被看作是维\mathbb{Q}向量空间中的一个(非零)向量。考虑以下的 + 1个向量: ::1, z, z^2, \cdots, z^m 它们都属于。根据向量空间的性质,它们是线性相关的。即存在不全为零的 + 1个有理数:a_0, a_1, \cdots , a_m,使得: ::a_0 + a_1 z + \cdots + a_m z^m = 0. 考虑非零多项式P = a_0 + a_1 X+ \cdots + a_m X^m,P(z) = 0,即是多项式P的根。所以是代数数。由上可知,任一代数数域的元素都是代数数。 ==代数整数== 代数整数是指能够成为某个首一整数系数多项式的根的数。显然代数整数是一种代数数。任何整数都是一次整系数多项式的根,因此是代数整数。给定代数数域,中所有代数整数构成一个环,称作中的(代数)整数环,也称为整数环,记作\mathcal{O}_F。例如\mathbb{Q}上的代数整数环就是\mathbb{Z},因此在代数数域研究中\mathbb{Z}也被称作" +963,"。这种“理想的唯一素分解”可部分弥补“代数整数一般不能唯一素因子分解”的不足,在历史上使代数数论发展起来。 ==代数数域的基== ===整数基=== 设为次代数数域,的整数基是任一由个整数组成的集合: ::B = \{b_1, b_2, \cdots, b_n \} 使得任一个整数都能唯一地表示为这个整数的整线性组合在不计顺序的情况下。,即: ::\forall x \in \mathcal{O}_F, \; \; \exists ! \; (m_1, m_2, \cdots, m_n) \in \mathbb{Z}^n,使得x = m_1 b_1 + m_2 b_2 + \cdots + m_n b_n. 换句话说,整数基是\mathcal{O}_F作为自由\mathbb{Z}模的基。给定的一组整数基,可以证明,所有中元素都可以唯一地表示为其中元素的有理线性组合,即: ::\forall x \in F, \; \; \exists ! \; (q_1, q_2, \cdots, q_n) \in \mathbb{Q}^n,使得x = q_1 b_1 + q_2 b_2 + \cdo" +964,"heory, CRC, 1999 * Ram Murty, Problems in Algebraic Number Theory, Second Edition, Springer, 2005 * * * * Andre Weil, Basic Number Theory, third edition, Springer, 1995 Category:代数数论 Category:域论" +965,"数域是近世代数学中常见的概念,指对加减乘除四则运算封闭的代数系统。通常定义的数域是指复数域\mathbb{C}的子域。“数域”一词有时也被用作代数数域的简称,但两者的定义有细微的差别。 ==定义== 设\mathcal{P}是复数域\mathbb{C}的子集。若\mathcal{P}中包含0与1,并且\mathcal{P}中任两个数的和、差、乘积以及商(约定除数不为0)都仍在\mathcal{P}中,就称\mathcal{P}为一个数域。用域论的话语来说,复数域的子域是为数域。 任何数域都包括有理数域\mathbb{Q},但并不一定是\mathbb{Q}的有限扩张,因此数域不一定是代数数域。例如实数域\mathbb{R}和复数域\mathbb{C}都不是代数数域。反之,每个代数数域都同构于某个数域。 ==例子== 除了常见的实数域\mathbb{R}和复数域\mathbb{C}以外,通过在有理数域中添加特定的无理数进行扩张得到的扩域也是数域。例如所有形同: :a + b\sqrt{2}, \; \; a, b \in \mathbb{Q} 的数的集合,就是一个数域。可以验证,任何两个这" +966,"{A}的子域。由于虚数单位也是代数数,所以\mathcal{A}不是\mathbb{R}的子域。另一方面,自然对数的底以及圆周率都不是代数数,所以\mathbb{R}也不是\mathcal{A}的子域事实上\mathcal{A}的元素个数是可数的,所以元素个数不可数的\mathbb{R}不可能是\mathcal{A}的子域。。 ==注释== ==参考来源== Category:代数结构 Category:抽象代数" +967,"數論()是纯粹数学的分支之一,主要研究整数的性質,被稱為「最純」的數學領域。 ==简介== 正整数按乘法性质划分,可以分成質数,合数,1,質数產生了很多一般人能理解卻又懸而未解的問題,如哥德巴赫猜想,孿生質數猜想等。即,很多問題虽然形式上十分初等,事实上却要用到许多艰深的数学知识。这一领域的研究从某种意义上推动了数学的发展,催生了大量的新思想和新方法。數論除了研究整數及質數外,也研究一些由整數衍生的數(如有理數)或是一些廣義的整數(如代數整數)。 整数可以是方程式的解(丟番圖方程)。有些解析函數(像黎曼ζ函數)中包括了一些整數、質數的性質,透過這些函數也可以了解一些數論的問題。透過數論也可以建立實數和有理數之間的關係,並且用有理數來逼近實數(丟番圖逼近)。 ==历史== ===古代=== 數論早期也稱為算術,而算術一詞則表示「基本運算」 ,在现代数论诞生前,早期铺垫有三大内容: #欧几里得证明-{zh-cn:质数; zh-tw:質數;}-无穷多。 #寻找-{|zh-cn:-{zh-cn:质数; zh-tw:質數}-; zh-tw:質數;}-的埃拉托斯特尼筛法;欧几里得求最大公约数的辗转" +968,"* 費馬小定理 (1640), Letter XLVI from Fermat to Frenicle, 1640, cited in ,若a不是質數p的倍數,則a^{p-1} \equiv 1 \pmod p. * 若a和b互質,則a^2 + b^2無法被任何除4後同餘-1的質數整除, cited in ,而且每個除4後同餘1的質數都可以表示為a^2 + b^2.,這二個是在1640年證明的,在1649年他在寫給惠更斯的信上提到他用无穷递降法證明的第二個問題,費馬和在其他平方形式上也有一些貢獻,不過其中有些錯誤及不嚴謹之處。 * 向英國的數學家提出了求解x^2 - N y^2 = 1的挑戰(1657年),但在幾個月後就由Wallis及Brouncker證明。費馬認為他們的證明有效,但用了一個在其中未經證明的演算法,費馬自己是由无穷递降法找到證明。 * 發展許多找亏格0或1曲線上點的方法,作法類似丟番圖,有許多特殊的步驟,使用了切線法構建曲線,而不是用割線法。 * 證明x^{4} + y^{4} = z^{4}不存在非尋常的正整數解。 費馬在1637年聲稱(費馬最後定理)證明了對於大於" +969,"圖方程:歐拉研究一些虧格為0或1的丟番圖方程,特別的是他研讀丟番圖的著作,試圖要找到系統化的方法,但時機尚不成熟,幾何數論才剛形成而已。歐拉有注意到丟番圖方程和椭圆积分之間的關係。 == 分支 == ;初等數論:意指使用不超過高中程度的初等代數處理的數論問題,最主要的工具包括整數的整除性與同餘。重要的結論包括中國餘數定理、費馬小定理、二次互反律等等。 ;解析數論:借助微積分及複分析的技術來研究關於整數的問題,主要又可以分為與加性數論兩類。積性數論藉由研究積性生成函數的性質來探討質數分佈的問題,其中質數定理與狄利克雷定理為這個領域中最著名的古典成果。加性數論則是研究整數的加法分解之可能性與表示的問題,華林問題是該領域最著名的課題。此外例如篩法、圓法等等都是屬於這個範疇的重要議題。 ;代數數論:引申代數數的話題,關於代數整數的研究,主要的研究目標是為了更一般地解決不定方程的問題,而為了達到此目的,這個領域與代數幾何之間有相當關聯,比如類域論(class field theory)就是此間的顛峰之作。 ;算術代数幾何:研究有理係數多變數方程組的有理點,其結構(主要是個數)和該方程組對應的代數" +970,"GNU-{zh-tw:自由文件授權條款;zh-hk:自由文件特許條款;zh-cn:自由文档许可证;}-(,缩写为GFDL)是一個版權屬左(copyleft,或稱「著佐權」)的內容開放的版權授權條款。它是由自由軟體基金會為了GNU計劃於2000年發佈的。 該授權條款適用於所有電腦軟體文件以及其他參考及指導材料。授權條款規定,所有使用GNU條款的材料產生的衍生品,不論是經過修改或轉載,也都必須採用GNU自由文件授權條款。採用該授權條款的材料可以用以商業用途,但必須允許任何人在遵守該協定下進一步修改或散發材料。 GNU自由文件授權條款是設計用於使用手冊、教科書、參考資料、指導性質的素材等。然而,它可以應用在任何文字作品。在網路上採用GFDL發佈的文件協作計劃中,维基百科是規模最大的计划之一。 == 歷史 == GNU自由文件授權條款最早在1999年以草稿的形式發佈;2000年3月,經過改版後的自由文件授權條款以1.1版正式發佈,接著1.2版在2002年12月發佈。2007年12月1日,吉米·威爾士宣布,維基媒體基金會、自由軟體基金會、創作共用等經過長期的討論與協商,決定修改GNU自由文件授權" +971,"== 次要章節可以被指定為「不变章節」。被指定為不变章節的文字不可以被修改或刪除。維基百科並未使用不变章節。 === 相容性 === GNU自由文件授權條款與GNU通用公共授權條款雙向不相容,導致範例-{}-程式碼必須雙重授權才能既使用在文件又使用在程式內。這點受到批評。 === 列印的重擔 === GNU自由文件授權條款要求在複製時附上「本『授權條款』、版權聲明、說明本『授權條款』效力及於『文档』的聲明」,若未遵守此條件則不能散佈、分發複製後的文档。這表示被授權者在列印以GNU自由文件授權條款發佈的單篇文章時(例如,維基百科的單一條目),必須將完整授權條款同時列印出來,而授權條款及聲明的長度可能比文章本身大得多。 === 透明格式 === 「透明格式」是個較為複雜的概念。它要求文档的格式必須是「廣泛使用的軟體」可以編輯的。文档的透明格式相當於程式的源碼。這個概念是為了確保文档可以易於被修改。该许可证也举出了一些「透明格式」的例子:没有标记的纯ASCII、Texinfo输入格式、LaTeX输入格式、使用可以公开取得其DTD的SGML或XML、合乎标准的简单HTML、PostScript" +972,"中國共產黨,簡稱中共,是中华人民共和国的執政黨。该党创立于中華民國北洋政府時期,由陳獨秀和李大釗等领导组织,於1920年8月草创,自称社会党,1921年7月23日同当时数个地方共产主义小组与中国留学生共产主义组织正式组建为中国共产党,1922年加入共产国际接受指导和援助,并与中國國民黨合作,共同发起国民革命运动。1927年第一次国共合作破裂後,两黨进行了長達10年的军事斗争。1930年代起,中國共產黨逐渐摆脱共产国际的影響,改由以毛泽东为领导的本国党员派系掌权,随后于1931年成立了中國第一个由共产党建立和执政的苏维埃政权:中华苏维埃共和国。在抗日战争期间,共产党与国民黨进行了第二次国共合作,遂建立抗日民族统一战线。1945年抗战胜利后,中国共产党组建并领导中國人民解放軍在第二次國共內戰中擊敗中国国民党及其领导的中華民國國軍,進而取得中國大陸地區及其絕大多數沿海島嶼的統治權,於1949年建立中华人民共和国,成为执政党并执政至今。自1980年代開始,中國共產黨開始排除意識形態問題和许多非共產主義的政黨建立关系。1989年东欧剧变和1991年蘇聯解體後,中國共產黨開始加强和其它社會主義國家" +973,"義、毛澤東思想、鄧小平理論、“三个代表”重要思想、科學發展觀和习近平新时代中国特色社会主义思想等意识形态,並將中國共產黨表述為:「中國工人階級的先鋒隊,同時是中國人民和中華民族的先鋒隊,是中國特色社會主義事業的領導核心,代表中國先進生產力的發展要求,代表中國先進文化的前進方向,代表中國最廣大人民的根本利益。黨的最高理想和最終目標是實現共產主义。」以马克思列宁主义、毛泽东思想作为行动指南。在毛澤東时期,中共领导下的中华人民共和国以苏联为师,进行社会主义改造,并建立起計劃經濟體制。此后中共提出了大跃进、人民公社等过于激进的经济政策,并在“无产阶级专政下继续革命的理论”指导下发动了文化大革命。文革结束后,中共在十一届三中全会上调整了党的路线,要求实事求是,停止使用“以阶级斗争为纲”的口号,鄧小平等人认可「實踐是檢驗真理的唯一標準」,决定改以经济建设为中心。此后邓主导了改革开放,改变缺乏弹性的计划经济体制,引入西方资本主义下的市场经济并建立社會主義市場經濟,从而发展“中國特色社會主義”道路至今。 == 名称标识 == 1920年9月1日,党派名称最早在陳獨秀在《新青年》發表的《對於時局之我見》" +974,"方1/4部分缀党徽图案”等。 中國共產黨黨旗採用紅色背景視為象徵革命、鎚子鐮刀象徵工人和農民的工具,黃色標誌則象徵光明,這也意味著中國共產黨代表人民群眾及其利益。發展至今日中國共產黨黨旗具有5種尺寸,其尺寸分別為橫388公分,豎192厘米(1號);橫240公分,豎160公分(2號);橫192公分,豎128公分(3號);橫144公分,豎96公分(4號);橫96公分,豎64公分(5號)。1966年9月21日中共中央辦公廳印製《中國共產黨黨旗黨徽製作和使用的若干規定》,其中指出中國共產黨的黨徽黨旗是中國共產黨的官方符號和象徵。之後則于2002年11月在中國共產黨第十六次全國代表大會通過《中國共產黨章程》修正案,在新增加的第53條中提到:「中國共產黨的黨徽黨旗是中國共產黨的象徵和標誌。黨的各級組織和每一個黨員都要維護黨徽黨旗的尊嚴。」2021年6月26日,中共中央发布《中国共产党党徽党旗条例》,进一步明确了党旗和党徽的制作、使用等有关规定。并在其附录中对党旗、党徽的制作标准及方法进行了详细说明。 中国共产党的官方英文翻译是Communist Party of China,缩写为CPC。而英文媒" +975,"的中国共产党第一次全国代表大会,剛組建的中国共产党正式成立並開始運作。 上海共產黨早期組織成立後,實際上成為中國各地建黨活動的聯絡中心。1920年10月,李大釗、張申府和張國燾發起成立北京共產黨早期組織,並且由李大釗擔任負責人。此后,羅章龍、劉仁靜、鄧中夏、高君宇、何孟雄、繆伯英、范鴻劼、張太雷等北京大學馬克思學說研究會成員先後加入。儘管李大釗和陳獨秀就是否要在中華民國政壇從事政治改革之論點有所差異,然而兩人都把俄國十月革命作為開創性的行動,認為它預示著許多被壓迫的國家即將進入新的時代。1920年秋季,施存統、周佛海在日本建立旅日共產黨早期組織,由施存統為負責人。同年秋冬之際,毛澤東、何叔衡等人在長沙以新民學會骨幹為核心,秘密組建共產黨早期組織。1920年年底至1921年年初,王盡美、鄧恩銘等人則在濟南建立共產黨早期組織。1921年春季陳獨秀聯合譚平山、陳公博、譚植棠等人重新組建廣州共產黨早期組織,並且先後由陳獨秀和譚平山擔任書記。同一年張申府、周恩來、趙世炎、劉清揚等人在法國巴黎也建立了由留學生組成的共產黨早期組織,並以張申府為負責人。中国当代学术界,将这八个小组视为中国共产党的启始" +976,"發展初期絕大部分運作經費是由總部設在莫斯科的第三國際所提供,其作為國際共產主義運動的一部份而接受第三國際的指揮。而後者提供的資金來源除了美元和盧布外,還包括有貴重的珠寶、鑽石、鴉片等。1923年1月12日,第三國際作出《共產國際執行委員會關於中國共產黨與國民黨關係問題的決議》,系統性地闡述了對於國共合作問題的指導性意見。1923年6月,中國共產黨第三次全國代表大會提出全體中共黨員以個人名義加入中國國民黨、並與中國國民黨建立「革命統一戰線」的方針。隔年1月召開的中國國民黨第一次全國代表大會,同樣也確定了「聯俄、容共、扶助農工」的三大政策。之後10名中國共產黨黨員入選成為中國國民黨中央執行委員會委員或者候補中央執行委員會委員,至此形成第一次國共合作關係。但是當孫中山於1925年3月逝世後,蔣中正開始排斥中國共產黨。1926年3月中山艦事件後,國共關係陷入緊張。1927年,蔣中正率領部隊進駐當時由共產主義武裝成員控制的上海,并於1927年4月12日發起大規模捕殺中國共產黨黨員和支持人士的四一二事件。之后蔣中正試圖進攻武漢,但是遭到中國共產黨將領葉挺軍隊的阻攔。與此同時,蔣中正許多盟友張作霖、" +977,"民革命軍在1930年至1934年間發生5次反圍剿戰爭。另外在黨內自身政治結構上,1927年8月13日時中國共產黨中央領導機構發出第44號通告,進而發起反對托洛斯基主義鬥爭外。而中國共產黨原先在城市的組織系統遭到破壞也促使得黨內制度的變革,除了原本作為革命組織政黨的中國共產黨確立採取民主集中制,並建立中國共產黨中央政治局、而由其成員擔任中央委員會常務委員。這樣的改組結果增加中國共產黨內部的集權狀態,同時新成立的中央政治局含原先實際掌握權力的中央執行委員會在各個層面有所重疊。1929年中國和蘇聯因為中東路事件而在中國東北地區爆發戰爭,陳獨秀等人因為反對中國共產黨內部的「武裝保衛蘇聯」方針而被開除黨籍。自此中國共產黨內部早期知識派系因而失勢,爾後陳獨秀於1931年5月1日在上海托洛斯基主義組織统一大会上成為“中国共产党左派反对派”總書記。而在陳獨秀遭開除黨籍後,1929年至1930年則由李立三實際掌握政黨組織。但是李立三的一連串錯誤決策反而讓中國共產黨一度接近瓦解狀態,之後第三國際開始積極介入並且在1930年取得主導權。1931年九一八事變爆發後,中國共產黨在蘇聯支援下於11月7日在江西省瑞" +978,"會寧縣會合,長征宣告結束。而在1936年5月時,中國共產黨遵照蘇聯「聯蔣抗日」的明確裁示,由中國工農紅軍在陝北地區提出「逼蔣抗日」的方針,呼籲中國國民黨中央高層「集中國力,一致對外」。同年12月爆發西安事變,蔣中正因而被迫接受「停止剿共、一同抗日」的要求。1937年7月日本發動七七事變,中國抗日戰爭因而全面爆發。中國共產黨和中國國民黨隨後暫緩衝突,針對日軍入侵中國組成統一戰線,進而展開第二次國共合作。同年8月22日,陝北地區的中國工農紅軍納入國民革命軍而改編為八路軍;到了10月,位於南方的中國工農紅軍游擊隊整編為國民革命軍的新四軍。儘管中國共產黨和中國國民黨在名義上維持合作關係,但是在1940年時雙方便結束所有合作關係。原因在於儘管正式結為統一戰線,中國共產黨利用這次機會擴大和發展自身獨立的根據地,進而為之後可能與中國國民黨間爆發的戰爭做出準備。為此中國國民黨自1939年開始限制中國共產黨的擴張,但這導致中國共產黨的部隊和中國國民黨的軍隊間經常發生衝突。但是由於雙方不希望在面對日本入侵時爆發內戰,使得有關的衝突一度逐漸減少。 自1940年代開始,中國共產黨內部意識到要摆脱外國勢力的影響" +979,"模地向东北地区扩张势力。1946年7月至1947年6月期間,中國國民黨所控制的主要城市數量大幅增加,其中甚至还包括曾被中國共產黨作為總部的延安市。然而,中國國民黨並沒有因此獲得更多優勢,原因在於中國共產黨已經戰術性地撤離城市,並且轉而煽動居住在城市的學生和知識份子等人發動抗議行動,進而抨擊中國國民黨為首的政府發動內戰,而對此中國國民黨則往往大力鎮壓民眾作為回應。在此期間中國國民黨內部的各個派系開始與蔣中正為首的執政派系爆發權力內訌,反對蔣中正在中國國民黨黨內實施專制統治,這削弱了中國國民黨應對中國共產黨攻擊的能力。其中在國民大會召開期間,當時由中國國民黨所主導的國民政府下令停戰,但是後來中國國民黨與中國共產黨雙方仍然因為《中華民國憲法》通過事宜而決裂。儘管國民政府也應美國政府代表馬歇爾的要求呼籲中國共產黨與之和談,但是中國共產黨南京市代表陸定一拒絕這建議,自此國共談判完全破裂而使得第二次國共內戰爆發。1947年7月至1948年8月期間中國共產黨開始發起有限的反攻行動,並將目標明確訂為從華中地區發展至華北地區,以及重新收復中國東北地區。而當時中國國民黨率領的國民革命軍本身則出現大量軍人逃" +980,"毛澤東因為彭德懷等人的異議而下令發動反右傾運動,一直到1962年七千人大會召開後才得以制止。與此同時,中國共產黨和蘇聯共產黨因為意識形態差異而宣告决裂。此時期毛澤東主張儘管社會主義革命似乎獲得成功,但是社會內部的階級敵人仍然持續存在,加上與國家主席劉少奇等人的矛盾加深,進而提出「無產階級專政下繼續革命」之觀點。1966年2月3日,中國共產黨透過文化革命五人小組發表《二月提綱》,文化大革命正式展開。在這之後引起長達10年激烈的一系列政治鬥爭,直到1976年毛澤東逝世及四人幫遭到瓦解,持續十年的文化大革命結束。 === 改革开放以后 === 1976年毛澤東逝世並逮捕四人幫成員後,華國鋒和鄧小平為了取得中國共產黨的最高領導權而爆發權力鬥爭,最後由鄧小平一派贏得鬥爭而掌握中國共產黨的最高領導權。其中鄧小平聯合陳雲和李先念推動改革開放政策,並且提出了中國特色社會主義的思想觀念。為了扭轉毛澤東極左派政策所帶來的影響,鄧小平認為一個社會主義國家可以排除資本主義思想而採納其市場經濟政策,並且在中國共產黨仍維持其自身政治權力的情況下,透過政策的轉變帶來顯著的經濟增長。1978年中共十一屆三中全會召開後" +981,"施以制止動亂,随后部分激进学生绝食抗议使局勢升級。5月19日晚間,國務院決定在首都部分地區實行戒嚴,不過戒嚴部隊被許多北京市市民阻攔而未能入城。最终中國共產黨高層和中央軍事委員會決定採取強硬措施,并於6月3日晚间派遣中國人民解放軍進入天安門廣場實行武力清場。途中軍方與群眾和學生爆發流血衝突,军队开枪射杀了很多平民,反抗的平民也导致了十四位军人的死亡(平民和军人的死亡人数至今仍有很大的争议),清场行动引來西方社會的全面譴責。 在示威运动結束後,中共十三屆四中全會召開。在會議中決定撤銷趙紫陽的中國共產黨中央委員會總書記等職務,由江澤民接任,確立其黨內的領導地位,並且建立包括李鵬、朱鎔基等人的第三代中央領導集體。而儘管此次示威一度使改革開放政策暫緩,1990年代初期有關社會主義市場經濟之政策重新獲得施行,這讓鄧小平的經濟學觀點重新獲得重視。1997年中國共產黨第十五次全國代表大會上,鄧小平所提出的觀點正式加入《中國共產黨章程》,與原先的馬克思列寧主義和毛澤東思想同樣列為指導思想。中國共產黨中央委員會總書記江澤民繼承鄧小平在1980年代「最高領導人」的位置,並延續後者絕大部分政策。隨後江澤民則" +982,"日的中國共產黨第十八次全國代表大會上才視為指導方針。胡錦濤在2012年11月召開的中國共產黨第十八次全國代表大會和中共十八屆一中全會上卸去中國共產黨中央委員會總書記和中國共產黨中央軍事委員會主席職務,並且由新選出的習近平接替這兩個職位,之後與李克強等人組建第五代中央領導集體。習近平上任後不久便展開數十年來最為積極的反腐敗工作,但與此同時開始整合中國共產黨中央委員會總書記作為最高領導人的權力,這使得外國評論家認為其破壞集體領導制度、而朝向毛澤東的個人獨裁統治方式。在2017年10月24日,中国共产党第十九次全国代表大会通过了关于《中国共产党章程(修正案)》的决议,将习近平新时代中国特色社会主义思想写入党章,由此习近平成为继毛泽东、邓小平之后第三位将自己的名字写进党章的中国共产党领导核心。 == 领导制度 == === 政治體制 === 中國共產黨創建之初便以蘇聯共產黨為榜樣,採取了蘇聯共產黨的組織模式,後來也接納了列寧的「黨的領導體制」和史達林的「無產階級專政體制」理論和實踐經驗。在政治體制建設方面基本上則是採納了史達林主義,也就是在每個非共產黨機關內部都設有嚴格服從共產黨的組織,並且建" +983,"共產黨的組織原則採取民主集中制,並且基於民主和兩個原則而成,其中官方所指稱的民主主要是「社會主義民主」或者是「黨內民主」。而有關民主集中制的討論,自從1927年召開的中國共產黨第五次全國代表大會便一直視為中國共產黨的組織原則。其中在《中國共產黨章程》中提到:「黨是根據自己的綱領和章程,按照民主集中制組織起來的統一整體。」而毛澤東曾經表示民主集中制是「將民主和集中兩個似乎相衝突的東西,在一定形式上統一起來」,認為藉此結合能夠處理民主和集中之間的內部矛盾,同時民主集中制在自由和紀律協調上更具優越性。 其中在《中國共產黨章程》中提到:「黨的領導主要是政治、思想和組織的領導。黨要適應改革開放和社會主義現代化建設的要求,加強和改善黨的領導。」而當前中國共產黨表示民主同時是中國共產黨自身與社會主義重要的生命線,但是認為要實現且正常運作民主,可行的方法唯有實施集中制度。中國共產黨主張民主得以使用包括集中制度等任何形式展開,並且認為如果沒有集中制度就將會失去秩序。對此中國共產黨依據毛澤東對於民主集中制的觀點成立人民代表大會制度,認為這是「在民主基礎上的集中,在集中指導下的民主。只有這個制度,才既能表現" +984,"領導人物,其中黨總書記和軍委主席的職務掌握實權。在過去鄧小平擔任最高領導人時期,中國共產黨內部核心成員和領導集體並沒有什麼區別,而在實際情況下核心人物往往不參與集體領導制度。然而集體領導制度發展到江澤民之後,中國共產黨開始大力推廣責任制度,並且在中國共產黨的官方聲明中改稱作「集體領導核心」,但胡錦濤開始,最高領導人改稱為「以某某(最高领导人)同志為總書記」。在2016年中国共产党的十八届六中全会上,明确确立了“以习近平同志为核心的党中央”。 2017年10月中共十九大之後,中共中央政治局全體委員和常委被規定要向中共中央總書記述職。2018年3月,中共中央總書記習近平首次審閱各中央政治局委員提交的報告,他又向各政治局委員提出重要要求。 === 中共中央主要负责人 === 中共中央主要负责人指的是中国共产党中央权力机构的最高领导人,在中共建党以来经历过多次变改: #中国共产党中央局书记(1921年-1922年) #中国共产党中央执行委员会委员长(1922年-1925年) #中国共产党中央政治局主席(1943年-1956年) #中国共产党中央委员会主席(1945年-1982年) #中国共产党" +985,"。6月,總書記向忠發被中國國民黨逮捕後遭處決,王明增補為中央政治局常委,代理中央總書記主持中央工作。 博古 1931年9月15日代理中央總書記1934年1月15日中共六届五中全会选举為正任中央總書記 1935年2月5日 1931年9月,博古接替赴苏联担任中共驻共产国际代表团团长的王明,为中共临时中央政治局总负责人。 张闻天 1935年1月15日遵义会议选举产生,於同年2月5日接受中共中央總書記印璽 1943年3月20日 遵義會議決議由朱德和周恩來為軍事指揮者,增選毛澤東為中央政治局常委。1936年12月7日以毛澤東為中共中央軍委主席。从1943年3月20日开始,总书记就不再是中共中央主要负责人。 毛泽东 1943年3月20日中央政治局会议决定 1976年9月9日逝世 先任中共中央政治局主席兼中央书记处主席,1945年6月起出任中央委员会主席。領導中國人民解放戰爭取得最終勝利,於1949年10月1日成立中華人民共和國。 华国锋 1976年9月9日毛澤東逝世1976年10月7日懷仁堂事變後接任黨主席,由中央政治局会议通过 1981年6月28日中共十一屆六中全會閉幕 1976年10月粉碎四人" +986,"全会选举产生 2012年11月15日 任期届满后一并辭去中共中央总书记、中央軍委主席職務,唯一主動「裸退」的領導人。 习近平 2012年11月15日中共十八届一中全会选举产生 現任 2012年11月接任胡锦涛的中共中央总书记、中央軍委主席職務。先后于2017年10月中共十九届一中全会和2022年10月中共二十届一中全会上连任。 == 組織机构 == === 中央组织 === ==== 非常設機關 ==== 根據《中国共产党章程》,中國共產黨全國代表大會是中國共產黨的最高权力機構。在1977年中國共產黨第十一次全國代表大會之前全國代表大會經常不定期召開,之後实行每5年舉行一次全國代表大會的制度,而每次大會將會持續數天。根據《中國共產黨章程》,除特別情況外,全國代表大會日程不得延期。《中國共產黨章程》賦予全國代表大會6項职权: # 听取和审查中央委员会的报告; # 审查中央纪律检查委员会的报告; # 讨论并决定党的重大问题; # 修改党的章程; # 选举中央委员会; # 选举中央纪律检查委员会。 在實際召開全國代表大會時,絕大部分時間與會代表很少討論重大問題;而比較具影響力的討論則是在全國" +987,"央委員會的職權,负责党的经常工作。 中國共產黨中央政治局及其常务委员会在中央委员会全体会议闭会期间,行使中央委员会的职权,作为最高决策机关和领导机关,承担中共中央经常工作。所有重大的政治、思想、軍事、政策和組織問題都必須在中央政治局會議上討論通過。中国共产党在組織上採取民主集中制作為基本原則,各级党组织对同下级组织有关的重要问题作出决定时,一般要征求下级组织的意见,执行少数服从多数的原则。各级党组织和党员必须坚决执行上级党组织的决定。本级党组织和党员如有不同意见,则必须在坚决执行的前提下,声明保留或向上级党委反映。 ==== 常設機關 ==== 在中國共產黨全國代表大會、中央委員會全體會議和中央政治局全體會議閉會期間,由中國共產黨中央政治局常務委員會行使中央政治局的职权,成為党内最高決策機構。 中央政治局常務委員會制度是在1956年中國共產黨第八次全國代表大會時建立,用以取代原本擔任政策決定角色的中國共產黨中央書記處。其中中央政治局常務委員會每周至少召開1次會議,成为中国共产党的中国的核心领导机构。藉由閉會制度使得中國共產黨的權力集中至中央政治局常務委員會。根據《中國共產黨章程》,中國" +988,"中央政法委员会。 中共中央设置办事机构8个,即中共中央政策研究室、中央国家安全委员会办公室、中央网络安全和信息化委员会办公室、中央军民融合发展委员会办公室、中共中央台湾工作办公室、中央财经委员会办公室、中央外事工作委员会办公室、中央机构编制委员会办公室。 中國共產黨還設立有提供高級幹部培训与政治教育的中共中央黨校,负责党史研究、文献出版发行与外文编译的中共中央党史和文献研究院。而在新聞媒體對外傳播方面,中國共產黨中央委員會直接管理《人民日報》社,而中共中央黨校則發行有作為理論雜誌的《求是》和《學習時報》。另外中央委員會还设立多个中央议事协调机构,包括中央全面深化改革委员会、中央国家安全委员会、中央精神文明建设指导委员会、中央财经委员会等,在全體會議期間將向中央委員會提交報告。 === 地方組織 === thumb|中國共産黨深圳市委員會 中國共產黨在各省、自治区、直辖市;设区的市、自治州;縣(自治县、旗)、不设区的市和市轄區成立地方代表大會,並且由這些代表大會自行選出本级行政区委員會。中國共產黨地方各級代表大會應該每5年舉行一次,但是在特殊情況下,可由上一级党委委員會決定提前或者延後進" +989,"= 申請加入中國共產黨者年齡必須滿18周歲,並且必須成為預備黨員為期1年。在民主革命时期,任何国籍之人士都可以加入中国共产党。根据1956年通过的中共八大党章规定:“任何从事劳动、不剥削他人劳动的中国公民,承认党的纲领和党的章程,参加党的一个组织并在其中工作,并且按照规定交纳党费的,都可以成为本党党员”,这是中共党章首次对入党申请者的国籍要求作出了规定,一直保持至今。相較於過去中國共產黨將重點放在申請人的思想準則上,目前中國共產黨則主要強調技術和學歷。申請人必須在中國共產黨黨旗前舉行入黨宣誓才能夠成為預備黨員,之後則由中國共產黨相關組織負責觀察和教育預備黨員。預備黨員的職責實際上和正式黨員相差不大,不同的是可能無法在中國共產黨黨內參與選舉投票,同時也沒有被選舉權。許多中國共產黨黨員是透過加入中國共產黨旗下的中國共產主義青年團,而江澤民以後民營企業家也被允許成為中國共產黨黨員。根據《中國共產黨章程》,全體黨員必須服從命令、遵守紀律、堅持團結、服務於中國共產黨和人民,並且促進社會主義道路的發展。 在加入中國共產黨將會獲得許多權限,這包括有參加中國共產黨的有關會議、閱讀中國共產黨有關文件、接" +990,"上學歷的黨員4,493.7万人,占黨員總數的49.6%;35歲及以下的黨員2,211.6万人。而自2013年開始,中國共產黨在保證發展黨員質量的前提下逐漸控制黨員人士增長速度,在發展受調控下使得總人數增加速度放緩。截至2021年底,中國共產黨總共有9671.2萬名黨員。中国共产党官方宣称中共为“世界第一大党”(除中共外,印度人民党官方以及印度媒体也宣称印度人民党为“世界第一大党”韩梅.印媒:印度人民党人数达8800万成世界第一大党 [EB/OL] (2015-03-30) [2024]. 环球网 .;2019年印度人民党约有1.8亿党员韩东育,李健,黄民兴.印度人民党 [DB/OL].牛军凯,杨洪贵,董灏智,工作组 (2023-07-17) [2024] // 陈奎元.中国大百科全书. 3版网络版.北京:中国大百科全书出版社.)。 === 青年團體 === 中國共產主義青年團是中國共產黨的青年組織,同時也是中華人民共和國最大的青年群眾組織。在《中國共產黨章程》中提到中國共產主義青年團是由中國共產黨領導的青年組成,並且指出其功能為「廣大青年在實踐中學習中國特色社會主義和共產主義的學校,是" +991,"、科學發展觀、習近平新時代中國特色社會主義思想作為自己的行動指南。」馬克思列寧主義是中國共產黨的第一個官方意識形態,內容結合了馬克思和恩格斯在著作中提到的馬克思主義、以及由列寧所提出的列寧主義思想。對此中國共產黨認為馬克思列寧主義揭示了人類社會歷史發展的普遍規律,同時馬克思列寧主義提供解決資本主義矛盾社會的一個未來願景,也就是社會主義社會和共產主義社會必然會取代資本主義社會。而第二個加入的毛澤東思想則被視為是馬克思列寧主義在中國的發展和應用,其中毛澤東思想的內容除了由毛澤東主導外,還包括其他中國共產黨的領導人物共同發想而成。 鄧小平理論則是在中國共產黨第十四次全國代表大會加入到《中國共產黨章程》,其概念包括有中國特色社會主義和社會主義初級階段等論點。鄧小平理論大體上可以視為一種理論總結,認為共產主義的定義中並不包括國有社會主義和計劃經濟等制度安排,同時市場機制實際上是中立的運作方式。此外他還提到中國共產黨需要應對不斷變化的形勢動態,並且要透過「實事求是」的方式來確認某一政策過時與否,進而提出《實踐是檢驗真理的唯一標準》這篇文章與相應的口號。在中國共產黨第十四次全國代表大會上,江澤民重申" +992,"心问题是“新时代坚持和发展什么样的中国特色社会主义、怎样坚持和发展中国特色社会主义”,该思想“内涵十分丰富”,囊括“五位一体”、“四个全面”、“党领导一切”等政治方略,是“全党全国人民为实现中华民族伟大复兴而奋斗的行动指南。” === 經濟方針 === 在《中國共產黨章程》中提到:「中國共產黨在領導社會主義事業中,必須堅持以經濟建設為中心,其他各項工作都服從和服務於這個中心。」在內容中也提到將會持續進行改革開放政策,以求根本上改革限制生產力發展的經濟體制,進而完善社會主義市場經濟體制。其中鄧小平不認為資本主義生產方式和社會主義生產方式的根本區別是自由市場與計劃經濟。對此他表示:「計劃經濟不等於社會主義,資本主義也有計劃﹔市場經濟不等於資本主義,社會主義也有市場。計劃和市場都是經濟手段。」江澤民支持鄧小平的思想並在中國共產黨會議上多次重申此觀點,認為並沒有一定的機制是資本主義還是社會主義,因為唯一重要的是其工作的方式。與此同時江澤民引入社會主義市場經濟的術語,藉此取代陳雲原先所提的「有計劃的社會主義市場經濟」用語。在中國共產黨第十四次全國代表大會上,江澤民於報告中向其他代表們表示認為社會" +993,",但也認為全球化可以推動社會主義的一系列進展。 === 思想改良 === 雖然非中國分析家普遍認為,中國共產黨已經拒絕正統的馬克思列寧主義和毛澤東思想,或者至少在正統思想的基本思路並沒有繼續沿用,然而中國共產黨本身並不同意。儘管一些評論認為江澤民引進「三個代表」思想理論後,結束中國共產黨長期所承諾的馬克思主義做法;然而中國共產黨黨理論家冷溶則不同意此類看法,認為江澤民透過各種不同的所有制擺脫原來中國共產黨的思想障礙,這作法本身並沒有放棄馬克思主義或社會主義,相對地反而藉由現代化理解馬克思主義和社會主義而強化中國共產黨自身,進而成為具有中國特色的「社會主義市場經濟」的一部分。其中中國共產黨和中華人民共和國仍然將實現真正的「共產主義」作為其「終極目標」,其中中國共產黨聲稱現時的中華人民共和國處於社會主義初級階段。而一部分中國共產黨理論家認為在目前的發展階段「看起來很像資本主義」,另外某一部分理論家則認為「資本主義是共產主義的早期或第一階段」。不過有些人則反對社會主義初級階段的概念,認為這實際上是受知識分子的犬儒主義影響。其中中國分析師庫恩便表示:「當我第一次聽到這個說法時,我認為它滑稽多於" +994,"與非執政的共產黨和工人黨保持聯繫,並且派代表出席共產黨和工人黨國際會議、国际共产主义研讨会等國際共產主義會議。同時許多國外的共產黨還會派遣代表前往中國訪問,例如在2008年葡萄牙共產黨總書記德索薩便和中國共產黨中央政治局委員劉奇葆會面,而法國共產黨全國書記洛朗也曾與中國共產黨中央政治局常務委員會委員劉雲山會面。2014年時,中國共產黨中央委員會總書記習近平則與俄羅斯聯邦共產黨主席久加諾夫會面,討論兩個政黨間的互動關係。1980年代至1990年代期間歐洲社會民主運動的一連串改革,使得中國共產黨認為西歐的共產黨地位日益边缘化。中國共產黨中央委員會一方面和葡萄牙共產黨、法國共產黨、俄羅斯聯邦共產黨、捷克和摩拉维亚共产党、巴西共產黨、尼泊爾共產黨(聯合馬列)、西班牙共產黨等已經作為主要政黨的共產黨接觸外,同時也和澳大利亞共產黨、孟加拉國工人黨、孟加拉国共产党、斯里蘭卡共產黨、比利時工人黨、匈牙利工人黨、多米尼加工人黨、宏都拉斯改革黨等仍然在發展中的共產黨和工人黨保持交流。中國共產黨與其他仍然信奉共產主義的社會主義國家繼續保持密切的關係,這包括有古巴、寮國、越南和它們各自的執政黨。中國共產黨花費" +995,"一提议遭到了宫本显治的拒绝。随后中共开始全面批判日共“修正主义路线”,毛泽东认为中国明确存在四个敌人,“苏联现代修正主义,美国帝国主义,宫本显治修正主义集团和佐藤荣作反动内阁”。在1967年8月3日,日共驻北京代表从机场回日本途中被红卫兵打伤,从此中共和日共关系全面破裂。改革开放后中共并不急于与日共重建关系,而是选择靠近当时为日本最大左翼政党的日本社会党,直到日本社会党解散后的1998年,中联部与日共代表团共举行了5次会谈,中共与日共才实现了两党关系正常化。21世纪初,中共与日共的关系总体稳定,不破哲三时期的日共一度对中国特色社会主义持赞许态度,认为是一种新的社会主义探索,“中国正是在走社会主义道路才取得了巨大成就”,中共也对日共主张日本应当走和平发展的道路,在历史、台湾问题上的立场表示赞赏,希望两党通过交流不断丰富社会主义的理论与实践,继续为推动两国关系和人类进步事业的发展做出积极贡献。但近年来习近平领导的中共加速朝民族主义政党转型,志位和夫领导的日共加速朝社会民主主义政党转型,两党的分歧愈来愈大。2017年,日共批判中共推行霸权主义政策,并在2019年修改党章,删除将中国评价为“开" +996,"執政黨人民行動黨如何繼續維持完全控制,並且在新加坡政治環境中得以採行低調的完全掌握社會。根據中國共產黨自己對於新加坡的分析,其認為人民行動黨的統治地位是因為藉由發達的社交網絡來有效地控制選區,其中新加坡政府和人民行動黨控制的分支機構深入且廣泛地掌握社會。儘管中國共產黨接受新加坡是自由民主制國家,他們認為這是以人民行動黨為首的指導式民主。不過中國共產黨認為其與人民行動黨的差異,則包括有後者並非工人階級的政黨、而是屬於政治菁英組成的政黨;同時人民行動黨是採行議會制的政黨,本身並非作為革命黨存在。中國共產黨還有與對其他政黨展開研究並且發展密切交流,這包括有自1957年以來長期統治馬來西亞的馬來民族統一機構,以及日本政壇具主導地位的自由民主黨。另外中國共產黨也對於一黨制國家或者有某方政黨主導的國家特別關注,並且透過發展政黨間的關係使得中國共產黨得以加以研究。 例如中國共產黨認為叙利亚复兴党之所以能夠長期統治敘利亞地區,源自於阿薩德家族塑造個人權力並透過嚴密統治,這使得哈菲茲·阿薩德得以將政權傳承給他的兒子巴沙爾·阿薩德,同時敘利亞武裝力量勢力也在其政治中扮演重要角色。近年來中國共產黨則對於拉丁" +997,"社會民主主義政黨大力發展政黨之間的互動,這也是中國共產黨首次與非共產黨的政黨展開交流。在1980年代之前,中國共產黨基於第二國際、列寧主義和史達林主義對於社會民主主義運動的觀點,曾一度對於社會民主主義感到負面和輕視。不過在1980年代這一觀點發生了變化,中國共產黨開始認為歐洲的社會民主主義試圖創建「有人性的資本主義」,而實際上自身也可以從社會民主主義運動中學到部分經驗。中國共產黨在1980年代陸續派遣代表前往歐洲各地考察,同時期大多數歐洲社會民主黨派別都面臨選舉支持率的下滑,以及開始對於自身展開改革。中國共產黨對此感到極大興趣,這之中又以英國工黨和德國社會民主黨的內部改革為首。中國共產黨對於两者再次取得執政權的原因歸咎於採行現代化措施,藉由轉而支持私有化而取代傳統的國家社會主義原則,並且將原先的大政府思維轉型成為新的福利國家觀點。至此改變對於市場的負面看法,而支持群眾也從傳統工會轉型成企業家、年輕成員和學生。 中國共產黨過去與北韓的緊密關係,這使得中國共產黨是經常派遣代表訪問北韓的組織之一。儘管中國的朝鮮分析家往往向社會大眾表態支持北韓,並且發表多份有關北韓經濟改革的報告。但是在中國共" +998,"反對中國共產黨的宣傳逐漸減少。與此同時,由江澤民領導的中國共產黨則開始向過去的敵人中國國民黨表達友好姿態,期望藉由加強與中國國民黨間的關係,以促進臺灣地區與中國大陸統一的可能性。然而在2000年後,中國國民黨喪失從1945年以來統治臺灣地區的政權。而過去的宣傳仍很大程度影響臺灣人民對於中國共產黨以及中國大陸的觀感,其中負責海峽兩岸事務的海峽交流基金會在2007年11月28日的調查中,指出近70%的臺灣民眾對於中國共產黨政府有不好印象。 有些觀點認為中國共產黨和其領導人應該對中國人數眾多的非正常死亡承擔責任,這些非正常死亡案例主要集中於1960年代的三年困難時期和隨後的文化大革命。不過在中國共產黨執政時期,中國大陸在1980年代經過改革開放之後的30年經濟高速發展,很快發展成為世界第二大經濟體、世界第一大工業國、世界第一大農業國和世界第二大服務業國。儘管一些評論認為中國共產黨採取的政策對環境、文化、人口結構造成了破壞,然而調查顯示目前中國公眾對於中央政府的滿意度很高。 南非《》認為,中國共產黨第十八屆中央委員會第三次全體會議所提出的全面深化改革方案將在之後決定中國未來的走向,使得國家經濟" +999,"題。 2008年1月15日,中共中央總書記胡錦濤在中國共產黨第十七屆中央紀律檢查委員會第二次全體會議上發表講話,表示今後會把反腐敗工作的成效看作是獲得民眾信任的重要指標,這是中國轉型發展近30年來首次明確提出的關於反腐敗目標的看法。中國共產黨召開十八次全國代表大會後,很多政治觀察人士對於習近平的改革舉措表示出乎意料。其中2014年9月11日,澳門大學社會科學學院助理教授陳定定在雜誌《外交學者》網站發表分析文章表示在不到2年時間裡,中國政府在計劃生育、勞動改造、戶口制度、反腐工作等領域都進行了改革。而在國際透明組織所發布的清廉印象指數中,中國的清廉印象指數自從2011年開始已經連續3年分數有所上升;不過2013年中國在清廉印象指數的得分爲40分,在176個國家中位列80名而屬於「嚴重腐敗」之列。但到了2014年,中国的清廉印象指數比上一年低了4分,得分为36分,排名更是从80名下滑到100名。外交部发言人回应:“2014年中国“清廉印象指数”评分和排名与中国反腐败取得举世瞩目成就的现实情况完全相背、严重不符。”并表示不承认国际透明组织的腐败指数标准。 === 言论管控 === 2008年" +1000,"對於網際網路進行審查而阻止民眾瀏覽部分網站,同時在中國國內還有出版物審查制度。而中國每年都會投入相當龐大的經費用於維穩,其中從2011年開始中國連續3年的維穩支出均高於軍費,2013年的預算中便高達7,690億元人民幣。 == 选举历史 == === 全国人民代表大会 === 届次 中共中央主席/总书记 席次 +/– 位次 1954 毛泽东 第一 1958-59 毛泽东 1 第一 1963–64 毛泽东 1621 第一 1974–75 毛泽东 53 第一 1977–78 华国锋 501 第一 1982–83 胡耀邦 1255 第一 1987–88 赵紫阳 125 第一 1993–94 江泽民 51 第一 1997–98 江泽民 93 第一 2002–03 胡锦涛 48 第一 2007–08 胡锦涛 79 第一 2012–13 习近平 58 第一 2017–18 习近平 38 第一 === 中国人民政治协商会议全国委员会 === *只计党派代表中拥有中国共产党党籍的委员数,不计拥有中国共产党党籍的其余界别委员数。以下记录包括中国人民政治协商会议第一届全体会议。 届次 中共中央主席/总书记" From 2b142144c3c24a2100d7d313e952717025d56aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=92=8C=E5=85=89=E5=90=8C=E5=B0=98?= <1659648118@qq.com> Date: Wed, 26 Aug 2026 19:49:19 +0800 Subject: [PATCH 05/17] perf(fulltext2): defer phrase position decode until alignment Backport-of: ec591dc9d4ec6b3beb81b804dac11aceb9b66441 --- pkg/fulltext2/phrase_cursor_test.go | 28 ++++++++++++ pkg/fulltext2/phrase_perf_bench_test.go | 42 +++++++++++++++++ pkg/fulltext2/pool.go | 8 +--- pkg/fulltext2/search.go | 60 ++++++++++++++++--------- pkg/fulltext2/segment.go | 31 +++++++++++++ 5 files changed, 141 insertions(+), 28 deletions(-) diff --git a/pkg/fulltext2/phrase_cursor_test.go b/pkg/fulltext2/phrase_cursor_test.go index be12ef2f22bb9..ca48d24452394 100644 --- a/pkg/fulltext2/phrase_cursor_test.go +++ b/pkg/fulltext2/phrase_cursor_test.go @@ -17,6 +17,7 @@ package fulltext2 import ( "bytes" "fmt" + "math" "testing" "github.com/matrixorigin/matrixone/pkg/container/types" @@ -88,3 +89,30 @@ func TestPhraseCursorMatchesFallback(t *testing.T) { }) } } + +func TestPhraseCursorDefersPositionsUntilRequested(t *testing.T) { + data, err := syntheticCorpus(t).Serialize() + require.NoError(t, err) + loaded, err := Deserialize("lazy-pos", bytes.NewReader(data)) + require.NoError(t, err) + t.Cleanup(func() { _ = loaded.dict.Close() }) + + tp, ok := loaded.lookup("alpha") + require.True(t, ok) + c := newPhraseCursor(tp, 0) + defer releasePhraseCursors([]*phraseCursor{c}) + + require.Equal(t, -1, c.curDocBlk) + require.Equal(t, -1, c.curPosBlk) + first := c.doc() + require.NotEqual(t, int64(math.MaxInt64), first) + require.Equal(t, 0, c.curDocBlk) + require.Equal(t, -1, c.curPosBlk, "reading doc IDs must not decode positions") + + c.skipTo(first + 1) + require.Equal(t, -1, c.curPosBlk, "skipTo must not decode positions") + if !c.atEnd() { + _ = c.positions() + require.Equal(t, c.idx/BlockSize, c.curPosBlk) + } +} diff --git a/pkg/fulltext2/phrase_perf_bench_test.go b/pkg/fulltext2/phrase_perf_bench_test.go index 1bf84386a95a9..6a9774160f14f 100644 --- a/pkg/fulltext2/phrase_perf_bench_test.go +++ b/pkg/fulltext2/phrase_perf_bench_test.go @@ -85,6 +85,48 @@ func BenchmarkPhraseSearchConcurrent(b *testing.B) { }) } +// BenchmarkPhraseSparseIntersection isolates doc alignment. Alpha occurs only in even +// docs and beta only in odd docs, so the two large posting lists never share a doc and +// no positional block should be decoded by the lazy phrase cursor. +func BenchmarkPhraseSparseIntersection(b *testing.B) { + bb := NewBuilder("phrase-sparse", int32(types.T_int64)) + for d := 0; d < 50000; d++ { + term := "alpha" + if d&1 != 0 { + term = "beta" + } + if err := bb.Add(term, 0, int64(d)); err != nil { + b.Fatal(err) + } + } + seg, err := bb.Finish() + if err != nil { + b.Fatal(err) + } + blob, err := seg.Serialize() + if err != nil { + b.Fatal(err) + } + loaded, err := Deserialize("phrase-sparse", bytes.NewReader(blob)) + if err != nil { + b.Fatal(err) + } + b.Cleanup(func() { _ = loaded.dict.Close() }) + idx := NewIndex([]*Segment{loaded}, nil) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + res, err := idx.SearchQuery([]byte("alpha beta"), false, ParserDefault, BM25, 10, nil) + if err != nil { + b.Fatal(err) + } + if len(res) != 0 { + b.Fatalf("expected no matches, got %d", len(res)) + } + } +} + // BenchmarkPhraseStreamNoLimit — concern B, the materializing fallback: a no-LIMIT NL // phrase that matches ALL docs, so StreamQuery goes through SearchQuery(globalN). func BenchmarkPhraseStreamNoLimit(b *testing.B) { diff --git a/pkg/fulltext2/pool.go b/pkg/fulltext2/pool.go index aaba6a865ab6b..dea1197fe3073 100644 --- a/pkg/fulltext2/pool.go +++ b/pkg/fulltext2/pool.go @@ -22,7 +22,7 @@ import ( ) // Per-query cursor block buffers are BlockSize-sized and short-lived: buildWandIters -// makes one pair per term and newPhraseCursor one triple per slot, then they die when +// makes one pair per term and newPhraseCursor one pair per slot, then they die when // the search returns. Under concurrent query load that churn outruns the GC, so the // buffers are recycled through sync.Pool instead of freshly allocated per query. Each // buffer is Get by exactly one (single-goroutine) search and Put back on return; the @@ -59,12 +59,10 @@ func releaseWandIters(iters []*wandIter) { } } -// phraseBuf is a phraseCursor's decoded-block buffers: docIDs + per-doc positions + a -// tf scratch (phrase ignores tf but fillBlock still writes it). +// phraseBuf is a phraseCursor's decoded-block buffers: docIDs + per-doc positions. type phraseBuf struct { docs []int64 pos [][]int32 - tfs []uint8 } var phraseBufPool = sync.Pool{ @@ -72,7 +70,6 @@ var phraseBufPool = sync.Pool{ return &phraseBuf{ docs: make([]int64, BlockSize), pos: make([][]int32, BlockSize), - tfs: make([]uint8, BlockSize), } }, } @@ -91,7 +88,6 @@ func releasePhraseCursors(cursors []*phraseCursor) { c.buf = nil c.bDocs = nil c.bPos = nil - c.tfbuf = nil } } diff --git a/pkg/fulltext2/search.go b/pkg/fulltext2/search.go index fc6f158883971..228e877434bbd 100644 --- a/pkg/fulltext2/search.go +++ b/pkg/fulltext2/search.go @@ -345,39 +345,47 @@ func sortedIndexInt64(a []int64, v int64) int { // intersection, decoding ONE docID block + ONE position block at a time (never the whole // list). It is the phrase analogue of wandIter, but also carries the block's positions. type phraseCursor struct { - tp *termPostings - off int32 // this slot's byte offset within the phrase - idx int // current global posting index (0..df-1) - curBlk int // block currently decoded into bDocs/bPos (-1 = none) - blen int // valid entries in bDocs/bPos - bDocs []int64 // decoded docIDs of curBlk (cap BlockSize); backed by buf - bPos [][]int32 // decoded positions of curBlk (cap BlockSize); backed by buf - tfbuf []uint8 // scratch for fillBlock's tfs (phrase ignores tf); backed by buf - buf *phraseBuf // pooled backing for bDocs/bPos/tfbuf; returned by releasePhraseCursors + tp *termPostings + off int32 // this slot's byte offset within the phrase + idx int // current global posting index (0..df-1) + curDocBlk int // block currently decoded into bDocs (-1 = none) + curPosBlk int // block currently decoded into bPos (-1 = none) + blen int // valid entries in bDocs + bDocs []int64 // decoded docIDs of curDocBlk (cap BlockSize); backed by buf + bPos [][]int32 // decoded positions of curPosBlk (cap BlockSize); backed by buf + buf *phraseBuf // pooled backing for bDocs/bPos; returned by releasePhraseCursors } func newPhraseCursor(tp *termPostings, off int32) *phraseCursor { b := getPhraseBuf() return &phraseCursor{ - tp: tp, off: off, curBlk: -1, + tp: tp, off: off, curDocBlk: -1, curPosBlk: -1, bDocs: b.docs, bPos: b.pos, - tfbuf: b.tfs, buf: b, } } func (c *phraseCursor) atEnd() bool { return c.idx >= c.tp.df() } -// decode ensures block b is the one loaded in bDocs/bPos (a field compare when the cursor -// stays within a block; skipped blocks are never decoded). -func (c *phraseCursor) decode(b int) { - if b == c.curBlk { +// ensureDocBlock loads only doc IDs. Positional payload stays compressed until all +// phrase cursors reach the same document. +func (c *phraseCursor) ensureDocBlock(b int) { + if b == c.curDocBlk { + return + } + c.blen = c.tp.fillBlockDocs(b, c.bDocs) + c.curDocBlk = b +} + +// ensurePositionBlock loads positions for block b on first use. A cursor can skip many +// doc blocks without ever calling this method. +func (c *phraseCursor) ensurePositionBlock(b int) { + if b == c.curPosBlk { return } - c.blen = c.tp.fillBlock(b, c.bDocs, c.tfbuf) c.tp.fillBlockPositions(b, c.bPos) - c.curBlk = b + c.curPosBlk = b } // doc returns the current doc ord, or MaxInt64 when exhausted (sorts last). @@ -385,13 +393,13 @@ func (c *phraseCursor) doc() int64 { if c.atEnd() { return math.MaxInt64 } - c.decode(c.idx / BlockSize) + c.ensureDocBlock(c.idx / BlockSize) return c.bDocs[c.idx%BlockSize] } // positions returns the current doc's ascending byte positions. func (c *phraseCursor) positions() []int32 { - c.decode(c.idx / BlockSize) + c.ensurePositionBlock(c.idx / BlockSize) return c.bPos[c.idx%BlockSize] } @@ -407,9 +415,17 @@ func (c *phraseCursor) skipTo(target int64) { c.idx = c.tp.df() // past the last posting → exhausted return } - c.decode(b) - w := sort.Search(c.blen, func(i int) bool { return c.bDocs[i] >= target }) - c.idx = b*BlockSize + w + c.ensureDocBlock(b) + lo, hi := 0, c.blen + for lo < hi { + mid := int(uint(lo+hi) >> 1) + if c.bDocs[mid] < target { + lo = mid + 1 + } else { + hi = mid + } + } + c.idx = b*BlockSize + lo } // matchPhraseCursor runs the block-cursor conjunctive phrase intersection over EXACT diff --git a/pkg/fulltext2/segment.go b/pkg/fulltext2/segment.go index f6e1bc68a58d5..ce280cf00b118 100644 --- a/pkg/fulltext2/segment.go +++ b/pkg/fulltext2/segment.go @@ -157,6 +157,37 @@ func (p *termPostings) fillBlock(b int, outDocs []int64, outTfs []uint8) int { return blen } +// fillBlockDocs decodes only block b's doc IDs. Phrase intersection never reads term +// frequency, so using fillBlockDocs there avoids a redundant tf copy for every block the +// doc cursor visits. WAND keeps using fillBlock because it scores with tf. +func (p *termPostings) fillBlockDocs(b int, outDocs []int64) int { + blen := p.blockLen(b) + if p.docIDs != nil { + lo := b * BlockSize + copy(outDocs[:blen], p.docIDs[lo:lo+blen]) + return blen + } + data := p.blockData[p.blockOff[b]:p.blockOff[b+1]] + var prev int64 + if b > 0 { + prev = p.blockLastDoc[b-1] + } + off := 0 + for i := 0; i < blen; i++ { + if off >= len(data) { + return i + } + g, n := binary.Uvarint(data[off:]) + if n <= 0 { + return i + } + off += n + prev += int64(g) + outDocs[i] = prev + } + return blen +} + // fillBlockPositions decodes block b's per-doc token positions into out[:blen], // returning the block length. loaded-side varint-decodes only block b's slice of posRaw // (via blockPosOff) — the per-block analogue of fillBlock, so the phrase cursor holds ONE From 835e046faa9242dbe288bd72364a475e2132bbed Mon Sep 17 00:00:00 2001 From: Eric Lam Date: Wed, 26 Aug 2026 19:49:20 +0800 Subject: [PATCH 06/17] feat(fulltext2): support wrapped scores and range pushdown Backport-of: 3da66f2ca28d62f5e657c46d90b7cde9475486d0 --- pkg/fulltext2/membership.go | 43 ++ pkg/fulltext2/membership_test.go | 16 + pkg/fulltext2/pool_bench_test.go | 2 +- pkg/fulltext2/search_cache.go | 28 +- pkg/fulltext2/stream.go | 14 +- .../table_function/fulltext2_search.go | 18 + pkg/sql/plan/apply_indices.go | 17 +- pkg/sql/plan/apply_indices_fulltext.go | 593 +++++++++++++++++- pkg/sql/plan/fulltext2_query_shape_test.go | 73 +++ 9 files changed, 761 insertions(+), 43 deletions(-) create mode 100644 pkg/sql/plan/fulltext2_query_shape_test.go diff --git a/pkg/fulltext2/membership.go b/pkg/fulltext2/membership.go index 1ee249d14addc..69ae3605f05d5 100644 --- a/pkg/fulltext2/membership.go +++ b/pkg/fulltext2/membership.go @@ -94,6 +94,49 @@ func (d *docFilterMembership) Contains(ord int64) bool { type prefilter struct { docFilter docfilter.MembershipFilter include []compiledIncludePred + // scoreRange is the pushed-down relevance interval. Unlike the membership + // filters above it cannot prune before scoring; it is applied at the point a + // scored document becomes a result (streamSink or runTopK). + scoreRange *ScoreRange +} + +// ScoreRange is a relevance interval pushed into a FULLTEXT2 search. Both ends +// are optional and retain SQL comparison inclusivity. The planner widens bounds +// outward in float32 space before passing them here, so this filter cannot drop +// a row that the SQL predicate would keep. +type ScoreRange struct { + Min float32 `json:"min,omitempty"` + Max float32 `json:"max,omitempty"` + HasMin bool `json:"has_min,omitempty"` + HasMax bool `json:"has_max,omitempty"` + MinInclusive bool `json:"min_inc,omitempty"` + MaxInclusive bool `json:"max_inc,omitempty"` +} + +// contains reports whether score is inside r. A nil range allows every score. +func (r *ScoreRange) contains(score float32) bool { + if r == nil { + return true + } + if r.HasMin { + if r.MinInclusive { + if score < r.Min { + return false + } + } else if score <= r.Min { + return false + } + } + if r.HasMax { + if r.MaxInclusive { + if score > r.Max { + return false + } + } else if score >= r.Max { + return false + } + } + return true } // mkAllow returns a per-segment WHERE-prefilter Membership over seg's ords (the docfilter diff --git a/pkg/fulltext2/membership_test.go b/pkg/fulltext2/membership_test.go index 9248121a77f10..c3c0c870fa231 100644 --- a/pkg/fulltext2/membership_test.go +++ b/pkg/fulltext2/membership_test.go @@ -133,3 +133,19 @@ func TestPhraseDfFilterIndependent(t *testing.T) { require.InDelta(t, s0, scoreOf(filtered, 0), 1e-5, "doc 0's score must be filter-independent (corpus df, not filtered count)") } + +func TestScoreRangeContains(t *testing.T) { + var nilRange *ScoreRange + require.True(t, nilRange.contains(0)) + + r := &ScoreRange{Min: 1, HasMin: true, MinInclusive: true, Max: 3, HasMax: true} + require.True(t, r.contains(1)) + require.True(t, r.contains(2)) + require.False(t, r.contains(0.99)) + require.False(t, r.contains(3), "exclusive upper bound") + + r.MaxInclusive = true + require.True(t, r.contains(3)) + r.MinInclusive = false + require.False(t, r.contains(1), "exclusive lower bound") +} diff --git a/pkg/fulltext2/pool_bench_test.go b/pkg/fulltext2/pool_bench_test.go index e1778e91314f7..d31cedfe34d00 100644 --- a/pkg/fulltext2/pool_bench_test.go +++ b/pkg/fulltext2/pool_bench_test.go @@ -109,7 +109,7 @@ func BenchmarkStreamAlloc(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - sink := newStreamSink(idx, false, func(o *vectorindex.SearchOutput) error { PutColumnBuffer(o.Keys); return nil }) + sink := newStreamSink(idx, false, nil, func(o *vectorindex.SearchOutput) error { PutColumnBuffer(o.Keys); return nil }) _ = idx.streamDisjunction(cs, BM25, nil, sink) } } diff --git a/pkg/fulltext2/search_cache.go b/pkg/fulltext2/search_cache.go index 0bce2d91ebd7d..8bde7017b9aaf 100644 --- a/pkg/fulltext2/search_cache.go +++ b/pkg/fulltext2/search_cache.go @@ -50,6 +50,11 @@ type Fulltext2Query struct { // values INSIDE the search — so a WHERE on an INCLUDE column needs no second base scan. // nil = none. ANDed with FilterBytes. IncludePredsJSON []byte + // ScoreRange is an optional pushed-down relevance interval from an + // AND-reachable MATCH score predicate. Out-of-range scored rows are removed + // before they cross the TVF/join boundary; the plan keeps its own predicate + // for exact SQL semantics. + ScoreRange *ScoreRange } // Fulltext2Search adapts a loaded fulltext2 Index to veccache.VectorIndexSearchIf so @@ -183,8 +188,8 @@ func (s *Fulltext2Search) prepare(proc *sqlexec.SqlProcess, query any, rt vector } else if k <= 0 { k = int(s.idx.NumDocs()) } - if len(q.FilterBytes) > 0 || len(q.IncludePredsJSON) > 0 { - pf = &prefilter{} + if len(q.FilterBytes) > 0 || len(q.IncludePredsJSON) > 0 || q.ScoreRange != nil { + pf = &prefilter{scoreRange: q.ScoreRange} if len(q.FilterBytes) > 0 { var filter docfilter.MembershipFilter if filter, err = docfilter.New(q.FilterBytes); err != nil { @@ -206,10 +211,25 @@ func (s *Fulltext2Search) prepare(proc *sqlexec.SqlProcess, query any, rt vector // runTopK dispatches the bounded top-k search (ranked bag-of-words vs positional query). func (s *Fulltext2Search) runTopK(q Fulltext2Query, k int, pf *prefilter) ([]Result, error) { + var ( + results []Result + err error + ) if q.BagOfWords { - return s.idx.SearchBagOfWords(q.Pattern, s.cfg.Parser, q.Algo, k, pf) + results, err = s.idx.SearchBagOfWords(q.Pattern, s.cfg.Parser, q.Algo, k, pf) + } else { + results, err = s.idx.SearchQuery(q.Pattern, q.Boolean, s.cfg.Parser, q.Algo, k, pf) + } + if err != nil || pf == nil || pf.scoreRange == nil { + return results, err + } + kept := results[:0] + for _, result := range results { + if pf.scoreRange.contains(result.Score) { + kept = append(kept, result) + } } - return s.idx.SearchQuery(q.Pattern, q.Boolean, s.cfg.Parser, q.Algo, k, pf) + return kept, nil } // Search runs the WAND positional query (NL exact-phrase or boolean) and returns diff --git a/pkg/fulltext2/stream.go b/pkg/fulltext2/stream.go index a21be9669820a..a519211d447ab 100644 --- a/pkg/fulltext2/stream.go +++ b/pkg/fulltext2/stream.go @@ -46,19 +46,23 @@ type streamSink struct { err error stopped bool wantInclude bool // covered fast path: also carry each doc's INCLUDE values through emit + scoreRange *ScoreRange } // newStreamSink resolves the index's pk type (all segments share it). The batch buffer // itself is fetched from the pool lazily on the first push. Callers create it only after // the globalN==0 early-out, so segments is non-empty. wantInclude turns on the covered // fast path: each pushed doc also carries its INCLUDE values (box-free) through emit. -func newStreamSink(idx *Index, wantInclude bool, emit func(out *vectorindex.SearchOutput) error) *streamSink { +func newStreamSink(idx *Index, wantInclude bool, filter *prefilter, emit func(out *vectorindex.SearchOutput) error) *streamSink { pkType := idx.segments[0].PkType w, fixed := fixedPkByteWidth(pkType) if !fixed { w = 0 } s := &streamSink{emit: emit, pkType: types.T(pkType), fixedW: w, wantInclude: wantInclude} + if filter != nil { + s.scoreRange = filter.scoreRange + } if wantInclude { s.includeTypes = idx.segments[0].includeTypes } @@ -94,7 +98,7 @@ func (s *streamSink) appendIncludes(seg *Segment, ord int64) { // pushPk appends the pk at (seg, ord) to the typed batch WITHOUT boxing, decoding it // straight from the segment docmap into ColumnBuffer.Data. func (s *streamSink) pushPk(seg *Segment, ord int64, score float32) { - if s.stopped { + if s.stopped || !s.scoreRange.contains(score) { return } s.ensure() @@ -115,7 +119,7 @@ func (s *streamSink) pushPk(seg *Segment, ord int64, score float32) { // boolean) by re-encoding its concrete value into ColumnBuffer.Data — no NEW box. When the // covered fast path is on, the INCLUDE values are pulled box-free from (seg, ord). func (s *streamSink) pushAny(seg *Segment, ord int64, pk any, score float32) { - if s.stopped { + if s.stopped || !s.scoreRange.contains(score) { return } s.ensure() @@ -228,7 +232,7 @@ func (idx *Index) StreamQuery(pattern []byte, boolean bool, parser string, algo if idx.globalN == 0 { return nil } - sink := newStreamSink(idx, wantInclude, emit) + sink := newStreamSink(idx, wantInclude, filter, emit) p := normalizeParser(parser) // Resolve the query the same way SearchQuery does, but prefer the streaming @@ -332,7 +336,7 @@ func (idx *Index) StreamBagOfWords(pattern []byte, parser string, algo ScoreAlgo if !ok { return nil // no resolvable tokens → nothing to stream } - return idx.streamDisjunction(terms, algo, filter, newStreamSink(idx, wantInclude, emit)) + return idx.streamDisjunction(terms, algo, filter, newStreamSink(idx, wantInclude, filter, emit)) } // streamDisjunction streams a pure OR of terms heap-free across all segments (global diff --git a/pkg/sql/colexec/table_function/fulltext2_search.go b/pkg/sql/colexec/table_function/fulltext2_search.go index 1342f29671640..c36cbe5681852 100644 --- a/pkg/sql/colexec/table_function/fulltext2_search.go +++ b/pkg/sql/colexec/table_function/fulltext2_search.go @@ -16,6 +16,7 @@ package table_function import ( "context" + "encoding/json" "fmt" "github.com/bytedance/sonic" @@ -378,6 +379,22 @@ func (u *fulltext2SearchState) start(tf *TableFunction, proc *process.Process, n } } } + // Optional score range (argVecs[4], a query const). The planner uses this + // slot for an AND-reachable FULLTEXT2 MATCH score comparison. Keep it + // positional after includePreds so direct three-argument calls remain valid. + var scoreRange *fulltext2.ScoreRange + if len(tf.ctr.argVecs) > 4 { + if rv := tf.ctr.argVecs[4]; rv != nil && rv.Length() > 0 && !rv.IsNull(0) { + if s := rv.GetStringAt(0); len(s) > 0 { + var r fulltext2.ScoreRange + if err := json.Unmarshal([]byte(s), &r); err != nil { + return moerr.NewInternalErrorf(proc.Ctx, + "fulltext2_search: invalid score range %q: %v", s, err) + } + scoreRange = &r + } + } + } newsearch := fulltext2.NewFulltext2Search(u.tblcfg) q := fulltext2.Fulltext2Query{ @@ -387,6 +404,7 @@ func (u *fulltext2SearchState) start(tf *TableFunction, proc *process.Process, n Algo: fulltext2ScoreAlgo(proc), FilterBytes: u.filterBytes, IncludePredsJSON: includePreds, + ScoreRange: scoreRange, } if u.limit == 0 { diff --git a/pkg/sql/plan/apply_indices.go b/pkg/sql/plan/apply_indices.go index c387d5ebf0b3b..19888722fbf38 100644 --- a/pkg/sql/plan/apply_indices.go +++ b/pkg/sql/plan/apply_indices.go @@ -614,11 +614,12 @@ func (builder *QueryBuilder) applyIndicesForProject(nodeID int32, projNode *plan // agg node and scan node present // get the list of filter that is fulltext_match func filterids, filter_ftidxs := builder.getFullTextMatchFiltersFromScanNode(scanNode) + wrappedFTExprs, wrappedFTIdxs := builder.getWrappedFullText2Matches(nil, scanNode, filterids, nil) // apply the match indices (one unified pass handles a mix of MATCH + BM25) - if len(filterids) > 0 { + if len(filterids) > 0 || len(wrappedFTExprs) > 0 { return builder.applyIndicesForAggUsingFullTextIndex(nodeID, projNode, aggNode, scanNode, - filterids, filter_ftidxs, colRefCnt, idxColMap) + filterids, filter_ftidxs, wrappedFTExprs, wrappedFTIdxs, colRefCnt, idxColMap) } } else { // get the list of project that is fulltext_match func @@ -626,11 +627,13 @@ func (builder *QueryBuilder) applyIndicesForProject(nodeID int32, projNode *plan // get the list of filter that is fulltext_match func filterids, filter_ftidxs := builder.getFullTextMatchFiltersFromScanNode(scanNode) + wrappedFTExprs, wrappedFTIdxs := builder.getWrappedFullText2Matches(projNode, scanNode, filterids, projids) // apply the match indices (one unified pass handles a mix of MATCH + BM25) - if len(filterids) > 0 || len(projids) > 0 { + if len(filterids) > 0 || len(projids) > 0 || len(wrappedFTExprs) > 0 { return builder.applyIndicesForProjectionUsingFullTextIndex(nodeID, projNode, sortNode, scanNode, - filterids, filter_ftidxs, projids, proj_ftidxs, colRefCnt, idxColMap) + filterids, filter_ftidxs, projids, proj_ftidxs, wrappedFTExprs, wrappedFTIdxs, + colRefCnt, idxColMap) } } } @@ -1410,7 +1413,8 @@ func (builder *QueryBuilder) detectFullTextGuard(projNode *plan.Node) []int32 { if aggNode != nil { filterids, _ := builder.getFullTextMatchFiltersFromScanNode(scanNode) - if len(filterids) > 0 { + wrapped, _ := builder.getWrappedFullText2Matches(nil, scanNode, filterids, nil) + if len(filterids) > 0 || len(wrapped) > 0 { return []int32{scanNode.NodeId} } return nil @@ -1418,7 +1422,8 @@ func (builder *QueryBuilder) detectFullTextGuard(projNode *plan.Node) []int32 { projids, _ := builder.getFullTextMatchFromProject(projNode, scanNode) filterids, _ := builder.getFullTextMatchFiltersFromScanNode(scanNode) - if len(filterids) > 0 || len(projids) > 0 { + wrapped, _ := builder.getWrappedFullText2Matches(projNode, scanNode, filterids, projids) + if len(filterids) > 0 || len(projids) > 0 || len(wrapped) > 0 { return []int32{scanNode.NodeId} } return nil diff --git a/pkg/sql/plan/apply_indices_fulltext.go b/pkg/sql/plan/apply_indices_fulltext.go index d5442e4eb171b..783c1eef2e2e3 100644 --- a/pkg/sql/plan/apply_indices_fulltext.go +++ b/pkg/sql/plan/apply_indices_fulltext.go @@ -14,16 +14,66 @@ package plan import ( + "encoding/json" "fmt" + "math" "strings" "github.com/matrixorigin/matrixone/pkg/catalog" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/fulltext2" "github.com/matrixorigin/matrixone/pkg/pb/plan" "github.com/matrixorigin/matrixone/pkg/sql/plan/function" ) +// exprCallsFunc reports whether expr contains a call to fnName, including nested arguments. +func exprCallsFunc(expr *plan.Expr, fnName string) bool { + if expr == nil { + return false + } + switch e := expr.Expr.(type) { + case *plan.Expr_F: + if e.F.Func != nil && e.F.Func.ObjName == fnName { + return true + } + for _, arg := range e.F.Args { + if exprCallsFunc(arg, fnName) { + return true + } + } + case *plan.Expr_List: + for _, sub := range e.List.List { + if exprCallsFunc(sub, fnName) { + return true + } + } + } + return false +} + +// replaceScoreFnInExprBy recursively replaces function calls selected by rewrite. A nil +// callback result leaves the call in place and continues walking its arguments. +func replaceScoreFnInExprBy(expr *plan.Expr, rewrite func(*plan.Function) *plan.Expr) *plan.Expr { + if expr == nil { + return nil + } + switch e := expr.Expr.(type) { + case *plan.Expr_F: + if repl := rewrite(e.F); repl != nil { + return repl + } + for i, arg := range e.F.Args { + e.F.Args[i] = replaceScoreFnInExprBy(arg, rewrite) + } + case *plan.Expr_List: + for i, sub := range e.List.List { + e.List.List[i] = replaceScoreFnInExprBy(sub, rewrite) + } + } + return expr +} + // The idea is as follows: // 1. Find fulltext_match() function from projection (projNode) and filters (ScanNode) // and then convert fulltext_match() to table function fulltext_index_scan(). @@ -53,6 +103,7 @@ import ( // +------------------------------------------------------------------------------------------+ func (builder *QueryBuilder) applyIndicesForProjectionUsingFullTextIndex(nodeID int32, projNode *plan.Node, sortNode *plan.Node, scanNode *plan.Node, filterids []int32, filterIndexDefs []*plan.IndexDef, projids []int32, projIndexDef []*plan.IndexDef, + wrappedExprs []*plan.Expr, wrappedIndexDefs []*plan.IndexDef, colRefCnt map[[2]int32]int, idxColMap map[[2]int32]*plan.Expr) (int32, error) { ctx := builder.ctxByNode[nodeID] @@ -80,16 +131,19 @@ func (builder *QueryBuilder) applyIndicesForProjectionUsingFullTextIndex(nodeID // WITH include columns can DROP the base-table JOIN and serve pk/score/include-cols // straight from the fulltext2_search TVF. Purely additive: when not covered, falls // through to the existing JOIN path below byte-for-byte. - if handled, herr := builder.tryApplyCoveredFulltext2(nodeID, projNode, sortNode, scanNode, - filterids, filterIndexDefs, projids, projIndexDef, eqmap, - paginationLimit, paginationOffset); herr != nil { - return -1, herr - } else if handled { - return nodeID, nil + if len(wrappedExprs) == 0 { + if handled, herr := builder.tryApplyCoveredFulltext2(nodeID, projNode, sortNode, scanNode, + filterids, filterIndexDefs, projids, projIndexDef, eqmap, + paginationLimit, paginationOffset); herr != nil { + return -1, herr + } else if handled { + return nodeID, nil + } } - idxID, filter_node_ids, proj_node_ids, err := builder.applyJoinFullTextIndices(nodeID, projNode, scanNode, - internalLimit, internalOffset, filterids, filterIndexDefs, projids, projIndexDef, eqmap, colRefCnt, idxColMap) + idxID, filter_node_ids, proj_node_ids, served, err := builder.applyJoinFullTextIndicesWithWrapped(nodeID, projNode, scanNode, + internalLimit, internalOffset, filterids, filterIndexDefs, projids, projIndexDef, + wrappedExprs, wrappedIndexDefs, eqmap, colRefCnt, idxColMap) if err != nil { return -1, err } @@ -145,6 +199,27 @@ func (builder *QueryBuilder) applyIndicesForProjectionUsingFullTextIndex(nodeID Flag: plan.OrderBySpec_DESC, }) } + seen := make(map[int32]struct{}, len(orderByScore)) + for _, id := range filter_node_ids { + seen[id] = struct{}{} + } + for i, id := range proj_node_ids { + if _, ok := eqmap[int32(i)]; !ok { + seen[id] = struct{}{} + } + } + for _, s := range served { + if s.nodeID < 0 { + continue + } + if _, ok := seen[s.nodeID]; ok { + continue + } + if score := builder.fullText2ScoreColRef(s.nodeID); score != nil { + orderByScore = append(orderByScore, &OrderBySpec{Expr: score, Flag: plan.OrderBySpec_DESC}) + seen[s.nodeID] = struct{}{} + } + } sortByID := builder.appendNode(&plan.Node{ NodeType: plan.Node_SORT, @@ -172,6 +247,12 @@ func (builder *QueryBuilder) applyIndicesForProjectionUsingFullTextIndex(nodeID }, } } + if len(served) > 0 { + rewriter := builder.fullText2ScoreRewriter(served) + for i := range projNode.ProjectList { + projNode.ProjectList[i] = replaceScoreFnInExprBy(projNode.ProjectList[i], rewriter) + } + } return nodeID, nil } @@ -191,6 +272,7 @@ func (builder *QueryBuilder) applyIndicesForProjectionUsingFullTextIndex(nodeID // +----------------------------------------------------------------+ func (builder *QueryBuilder) applyIndicesForAggUsingFullTextIndex(nodeID int32, projNode *plan.Node, aggNode *plan.Node, scanNode *plan.Node, filterids []int32, filterIndexDefs []*plan.IndexDef, + wrappedExprs []*plan.Expr, wrappedIndexDefs []*plan.IndexDef, colRefCnt map[[2]int32]int, idxColMap map[[2]int32]*plan.Expr) (int32, error) { var err error @@ -199,8 +281,9 @@ func (builder *QueryBuilder) applyIndicesForAggUsingFullTextIndex(nodeID int32, eqmap := make(map[int32]int32) - idxID, _, _, err := builder.applyJoinFullTextIndices(nodeID, projNode, scanNode, - scanNode.Limit, scanNode.Offset, filterids, filterIndexDefs, projids, projIndexDefs, eqmap, colRefCnt, idxColMap) + idxID, _, _, _, err := builder.applyJoinFullTextIndicesWithWrapped(nodeID, projNode, scanNode, + scanNode.Limit, scanNode.Offset, filterids, filterIndexDefs, projids, projIndexDefs, + wrappedExprs, wrappedIndexDefs, eqmap, colRefCnt, idxColMap) if err != nil { return -1, err } @@ -220,6 +303,18 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl filterids []int32, filter_indexDefs []*plan.IndexDef, projids []int32, proj_indexDefs []*plan.IndexDef, eqmap map[int32]int32, colRefCnt map[[2]int32]int, idxColMap map[[2]int32]*plan.Expr) (int32, []int32, []int32, error) { + joinID, filterIDs, projIDs, _, err := builder.applyJoinFullTextIndicesWithWrapped(nodeID, projNode, scanNode, + paginationLimit, paginationOffset, filterids, filter_indexDefs, projids, proj_indexDefs, + nil, nil, eqmap, colRefCnt, idxColMap) + return joinID, filterIDs, projIDs, err +} + +func (builder *QueryBuilder) applyJoinFullTextIndicesWithWrapped(nodeID int32, projNode *plan.Node, scanNode *plan.Node, + paginationLimit, paginationOffset *plan.Expr, + filterids []int32, filter_indexDefs []*plan.IndexDef, + projids []int32, proj_indexDefs []*plan.IndexDef, + wrappedExprs []*plan.Expr, wrapped_indexDefs []*plan.IndexDef, eqmap map[int32]int32, + colRefCnt map[[2]int32]int, idxColMap map[[2]int32]*plan.Expr) (int32, []int32, []int32, []fulltextServedMatch, error) { ctx := builder.ctxByNode[nodeID] @@ -264,6 +359,35 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl } } + // Wrapped-only MATCHes have no bare projection/filter slot to map back to. Add their + // own FT2 streams after the ordinary filter/projection streams and resolve wrapped scalar + // expressions by the served function arguments below. + extraStart := len(ft_filters) + ft_filters = append(ft_filters, wrappedExprs...) + indexDefs = append(indexDefs, wrapped_indexDefs...) + served := make([]fulltextServedMatch, len(ft_filters)) + for i, expr := range ft_filters { + served[i] = fulltextServedMatch{ + fn: expr.GetF(), nodeID: -1, + fulltext2: i < len(indexDefs) && catalog.IsFullText2IndexAlgo(indexDefs[i].IndexAlgo), + } + } + + // A nested score predicate is evaluated only after the TVF has produced a score. Lift + // fully-served predicates off the source scan and attach them above the join. Leave any + // predicate containing an unserved MATCH untouched so it still fails closed at execution. + wrappedMatchFilters := make([]*plan.Expr, 0) + keptFilters := scanNode.FilterList[:0] + for _, expr := range scanNode.FilterList { + if exprCallsFunc(expr, "fulltext_match") && builder.exprHasFullText2Match(expr, scanNode) && !hasUnservedFullText2Match(expr, + func(fn *plan.Function) bool { return builder.isServedFullText2Match(fn, served) }) { + wrappedMatchFilters = append(wrappedMatchFilters, expr) + continue + } + keptFilters = append(keptFilters, expr) + } + scanNode.FilterList = keptFilters + // fulltext2 INCLUDE/pk prefilter pushdown: when the driving index is a fulltext2 index // WITH INCLUDE columns, peel the WHERE predicates on those INCLUDE columns (and the pk) // out of scanNode.FilterList into the ivfpq-aligned predicate JSON, which fulltext2_search @@ -290,7 +414,7 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl // documents that belong to the final top page, so leave those inputs // unbounded until a joint top-k implementation exists. var limitExpr *plan.Expr - if len(scanNode.FilterList) == 0 && len(ft_filters) == 1 { + if len(scanNode.FilterList) == 0 && len(wrappedMatchFilters) == 0 && len(ft_filters) == 1 { limitExpr, _ = buildCandidateLimit(paginationLimit, paginationOffset) } @@ -309,7 +433,7 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl modeLit := fn.Args[1].GetLit() if modeLit == nil { - return -1, nil, nil, moerr.NewInvalidInput(builder.GetContext(), "fulltext search mode must be a constant") + return -1, nil, nil, nil, moerr.NewInvalidInput(builder.GetContext(), "fulltext search mode must be a constant") } mode := modeLit.GetI64Val() @@ -324,23 +448,38 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl if catalog.IsFullText2IndexAlgo(idxdef.IndexAlgo) { cfg, cfgErr := builder.buildFulltext2SearchCfg(scanNode, idxdef, mode) if cfgErr != nil { - return -1, nil, nil, cfgErr + return -1, nil, nil, nil, cfgErr } exprs := []*plan.Expr{ makePlan2StringConstExprWithType(cfg), DeepCopyExpr(fn.Args[0]), // pattern (may be a bound '?' parameter) DeepCopyExpr(fn.Args[1]), // mode (a constant) } - // Attach the peeled INCLUDE/pk predicate JSON to the DRIVING TVF (i==0). With - // multiple MATCHes the JOIN #1 doc_id intersection propagates the filter, so one - // filtered stream constrains the whole result — the predicate need only ride the - // driving stream. - if i == 0 && ft2PredsJSON != "" { - exprs = append(exprs, makePlan2StringConstExprWithType(ft2PredsJSON)) + // Push the score interval for this MATCH into the engine. The original filter is + // retained above the join, while the float32 bound is widened outward for safety. + var scoreRangeJSON string + if rng := builder.fulltext2ScoreRangeFromFilters(wrappedMatchFilters, fn); rng != nil { + buf, marshalErr := json.Marshal(rng) + if marshalErr != nil { + return -1, nil, nil, nil, marshalErr + } + scoreRangeJSON = string(buf) + } + // arg 3 is INCLUDE/pk predicate JSON and arg 4 is score range. Keep the + // positional empty arg when only the range is present. + preds := "" + if i == 0 { + preds = ft2PredsJSON + } + if preds != "" || scoreRangeJSON != "" { + exprs = append(exprs, makePlan2StringConstExprWithType(preds)) + } + if scoreRangeJSON != "" { + exprs = append(exprs, makePlan2StringConstExprWithType(scoreRangeJSON)) } curr_ftnode_id, err = builder.buildFulltext2SearchNode(ctx, exprs, nil) if err != nil { - return -1, nil, nil, err + return -1, nil, nil, nil, err } } else { // A literal pattern is pre-compiled to the index-scan SQL now; a runtime @@ -349,7 +488,7 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl if patternLit := fn.Args[0].GetLit(); patternLit != nil { fullTextSQL, sqlErr := builder.getFullTextIndexScanSql(params, idxtblname, patternLit.GetSval(), mode) if sqlErr != nil { - return -1, nil, nil, sqlErr + return -1, nil, nil, nil, sqlErr } sql = fullTextSQL } @@ -362,12 +501,15 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl } curr_ftnode_id, err = builder.buildFullTextIndexScanNode(ctx, exprs, nil, params, sql) if err != nil { - return -1, nil, nil, err + return -1, nil, nil, nil, err } } + served[i].nodeID = curr_ftnode_id // save the created fulltext node to either filter or projection // check equal fulltext_match() and return node id to correct project position - if i < proj_offset { + if i >= extraStart { + // Wrapped-only stream: no bare output slot to map. + } else if i < proj_offset { v, ok := ret_proj_node_ids_map[int32(i)] if ok { // equal fulltext_match() in proj and filter @@ -381,7 +523,7 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl ret_proj_node_ids[v] = curr_ftnode_id } else { - return -1, nil, nil, moerr.NewInternalError(builder.GetContext(), "Invalid ret_proj_node_ids_map") + return -1, nil, nil, nil, moerr.NewInternalError(builder.GetContext(), "Invalid ret_proj_node_ids_map") } } @@ -686,6 +828,34 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl }, ctx) } + // Score predicates lifted from the source scan must run after the TVF has produced its + // score column. Use a real FILTER node; JOIN.FilterList is not evaluated by the executor. + if len(wrappedMatchFilters) > 0 { + rewriter := builder.fullText2ScoreRewriter(served) + rewritten := make([]*plan.Expr, 0, len(wrappedMatchFilters)) + for _, expr := range wrappedMatchFilters { + if hasUnservedFullText2Match(expr, func(fn *plan.Function) bool { + for _, s := range served { + if s.fn != nil && s.nodeID >= 0 && builder.equalsFullTextMatchFunc(fn, s.fn) { + return true + } + } + return false + }) { + scanNode.FilterList = append(scanNode.FilterList, expr) + continue + } + rewritten = append(rewritten, replaceScoreFnInExprBy(expr, rewriter)) + } + if len(rewritten) > 0 { + joinnodeID = builder.appendNode(&plan.Node{ + NodeType: plan.Node_FILTER, + Children: []int32{joinnodeID}, + FilterList: rewritten, + }, ctx) + } + } + // Clear Limit/Offset from scanNode when pushdown is enabled // (they should be applied after SORT) // Note: caller (applyIndicesForProjectionUsingFullTextIndex) may still need @@ -693,9 +863,9 @@ func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *pl // The caller is responsible for clearing scanNode.Limit/Offset after using it. if err := builder.recordPreparedPluginDependencies(scanNode); err != nil { - return -1, nil, nil, err + return -1, nil, nil, served, err } - return joinnodeID, ret_filter_node_ids, ret_proj_node_ids, nil + return joinnodeID, ret_filter_node_ids, ret_proj_node_ids, served, nil } // tryApplyCoveredFulltext2 implements the covered fast path (Phase 6): a fully-covered @@ -907,6 +1077,7 @@ func (builder *QueryBuilder) tryApplyCoveredFulltext2(nodeID int32, projNode, so // Remap the projection off the TVF: MATCH -> score (col 1); pk base ColRef -> col 0; // include base ColRef -> col 2+j. scoreType := ftnode.TableDef.Cols[1].Typ + wrappedRewriter := builder.fullText2ScoreRewriter([]fulltextServedMatch{{fn: fn, nodeID: ftnodeID, fulltext2: true}}) for i := range projNode.ProjectList { if isProjIndexPosition(projids, int32(i)) { projNode.ProjectList[i] = &Expr{ @@ -915,6 +1086,7 @@ func (builder *QueryBuilder) tryApplyCoveredFulltext2(nodeID int32, projNode, so } continue } + projNode.ProjectList[i] = replaceScoreFnInExprBy(projNode.ProjectList[i], wrappedRewriter) projNode.ProjectList[i] = remapCoveredBaseColRefs(projNode.ProjectList[i], scanTag, ftTag, pkColPos, incSet, scanNode) } @@ -1003,7 +1175,11 @@ func remapCoveredBaseColRefs(expr *plan.Expr, scanTag, ftTag, pkColPos int32, func (builder *QueryBuilder) scanHasMatchedFullTextFilter(node *plan.Node) bool { filterids, _ := builder.getFullTextMatchFiltersFromScanNode(node) - return len(filterids) > 0 + if len(filterids) > 0 { + return true + } + wrapped, _ := builder.getWrappedFullText2Matches(nil, node, filterids, nil) + return len(wrapped) > 0 } func (builder *QueryBuilder) applyFullTextFiltersForJoinChildren(nodeID int32, joinNode *plan.Node, @@ -1282,6 +1458,369 @@ func (builder *QueryBuilder) getFullTextMatchScoreExpr(expr *plan.Expr) *plan.Ex return newExpr } +// fulltextServedMatch pairs a MATCH call with the TVF node that computes its score. The +// node is filled after the TVF is built; keeping the original function lets wrapped +// expressions resolve by pattern/mode/index parts instead of by function name. +type fulltextServedMatch struct { + fn *plan.Function + nodeID int32 + fulltext2 bool +} + +// collectNestedFullTextMatches finds MATCH calls below scalar/comparison expressions. +func collectNestedFullTextMatches(expr *plan.Expr, out []*plan.Expr) []*plan.Expr { + if expr == nil { + return out + } + switch e := expr.Expr.(type) { + case *plan.Expr_F: + if e.F.Func != nil && e.F.Func.ObjName == "fulltext_match" { + return append(out, expr) + } + for _, arg := range e.F.Args { + out = collectNestedFullTextMatches(arg, out) + } + case *plan.Expr_List: + for _, sub := range e.List.List { + out = collectNestedFullTextMatches(sub, out) + } + } + return out +} + +// monotoneWrappedFullTextMatch returns the inner MATCH for an order-preserving wrapper. +// It is used only to recognise score comparisons; arbitrary wrappers are still discoverable +// in projections, but cannot safely provide a range bound. +func monotoneWrappedFullTextMatch(expr *plan.Expr) *plan.Expr { + if expr == nil { + return nil + } + fn := expr.GetF() + if fn == nil || fn.Func == nil { + return nil + } + switch fn.Func.ObjName { + case "fulltext_match": + return expr + case "round", "cast", "floor", "ceil": + if len(fn.Args) == 0 { + return nil + } + return monotoneWrappedFullTextMatch(fn.Args[0]) + default: + return nil + } +} + +// collectDrivingFullText2Matches returns MATCHes whose predicate implies index membership. +// A score lower-bound comparison is safe to drive an INNER JOIN; an upper-bound-only +// predicate is not, because a non-matching row has score zero and is absent from the TVF. +func collectDrivingFullText2Matches(expr *plan.Expr, out []*plan.Expr) []*plan.Expr { + if expr == nil { + return out + } + fn := expr.GetF() + if fn == nil || fn.Func == nil { + return out + } + switch fn.Func.ObjName { + case "and": + for _, arg := range fn.Args { + out = collectDrivingFullText2Matches(arg, out) + } + return out + case ">", ">=": + if len(fn.Args) != 2 { + return out + } + matchSide, constSide := fn.Args[0], fn.Args[1] + if monotoneWrappedFullTextMatch(matchSide) == nil { + // Reverse form: c < score / c <= score. + matchSide, constSide = fn.Args[1], fn.Args[0] + } + if monotoneWrappedFullTextMatch(matchSide) == nil { + return out + } + v, ok := constValueAsFloat(constSide) + if !ok || v < 0 { + return out + } + if fn.Func.ObjName == ">=" && v == 0 { + return out + } + return append(out, monotoneWrappedFullTextMatch(matchSide)) + case "<", "<=": + // Upper-bound-only MATCH cannot establish membership, so it is only lifted when a + // separate bare MATCH already supplies the stream. + return out + default: + return out + } +} + +// constValueAsFloat reads the numeric literal forms emitted by the planner. +func constValueAsFloat(expr *plan.Expr) (float64, bool) { + if expr == nil || expr.GetLit() == nil { + return 0, false + } + switch v := expr.GetLit().Value.(type) { + case *plan.Literal_I64Val: + return float64(v.I64Val), true + case *plan.Literal_U64Val: + return float64(v.U64Val), true + case *plan.Literal_Fval: + return float64(v.Fval), true + case *plan.Literal_Dval: + return v.Dval, true + default: + return 0, false + } +} + +// getWrappedFullText2Matches discovers FT2 MATCH calls nested in a projection or in a +// membership-implying score predicate. Classic FULLTEXT is deliberately excluded: this +// backport only changes FT2 query shapes and leaves classic behaviour untouched. +func (builder *QueryBuilder) getWrappedFullText2Matches(projNode, scanNode *plan.Node, + filterids, projids []int32) ([]*plan.Expr, []*plan.IndexDef) { + if scanNode == nil { + return nil, nil + } + isPos := func(ids []int32, i int) bool { + for _, id := range ids { + if int(id) == i { + return true + } + } + return false + } + var candidates []*plan.Expr + for i, expr := range scanNode.FilterList { + if !isPos(filterids, i) { + candidates = collectDrivingFullText2Matches(expr, candidates) + } + } + if projNode != nil { + for i, expr := range projNode.ProjectList { + if !isPos(projids, i) { + candidates = collectNestedFullTextMatches(expr, candidates) + } + } + } + if len(candidates) == 0 { + return nil, nil + } + seen := make([]*plan.Function, 0, len(filterids)+len(projids)) + for _, id := range filterids { + if fn := scanNode.FilterList[id].GetF(); fn != nil { + seen = append(seen, fn) + } + } + if projNode != nil { + for _, id := range projids { + if fn := projNode.ProjectList[id].GetF(); fn != nil { + seen = append(seen, fn) + } + } + } + var exprs []*plan.Expr + var defs []*plan.IndexDef + for _, candidate := range candidates { + fn := candidate.GetF() + if fn == nil || len(fn.Args) < 2 { + continue + } + duplicate := false + for _, prior := range seen { + if builder.equalsFullTextMatchFunc(fn, prior) { + duplicate = true + break + } + } + if duplicate { + continue + } + idx := builder.findMatchFullTextIndex(fn, scanNode) + if idx == nil || !catalog.IsFullText2IndexAlgo(idx.IndexAlgo) { + continue + } + seen = append(seen, fn) + exprs = append(exprs, candidate) + defs = append(defs, idx) + } + return exprs, defs +} + +func (builder *QueryBuilder) isServedFullText2Match(fn *plan.Function, served []fulltextServedMatch) bool { + if fn == nil || fn.Func == nil || fn.Func.ObjName != "fulltext_match" { + return false + } + for _, s := range served { + if s.fulltext2 && s.fn != nil && builder.equalsFullTextMatchFunc(fn, s.fn) { + return true + } + } + return false +} + +func (builder *QueryBuilder) fullText2ScoreColRef(nodeID int32) *plan.Expr { + if nodeID < 0 || int(nodeID) >= len(builder.qry.Nodes) { + return nil + } + node := builder.qry.Nodes[nodeID] + if node == nil || node.TableDef == nil || len(node.TableDef.Cols) < 2 || len(node.BindingTags) == 0 { + return nil + } + return &plan.Expr{Typ: node.TableDef.Cols[1].Typ, Expr: &plan.Expr_Col{Col: &plan.ColRef{ + RelPos: node.BindingTags[0], ColPos: 1, + }}} +} + +func (builder *QueryBuilder) fullText2ScoreRewriter(served []fulltextServedMatch) func(*plan.Function) *plan.Expr { + return func(fn *plan.Function) *plan.Expr { + if fn == nil || fn.Func == nil || fn.Func.ObjName != "fulltext_match" { + return nil + } + for _, s := range served { + if s.fulltext2 && s.fn != nil && builder.equalsFullTextMatchFunc(fn, s.fn) { + return builder.fullText2ScoreColRef(s.nodeID) + } + } + return nil + } +} + +func hasUnservedFullText2Match(expr *plan.Expr, isServed func(*plan.Function) bool) bool { + if expr == nil { + return false + } + switch e := expr.Expr.(type) { + case *plan.Expr_F: + if e.F.Func != nil && e.F.Func.ObjName == "fulltext_match" { + return !isServed(e.F) + } + for _, arg := range e.F.Args { + if hasUnservedFullText2Match(arg, isServed) { + return true + } + } + case *plan.Expr_List: + for _, sub := range e.List.List { + if hasUnservedFullText2Match(sub, isServed) { + return true + } + } + } + return false +} + +func (builder *QueryBuilder) exprHasFullText2Match(expr *plan.Expr, scanNode *plan.Node) bool { + var matches []*plan.Expr + matches = collectNestedFullTextMatches(expr, matches) + for _, match := range matches { + if idx := builder.findMatchFullTextIndex(match.GetF(), scanNode); idx != nil && catalog.IsFullText2IndexAlgo(idx.IndexAlgo) { + return true + } + } + return false +} + +func scoreBoundDown(v float64) float32 { + f := float32(v) + if float64(f) > v { + f = math.Nextafter32(f, float32(math.Inf(-1))) + } + return f +} + +func scoreBoundUp(v float64) float32 { + f := float32(v) + if float64(f) < v { + f = math.Nextafter32(f, float32(math.Inf(1))) + } + return f +} + +// fulltext2ScoreRangeFromFilters turns direct, AND-reachable score comparisons for one +// MATCH into a float32 range. Bounds are widened outward so engine filtering is conservative; +// the original predicate remains above the join for exact SQL evaluation. +func (builder *QueryBuilder) fulltext2ScoreRangeFromFilters(filters []*plan.Expr, matchFn *plan.Function) *fulltext2.ScoreRange { + var out *fulltext2.ScoreRange + setMin := func(v float64, inclusive bool) { + if out == nil { + out = &fulltext2.ScoreRange{} + } + f := scoreBoundDown(v) + if !out.HasMin || f > out.Min || (f == out.Min && !inclusive && out.MinInclusive) { + out.Min, out.HasMin, out.MinInclusive = f, true, inclusive + } + } + setMax := func(v float64, inclusive bool) { + if out == nil { + out = &fulltext2.ScoreRange{} + } + f := scoreBoundUp(v) + if !out.HasMax || f < out.Max || (f == out.Max && !inclusive && out.MaxInclusive) { + out.Max, out.HasMax, out.MaxInclusive = f, true, inclusive + } + } + var walk func(*plan.Expr) + walk = func(expr *plan.Expr) { + if expr == nil { + return + } + fn := expr.GetF() + if fn == nil || fn.Func == nil { + return + } + if fn.Func.ObjName == "and" { + for _, arg := range fn.Args { + walk(arg) + } + return + } + op := fn.Func.ObjName + if op != ">" && op != ">=" && op != "<" && op != "<=" || len(fn.Args) != 2 { + return + } + matchSide, constSide := fn.Args[0], fn.Args[1] + if monotoneWrappedFullTextMatch(matchSide) == nil { + matchSide, constSide = fn.Args[1], fn.Args[0] + switch op { + case ">": + op = "<" + case ">=": + op = "<=" + case "<": + op = ">" + case "<=": + op = ">=" + } + } + inner := monotoneWrappedFullTextMatch(matchSide) + if inner == nil || matchSide != inner || !builder.equalsFullTextMatchFunc(inner.GetF(), matchFn) { + return + } + v, ok := constValueAsFloat(constSide) + if !ok { + return + } + switch op { + case ">": + setMin(v, false) + case ">=": + setMin(v, true) + case "<": + setMax(v, false) + case "<=": + setMax(v, true) + } + } + for _, filter := range filters { + walk(filter) + } + return out +} + func (builder *QueryBuilder) resolveAggNode(node *plan.Node, depth int32) *plan.Node { if depth == 0 { if node.NodeType == plan.Node_AGG { diff --git a/pkg/sql/plan/fulltext2_query_shape_test.go b/pkg/sql/plan/fulltext2_query_shape_test.go new file mode 100644 index 0000000000000..aa0ec36b7cdeb --- /dev/null +++ b/pkg/sql/plan/fulltext2_query_shape_test.go @@ -0,0 +1,73 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +package plan + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/stretchr/testify/require" +) + +func ft2TestMatch(pattern string, mode int64, rel int32, col string) *plan.Function { + return &plan.Function{ + Func: &plan.ObjectRef{ObjName: "fulltext_match"}, + Args: []*plan.Expr{ + makePlan2StringConstExprWithType(pattern), + makePlan2Int64ConstExprWithType(mode), + {Typ: plan.Type{Id: int32(types.T_varchar)}, Expr: &plan.Expr_Col{Col: &plan.ColRef{RelPos: rel, Name: col}}}, + }, + } +} + +func ft2TestExpr(fn *plan.Function) *plan.Expr { + return &plan.Expr{Typ: plan.Type{Id: int32(types.T_float32)}, Expr: &plan.Expr_F{F: fn}} +} + +func TestFulltext2ScoreRangeBounds(t *testing.T) { + b := &QueryBuilder{} + match := ft2TestMatch("hello", 0, 1, "body") + value := func(v float64) *plan.Expr { return makePlan2Float64ConstExprWithType(v) } + cmp := func(op string, left, right *plan.Expr) *plan.Expr { + return &plan.Expr{Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{ObjName: op}, Args: []*plan.Expr{left, right}, + }}} + } + + r := b.fulltext2ScoreRangeFromFilters([]*plan.Expr{cmp(">", ft2TestExpr(match), value(0.5))}, match) + require.NotNil(t, r) + require.True(t, r.HasMin) + require.False(t, r.MinInclusive) + require.LessOrEqual(t, r.Min, float32(0.5)) + + r = b.fulltext2ScoreRangeFromFilters([]*plan.Expr{cmp("<", ft2TestExpr(match), value(0.8))}, match) + require.NotNil(t, r) + require.True(t, r.HasMax) + require.False(t, r.MaxInclusive) + require.GreaterOrEqual(t, r.Max, float32(0.8)) + + r = b.fulltext2ScoreRangeFromFilters([]*plan.Expr{cmp("<", value(0.5), ft2TestExpr(match))}, match) + require.True(t, r.HasMin, "reversed comparison must become a lower bound") + + r = b.fulltext2ScoreRangeFromFilters([]*plan.Expr{cmp("or", ft2TestExpr(match), value(0.5))}, match) + require.Nil(t, r, "only direct AND-reachable comparisons are pushable") +} + +func TestFulltext2ScoreWrapperRewrite(t *testing.T) { + match := ft2TestMatch("hello", 0, 1, "body") + served := []fulltextServedMatch{{fn: match, nodeID: 3, fulltext2: true}} + b := &QueryBuilder{qry: &plan.Query{Nodes: []*plan.Node{ + {}, {}, {}, {BindingTags: []int32{9}, TableDef: &plan.TableDef{Cols: []*plan.ColDef{{}, {Typ: plan.Type{Id: int32(types.T_float32)}}}}}, + }}} + wrapped := &plan.Expr{Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{ObjName: "round"}, Args: []*plan.Expr{ft2TestExpr(match)}, + }}} + got := replaceScoreFnInExprBy(wrapped, b.fullText2ScoreRewriter(served)) + require.NotNil(t, got.GetF()) + require.NotNil(t, got.GetF().Args[0].GetCol()) + require.Equal(t, int32(9), got.GetF().Args[0].GetCol().RelPos) +} From e4abf5e90d0b5e09590571ca78447e50806527c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=92=8C=E5=85=89=E5=90=8C=E5=B0=98?= <1659648118@qq.com> Date: Wed, 26 Aug 2026 19:49:20 +0800 Subject: [PATCH 07/17] perf(fulltext2): add cold-load and generation reuse observability Backport-of: 6dccc954a1a32c12a2114328c752f15cf97ef2ba Backport-of: b8aa3f7f7351aacaaa37da27891d8f8d85e2e44d --- pkg/fulltext2/base_pool.go | 498 +++++++++++++++++ pkg/fulltext2/base_pool_test.go | 466 ++++++++++++++++ pkg/fulltext2/load_observer.go | 197 +++++++ pkg/fulltext2/load_observer_test.go | 110 ++++ pkg/fulltext2/load_reason.go | 335 ++++++++++++ pkg/fulltext2/load_reason_test.go | 119 ++++ pkg/fulltext2/plugin/compile/compile.go | 19 +- .../plugin/compile/parser_coverage_test.go | 27 + pkg/fulltext2/reuse_pool_coverage_test.go | 515 ++++++++++++++++++ pkg/fulltext2/search_cache.go | 159 +++++- pkg/fulltext2/search_cache_unit_test.go | 369 +++++++++++++ pkg/fulltext2/segment.go | 20 + pkg/fulltext2/serialize.go | 1 + pkg/fulltext2/storage.go | 233 +++++++- pkg/fulltext2/storage_mock_test.go | 25 +- pkg/fulltext2/tail_pool.go | 458 ++++++++++++++++ pkg/fulltext2/tail_pool_test.go | 124 +++++ pkg/iscp/fulltext2_consumer.go | 1 + .../table_function/fulltext2_compact.go | 1 + .../table_function/fulltext2_create.go | 1 + pkg/vectorindex/cache/cache.go | 310 +++++++++-- pkg/vectorindex/cache/cache_observer_test.go | 419 ++++++++++++++ pkg/vectorindex/cache/cache_retry_test.go | 233 ++++++++ .../cache/search_into_coverage_test.go | 55 ++ 24 files changed, 4619 insertions(+), 76 deletions(-) create mode 100644 pkg/fulltext2/base_pool.go create mode 100644 pkg/fulltext2/base_pool_test.go create mode 100644 pkg/fulltext2/load_observer.go create mode 100644 pkg/fulltext2/load_observer_test.go create mode 100644 pkg/fulltext2/load_reason.go create mode 100644 pkg/fulltext2/load_reason_test.go create mode 100644 pkg/fulltext2/plugin/compile/parser_coverage_test.go create mode 100644 pkg/fulltext2/reuse_pool_coverage_test.go create mode 100644 pkg/fulltext2/tail_pool.go create mode 100644 pkg/fulltext2/tail_pool_test.go create mode 100644 pkg/vectorindex/cache/cache_observer_test.go create mode 100644 pkg/vectorindex/cache/cache_retry_test.go create mode 100644 pkg/vectorindex/cache/search_into_coverage_test.go diff --git a/pkg/fulltext2/base_pool.go b/pkg/fulltext2/base_pool.go new file mode 100644 index 0000000000000..01b7e623f5f2d --- /dev/null +++ b/pkg/fulltext2/base_pool.go @@ -0,0 +1,498 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "context" + "fmt" + "sync" + "time" +) + +const ( + // The pool is deliberately bounded independently of VectorIndexCache. A cache + // entry can expire while an immutable base is still reusable, but that reuse + // must not turn into a process-lifetime cache for every index ever queried. + basePoolMaxEntries = 1024 + basePoolMaxBytes = int64(8 << 30) + basePoolIdleTTL = 15 * time.Minute +) + +// baseKey identifies the immutable bytes of one tag=0 segment. Recency is +// deliberately excluded: a metadata rewrite may change recency while the +// segment bytes remain identical, and each generation gets its own Segment +// header with the new recency. +type baseKey struct { + index string + id string + checksum string + filesize int64 +} + +type baseEntry struct { + ready chan struct{} + lease *segmentLease + err error + loading bool + retired bool + published bool + owners map[uint64]struct{} + lastUsed time.Time +} + +// segmentLease owns one mmap and hands out shallow Segment views. The views +// have independent mutable headers (notably AvgDocLen and Recency) while all +// serialized slices/FST pointers remain shared and read-only. +type segmentLease struct { + mu sync.Mutex + template *Segment + refs int // pool reference plus one reference per returned view + pooled bool + retired bool +} + +func newSegmentLease(template *Segment) *segmentLease { + return &segmentLease{template: template, refs: 1, pooled: true} +} + +func (l *segmentLease) acquire(recency int64) *Segment { + l.mu.Lock() + defer l.mu.Unlock() + if l.template == nil { + return nil + } + l.refs++ + view := *l.template + if recency == 0 { + recency = l.template.Recency + } + view.Recency = recency + view.lease = l + return &view +} + +func (l *segmentLease) release() { + template := func() *Segment { + l.mu.Lock() + defer l.mu.Unlock() + if l.refs <= 0 { + return nil + } + l.refs-- + if l.refs != 0 { + return nil + } + template := l.template + l.template = nil + return template + }() + if template != nil { + template.freeOwned() + } +} + +// retire removes the pool's reference. Active generations keep the mapping +// alive until their Segment views are freed. +func (l *segmentLease) retire() { + template := func() *Segment { + l.mu.Lock() + defer l.mu.Unlock() + if !l.pooled { + return nil + } + l.pooled = false + l.retired = true + l.refs-- // drop the pool reference + if l.refs != 0 { + return nil + } + template := l.template + l.template = nil + return template + }() + if template != nil { + template.freeOwned() + } +} + +type immutableBasePool struct { + mu sync.Mutex + entries map[baseKey]*baseEntry + totalBytes int64 + maxEntries int + maxBytes int64 + idleTTL time.Duration +} + +var loadedBasePool = newImmutableBasePool() + +func newImmutableBasePool() *immutableBasePool { + return &immutableBasePool{ + entries: make(map[baseKey]*baseEntry), + maxEntries: basePoolMaxEntries, + maxBytes: basePoolMaxBytes, + idleTTL: basePoolIdleTTL, + } +} + +func (p *immutableBasePool) limits() (int, int64, time.Duration) { + maxEntries, maxBytes, idleTTL := p.maxEntries, p.maxBytes, p.idleTTL + if maxEntries <= 0 { + maxEntries = basePoolMaxEntries + } + if maxBytes <= 0 { + maxBytes = basePoolMaxBytes + } + if idleTTL <= 0 { + idleTTL = basePoolIdleTTL + } + return maxEntries, maxBytes, idleTTL +} + +func baseEntryBytes(key baseKey) int64 { + if key.filesize <= 0 { + return 0 + } + return key.filesize +} + +// evictLocked retires pool ownership for idle or over-cap entries. It must be +// called with p.mu held; the returned leases are retired after unlocking so +// Segment.Free never runs while the pool map is locked. +func (p *immutableBasePool) evictLocked(now time.Time) (retire []*segmentLease) { + _, _, idleTTL := p.limits() + remove := func(key baseKey, entry *baseEntry) { + delete(p.entries, key) + p.totalBytes -= baseEntryBytes(key) + if p.totalBytes < 0 { + p.totalBytes = 0 + } + if entry.lease != nil { + retire = append(retire, entry.lease) + } + } + for key, entry := range p.entries { + if entry.loading || entry.lease == nil || entry.lastUsed.IsZero() { + continue + } + if now.Sub(entry.lastUsed) >= idleTTL { + remove(key, entry) + } + } + maxEntries, maxBytes, _ := p.limits() + for len(p.entries) > maxEntries || p.totalBytes > maxBytes { + var oldestKey baseKey + var oldest *baseEntry + for key, entry := range p.entries { + if entry.loading || entry.lease == nil { + continue + } + if oldest == nil || entry.lastUsed.Before(oldest.lastUsed) { + oldestKey, oldest = key, entry + } + } + if oldest == nil { + break // a loading entry will be accounted for when its load completes + } + remove(oldestKey, oldest) + } + return retire +} + +// retireLoadingLocked marks an in-flight entry so its loader will not repool +// the result when it completes. The entry stays in the map until the loader +// closes ready; this keeps existing waiters from being stranded on a channel +// that can never be signaled. +func (p *immutableBasePool) retireLoadingLocked(entry *baseEntry) { + if entry != nil && entry.loading { + entry.retired = true + } +} + +func retireBaseLeases(leases []*segmentLease) { + for _, lease := range leases { + lease.retire() + } +} + +func (p *immutableBasePool) evict(now time.Time) { + retire := func() []*segmentLease { + p.mu.Lock() + defer p.mu.Unlock() + return p.evictLocked(now) + }() + retireBaseLeases(retire) +} + +func (p *immutableBasePool) acquire(ctx context.Context, key baseKey, load func() (*Segment, error), recency int64) (*Segment, error) { + return p.acquireOwned(ctx, key, load, recency, 0) +} + +// acquireOwned is the load-path variant. Each load attempt claims the pooled +// entry it creates or reuses, allowing an obsolete failed load to roll back +// only its own unpublished claims. +func (p *immutableBasePool) acquireOwned(ctx context.Context, key baseKey, load func() (*Segment, error), recency int64, owner uint64) (*Segment, error) { + if ctx == nil { + ctx = context.Background() + } + p.evict(time.Now()) + for { + if err := context.Cause(ctx); err != nil { + return nil, err + } + e, ready, view, found := func() (*baseEntry, chan struct{}, *Segment, bool) { + p.mu.Lock() + defer p.mu.Unlock() + if e, ok := p.entries[key]; ok { + if e.loading { + if owner != 0 { + if e.owners == nil { + e.owners = make(map[uint64]struct{}, 1) + } + e.owners[owner] = struct{}{} + } + return nil, e.ready, nil, true + } + if owner != 0 { + if e.owners == nil { + e.owners = make(map[uint64]struct{}, 1) + } + e.owners[owner] = struct{}{} + } + e.lastUsed = time.Now() + return nil, nil, e.lease.acquire(recency), true + } + owners := make(map[uint64]struct{}, 1) + if owner != 0 { + owners[owner] = struct{}{} + } + e := &baseEntry{ready: make(chan struct{}), loading: true, owners: owners, lastUsed: time.Now()} + p.entries[key] = e + return e, nil, nil, false + }() + if found { + if ready != nil { + select { + case <-ready: + case <-ctx.Done(): + return nil, context.Cause(ctx) + } + continue + } + if view == nil { + continue + } + return view, nil + } + + template, err := func() (template *Segment, err error) { + defer func() { + if r := recover(); r != nil { + err = baseLeaseError(fmt.Sprintf("fulltext2 base load panic: %v", r)) + } + }() + return load() + }() + retired, directTemplate, resultErr := func() (bool, *Segment, error) { + p.mu.Lock() + defer p.mu.Unlock() + e.loading = false + e.err = err + if e.retired { + delete(p.entries, key) + e.err = errBaseLeaseGone + close(e.ready) + if err != nil { + return true, template, err + } + if template == nil { + return true, nil, errBaseLeaseGone + } + return true, template, nil + } + if err == nil { + if template == nil { + err = errBaseLeaseGone + e.err = err + } else { + e.lease = newSegmentLease(template) + e.lastUsed = time.Now() + p.totalBytes += baseEntryBytes(key) + } + } + close(e.ready) + if err != nil { + delete(p.entries, key) + } + return false, nil, err + }() + if retired { + if directTemplate != nil && resultErr != nil { + directTemplate.Free() + } + if resultErr != nil { + return nil, resultErr + } + if directTemplate == nil { + return nil, errBaseLeaseGone + } + if ctxErr := context.Cause(ctx); ctxErr != nil { + directTemplate.Free() + return nil, ctxErr + } + // The invalidation retired the pool entry while this query was + // loading it. Let this in-flight query finish with direct ownership; + // the stale closure result is never repooled or retried here. + return directTemplate, nil + } + if resultErr != nil { + if template != nil { + template.Free() + } + return nil, resultErr + } + if err := context.Cause(ctx); err != nil { + return nil, err + } + view = func() *Segment { + p.mu.Lock() + defer p.mu.Unlock() + e.lastUsed = time.Now() + return e.lease.acquire(recency) + }() + if view == nil { + continue + } + p.evict(time.Now()) + return view, nil + } +} + +// commit keeps only the base keys used by the successfully assembled +// generation. A retired lease remains alive for old readers but cannot be +// acquired by a later generation. +func (p *immutableBasePool) commit(index string, used map[baseKey]struct{}) { + p.commitOwned(index, used, 0) +} + +func (p *immutableBasePool) commitOwned(index string, used map[baseKey]struct{}, owner uint64) { + p.commitOwnedIfCurrent(index, used, owner, nil) +} + +func (p *immutableBasePool) commitOwnedIfCurrent(index string, used map[baseKey]struct{}, owner uint64, current func() bool) bool { + published := true + retire := func() []*segmentLease { + p.mu.Lock() + defer p.mu.Unlock() + if current != nil && !current() { + published = false + return nil + } + retire := make([]*segmentLease, 0, len(p.entries)) + for key, entry := range p.entries { + if key.index != index { + continue + } + if entry.loading { + p.retireLoadingLocked(entry) + continue + } + if _, ok := used[key]; ok { + entry.owners = nil + entry.published = true + entry.lastUsed = time.Now() + continue + } + delete(p.entries, key) + p.totalBytes -= baseEntryBytes(key) + retire = append(retire, entry.lease) + } + return append(retire, p.evictLocked(time.Now())...) + }() + retireBaseLeases(retire) + return published +} + +// rollback removes only entries still claimed by owner. Reused entries retain +// claims from concurrent loads until one of them publishes or rolls back. +func (p *immutableBasePool) rollback(owner uint64) { + if owner == 0 { + return + } + retire := func() []*segmentLease { + p.mu.Lock() + defer p.mu.Unlock() + retire := make([]*segmentLease, 0) + for key, entry := range p.entries { + if _, ok := entry.owners[owner]; !ok { + continue + } + delete(entry.owners, owner) + if len(entry.owners) != 0 || entry.published { + if entry.published && len(entry.owners) == 0 { + entry.owners = nil + } + continue + } + if entry.loading { + entry.retired = true + continue + } + delete(p.entries, key) + p.totalBytes -= baseEntryBytes(key) + if p.totalBytes < 0 { + p.totalBytes = 0 + } + if entry.lease != nil { + retire = append(retire, entry.lease) + } + } + return retire + }() + retireBaseLeases(retire) +} + +func (p *immutableBasePool) clearIndex(index string) { + p.commit(index, nil) +} + +func (p *immutableBasePool) clearAll() { + retire := func() []*segmentLease { + p.mu.Lock() + defer p.mu.Unlock() + retire := make([]*segmentLease, 0, len(p.entries)) + for key, entry := range p.entries { + if entry.loading { + p.retireLoadingLocked(entry) + continue + } + delete(p.entries, key) + if entry.lease != nil { + retire = append(retire, entry.lease) + } + } + p.totalBytes = 0 + return retire + }() + retireBaseLeases(retire) +} + +// errBaseLeaseGone is only reachable if a pool entry is retired concurrently +// with an acquire. The caller can retry the metadata/load sequence safely. +var errBaseLeaseGone = baseLeaseError("fulltext2 base lease retired") + +type baseLeaseError string + +func (e baseLeaseError) Error() string { return string(e) } diff --git a/pkg/fulltext2/base_pool_test.go b/pkg/fulltext2/base_pool_test.go new file mode 100644 index 0000000000000..26fc76bd70a0a --- /dev/null +++ b/pkg/fulltext2/base_pool_test.go @@ -0,0 +1,466 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "context" + "errors" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestSegmentLeaseFreeIsIdempotent(t *testing.T) { + template := NewSegment("base", 0) + lease := newSegmentLease(template) + view := lease.acquire(17) + require.NotNil(t, view) + require.Equal(t, int64(17), view.Recency) + + view.Free() + view.Free() + require.NotNil(t, lease.template, "the pool reference still owns the template") + lease.retire() + require.Nil(t, lease.template) + lease.retire() +} + +func TestSegmentLeaseAcquireAfterRetire(t *testing.T) { + lease := newSegmentLease(NewSegment("base", 0)) + lease.retire() + require.Nil(t, lease.acquire(0)) +} + +func TestImmutableBasePoolSingleflightAndRetire(t *testing.T) { + p := &immutableBasePool{entries: make(map[baseKey]*baseEntry)} + key := baseKey{index: "db.store", id: "base-0", checksum: "sum", filesize: 1} + var loads atomic.Int32 + var wg sync.WaitGroup + errs := make(chan error, 8) + for i := 0; i < 8; i++ { + wg.Add(1) + go func() { + defer wg.Done() + view, err := p.acquire(context.Background(), key, func() (*Segment, error) { + loads.Add(1) + return NewSegment("base-0", 0), nil + }, 3) + if err != nil { + errs <- err + return + } + view.Free() + }() + } + wg.Wait() + close(errs) + for err := range errs { + require.NoError(t, err) + } + require.Equal(t, int32(1), loads.Load()) + + p.commit(key.index, nil) + require.Empty(t, p.entries) +} + +func TestImmutableBasePoolWaiterCancellationDoesNotCancelLoader(t *testing.T) { + p := &immutableBasePool{entries: make(map[baseKey]*baseEntry)} + key := baseKey{index: "db.store", id: "base-0", checksum: "sum", filesize: 1} + started := make(chan struct{}) + release := make(chan struct{}) + loaderDone := make(chan error, 1) + go func() { + view, err := p.acquire(context.Background(), key, func() (*Segment, error) { + close(started) + <-release + return NewSegment("base-0", 0), nil + }, 0) + if view != nil { + view.Free() + } + loaderDone <- err + }() + <-started + + ctx, cancel := context.WithCancel(context.Background()) + waiterDone := make(chan error, 1) + go func() { + _, err := p.acquire(ctx, key, func() (*Segment, error) { + return nil, errors.New("canceled waiter became the loader") + }, 0) + waiterDone <- err + }() + select { + case err := <-waiterDone: + t.Fatalf("waiter returned before cancellation: %v", err) + case <-time.After(20 * time.Millisecond): + } + cancel() + select { + case err := <-waiterDone: + require.ErrorIs(t, err, context.Canceled) + case <-time.After(time.Second): + t.Fatal("canceled waiter did not return") + } + + close(release) + require.NoError(t, <-loaderDone) + p.clearAll() +} + +type doneSignalContext struct { + context.Context + doneCalled chan struct{} + once sync.Once +} + +func (c *doneSignalContext) Done() <-chan struct{} { + c.once.Do(func() { close(c.doneCalled) }) + return c.Context.Done() +} + +func TestImmutableBasePoolWaitingOwnerSurvivesObsoleteRollback(t *testing.T) { + p := &immutableBasePool{entries: make(map[baseKey]*baseEntry)} + key := baseKey{index: "db.store", id: "base-0", checksum: "sum", filesize: 1} + loaderStarted := make(chan struct{}) + releaseLoader := make(chan struct{}) + loaderDone := make(chan error, 1) + var loads atomic.Int32 + go func() { + view, err := p.acquireOwned(context.Background(), key, func() (*Segment, error) { + if loads.Add(1) == 1 { + close(loaderStarted) + <-releaseLoader + } + return NewSegment(key.id, 0), nil + }, 0, 11) + if view != nil { + view.Free() + } + loaderDone <- err + }() + <-loaderStarted + + waiting := &doneSignalContext{Context: context.Background(), doneCalled: make(chan struct{})} + replacementDone := make(chan error, 1) + go func() { + view, err := p.acquireOwned(waiting, key, func() (*Segment, error) { + loads.Add(1) + return NewSegment("replacement", 0), nil + }, 0, 22) + if view != nil { + view.Free() + } + replacementDone <- err + }() + select { + case <-waiting.doneCalled: + case <-time.After(time.Second): + t.Fatal("replacement did not reach the in-flight wait") + } + + p.mu.Lock() + _, claimed := p.entries[key].owners[22] + p.mu.Unlock() + require.True(t, claimed, "a waiting generation must claim the in-flight entry") + + p.rollback(11) + close(releaseLoader) + require.NoError(t, <-loaderDone) + require.NoError(t, <-replacementDone) + require.Equal(t, int32(1), loads.Load(), "the replacement must reuse the completed base") + require.Contains(t, p.entries, key) + + p.rollback(22) + p.clearAll() +} + +func TestImmutableBasePoolCanceledWaiterReleasesProvisionalOwner(t *testing.T) { + p := &immutableBasePool{entries: make(map[baseKey]*baseEntry)} + key := baseKey{index: "db.store", id: "base-0", checksum: "sum", filesize: 1} + loaderStarted := make(chan struct{}) + releaseLoader := make(chan struct{}) + loaderDone := make(chan error, 1) + go func() { + view, err := p.acquireOwned(context.Background(), key, func() (*Segment, error) { + close(loaderStarted) + <-releaseLoader + return NewSegment(key.id, 0), nil + }, 0, 11) + if view != nil { + view.Free() + } + loaderDone <- err + }() + <-loaderStarted + + ctx, cancel := context.WithCancel(context.Background()) + waiting := &doneSignalContext{Context: ctx, doneCalled: make(chan struct{})} + waiterDone := make(chan error, 1) + go func() { + _, err := p.acquireOwned(waiting, key, func() (*Segment, error) { + return nil, errors.New("canceled waiter became the loader") + }, 0, 22) + waiterDone <- err + }() + select { + case <-waiting.doneCalled: + case <-time.After(time.Second): + t.Fatal("waiter did not reach the in-flight wait") + } + + p.mu.Lock() + _, claimed := p.entries[key].owners[22] + p.mu.Unlock() + require.True(t, claimed, "the canceled waiter must have a provisional claim to release") + + cancel() + select { + case err := <-waiterDone: + require.ErrorIs(t, err, context.Canceled) + case <-time.After(time.Second): + t.Fatal("canceled waiter did not return") + } + p.rollback(22) + p.mu.Lock() + _, claimed = p.entries[key].owners[22] + _, loaderClaimed := p.entries[key].owners[11] + p.mu.Unlock() + require.False(t, claimed) + require.True(t, loaderClaimed) + + p.rollback(11) + close(releaseLoader) + require.NoError(t, <-loaderDone) + p.clearAll() +} + +func TestImmutableBasePoolBoundsEntriesAndEvictsIdle(t *testing.T) { + p := &immutableBasePool{ + entries: make(map[baseKey]*baseEntry), + maxEntries: 1, + maxBytes: 1 << 30, + idleTTL: time.Hour, + } + for i := 0; i < 2; i++ { + key := baseKey{index: "db" + string(rune('0'+i)), id: "base", checksum: "sum", filesize: 1} + view, err := p.acquire(context.Background(), key, func() (*Segment, error) { + return NewSegment(key.id, 0), nil + }, 0) + require.NoError(t, err) + view.Free() + } + require.LessOrEqual(t, len(p.entries), 1) + + p.idleTTL = time.Nanosecond + p.evict(time.Now().Add(time.Second)) + require.Empty(t, p.entries) + p.clearAll() +} + +func TestImmutableBasePoolBoundsBytes(t *testing.T) { + p := &immutableBasePool{ + entries: make(map[baseKey]*baseEntry), + maxEntries: 8, + maxBytes: 2, + idleTTL: time.Hour, + } + for i := 0; i < 2; i++ { + key := baseKey{index: "db.store", id: string(rune('a' + i)), checksum: "sum", filesize: 2} + view, err := p.acquire(context.Background(), key, func() (*Segment, error) { + return NewSegment(key.id, 0), nil + }, 0) + require.NoError(t, err) + view.Free() + } + require.LessOrEqual(t, p.totalBytes, int64(2)) + require.LessOrEqual(t, len(p.entries), 1) + p.clearAll() +} + +func TestImmutableBasePoolLoadFailureDoesNotRetainEntry(t *testing.T) { + p := &immutableBasePool{entries: make(map[baseKey]*baseEntry)} + key := baseKey{index: "db.store", id: "broken", checksum: "sum", filesize: 1} + want := errors.New("load failed") + _, err := p.acquire(context.Background(), key, func() (*Segment, error) { + return nil, want + }, 0) + require.ErrorIs(t, err, want) + require.Empty(t, p.entries) + require.Zero(t, p.totalBytes) +} + +func TestImmutableBasePoolInvalidationDuringLoadDoesNotRepoolStaleLease(t *testing.T) { + p := &immutableBasePool{entries: make(map[baseKey]*baseEntry)} + key := baseKey{index: "db.store", id: "base-0", checksum: "sum", filesize: 1} + started := make(chan struct{}) + release := make(chan struct{}) + var loads atomic.Int32 + done := make(chan error, 1) + go func() { + view, err := p.acquire(context.Background(), key, func() (*Segment, error) { + if loads.Add(1) == 1 { + close(started) + <-release + } + return NewSegment(key.id, 0), nil + }, 0) + if view != nil { + view.Free() + } + done <- err + }() + <-started + + p.clearIndex(key.index) + close(release) + require.NoError(t, <-done) + require.Equal(t, int32(1), loads.Load(), "the invalidated in-flight result must not be retried in place") + require.Empty(t, p.entries, "the stale in-flight result is owned by the current query, not the pool") + p.clearAll() + require.Empty(t, p.entries) +} + +func TestImmutableBasePoolCommittedReplacementSurvivesObsoleteRollback(t *testing.T) { + p := &immutableBasePool{entries: make(map[baseKey]*baseEntry)} + key := baseKey{index: "db.store", id: "base-0", checksum: "sum", filesize: 1} + + first, err := p.acquireOwned(context.Background(), key, func() (*Segment, error) { + return NewSegment("base-0", 0), nil + }, 0, 11) + require.NoError(t, err) + first.Free() + + // The replacement reuses the same immutable bytes and commits them before + // the obsolete loader reports its later failure. Commit transfers ownership + // away from the obsolete attempt, so its rollback must not retire this entry. + replacement, err := p.acquireOwned(context.Background(), key, func() (*Segment, error) { + return nil, errors.New("replacement unexpectedly became the loader") + }, 0, 22) + require.NoError(t, err) + replacement.Free() + p.commitOwned(key.index, map[baseKey]struct{}{key: {}}, 22) + p.rollback(11) + + require.Contains(t, p.entries, key) + p.clearAll() +} + +func TestImmutableBasePoolReusedClaimSurvivesObsoleteRollback(t *testing.T) { + p := &immutableBasePool{entries: make(map[baseKey]*baseEntry)} + key := baseKey{index: "db.store", id: "base-0", checksum: "sum", filesize: 1} + + first, err := p.acquireOwned(context.Background(), key, func() (*Segment, error) { + return NewSegment("base-0", 0), nil + }, 0, 11) + require.NoError(t, err) + t.Cleanup(func() { + first.Free() + p.clearAll() + }) + + replacement, err := p.acquireOwned(context.Background(), key, func() (*Segment, error) { + return nil, errors.New("replacement unexpectedly became the loader") + }, 0, 22) + require.NoError(t, err) + t.Cleanup(replacement.Free) + + // The replacement has claimed the reused lease but has not published its + // generation yet. The obsolete loader must not retire that lease first. + p.rollback(11) + p.commitOwned(key.index, map[baseKey]struct{}{key: {}}, 22) + + require.Contains(t, p.entries, key) +} + +func TestImmutableBasePoolPublishedReuseFailureKeepsEntry(t *testing.T) { + p := &immutableBasePool{entries: make(map[baseKey]*baseEntry)} + key := baseKey{index: "db.store", id: "base-0", checksum: "sum", filesize: 1} + + first, err := p.acquireOwned(context.Background(), key, func() (*Segment, error) { + return NewSegment("base-0", 0), nil + }, 0, 11) + require.NoError(t, err) + first.Free() + p.commitOwned(key.index, map[baseKey]struct{}{key: {}}, 11) + + replacement, err := p.acquireOwned(context.Background(), key, func() (*Segment, error) { + return nil, errors.New("published entry unexpectedly became the loader") + }, 0, 22) + require.NoError(t, err) + replacement.Free() + p.rollback(22) + + require.Contains(t, p.entries, key, "a failed reuse must not retire a published base") + p.clearAll() +} + +func TestImmutableBasePoolRollbackOwnerIsGlobalAcrossIndexes(t *testing.T) { + p := &immutableBasePool{entries: make(map[baseKey]*baseEntry)} + firstGeneration := beginLoadGeneration("db.a") + secondGeneration := beginLoadGeneration("db.b") + require.NotEqual(t, firstGeneration.owner, secondGeneration.owner) + + firstKey := baseKey{index: "db.a", id: "base-0", checksum: "sum", filesize: 1} + secondKey := baseKey{index: "db.b", id: "base-0", checksum: "sum", filesize: 1} + first, err := p.acquireOwned(context.Background(), firstKey, func() (*Segment, error) { + return NewSegment("base-a", 0), nil + }, 0, firstGeneration.owner) + require.NoError(t, err) + first.Free() + second, err := p.acquireOwned(context.Background(), secondKey, func() (*Segment, error) { + return NewSegment("base-b", 0), nil + }, 0, secondGeneration.owner) + require.NoError(t, err) + second.Free() + + p.rollback(firstGeneration.owner) + require.NotContains(t, p.entries, firstKey) + require.Contains(t, p.entries, secondKey) + p.clearAll() + endLoadGeneration(firstGeneration) + endLoadGeneration(secondGeneration) +} + +func TestLoadReasonRegistryIsDatabaseQualified(t *testing.T) { + cleanup := setLoadObserver(func(LoadEvent) {}) + defer cleanup() + key1 := loadReasonKey("db1", "store") + key2 := loadReasonKey("db2", "store") + rememberLoadReason(key1, LoadMissCDCFlush) + rememberLoadReason(key2, LoadMissMerge) + reason, generation := peekLoadReason(key1) + require.Equal(t, LoadMissCDCFlush, reason) + consumeLoadReason(key1, generation) + reason, generation = peekLoadReason(key2) + require.Equal(t, LoadMissMerge, reason) + consumeLoadReason(key2, generation) + reason, generation = peekLoadReason(key1) + require.Empty(t, reason) + require.Zero(t, generation) +} + +func TestLoadReasonRegistryIsDisabledWithObserverOff(t *testing.T) { + cleanup := setLoadObserver(nil) + defer cleanup() + key := loadReasonKey("db", "store") + rememberLoadReason(key, LoadMissCDCFlush) + reason, generation := peekLoadReason(key) + require.Empty(t, reason) + require.Zero(t, generation) +} diff --git a/pkg/fulltext2/load_observer.go b/pkg/fulltext2/load_observer.go new file mode 100644 index 0000000000000..a7908ae13c4d1 --- /dev/null +++ b/pkg/fulltext2/load_observer.go @@ -0,0 +1,197 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "encoding/json" + "os" + "sync" + "sync/atomic" + "time" + + "github.com/matrixorigin/matrixone/pkg/logutil" +) + +// LoadMissReason identifies the event which caused a cached FULLTEXT2 load. +// It is intentionally a small, bounded vocabulary: these values are suitable +// for diagnostics and must not contain query or primary-key data. +type LoadMissReason string + +const ( + LoadMissProcessStart LoadMissReason = "process_start" + LoadMissTTLExpired LoadMissReason = "ttl_expired" + LoadMissCDCFlush LoadMissReason = "cdc_flush" + LoadMissMerge LoadMissReason = "merge" + LoadMissRebuild LoadMissReason = "rebuild" + LoadMissGenerationChange LoadMissReason = "generation_changed" +) + +// LoadEvent is emitted once for one Fulltext2Search.Load attempt when the +// internal observer is installed or MO_FULLTEXT2_LOAD_TRACE=1 is set. The +// default process has no observer and does not sample any phase timestamps. +type LoadEvent struct { + Index string `json:"index"` + MissReason LoadMissReason `json:"miss_reason"` + BaseGeneration int64 `json:"base_generation"` + TailGeneration int64 `json:"tail_generation"` + BaseBytes int64 `json:"base_bytes"` + TailBytes int64 `json:"tail_bytes"` + InternalSQLTimeMicros int64 `json:"internal_sql_time_us"` + TempFileWriteMicros int64 `json:"temp_file_write_time_us"` + MmapMicros int64 `json:"mmap_time_us"` + ChecksumMicros int64 `json:"checksum_time_us"` + TotalLoadMicros int64 `json:"total_load_time_us"` + SingleflightWaiters int64 `json:"singleflight_waiters"` + LoadSuccess bool `json:"load_success"` + LoadError bool `json:"load_error"` + LoadCancel bool `json:"load_cancel"` +} + +type loadObserverHolder struct { + observe func(LoadEvent) +} + +var loadObserver atomic.Pointer[loadObserverHolder] + +// loadTraceEnabled is read once at process startup. The environment switch is +// diagnostic-only; it is not a SQL/session setting and remains off by default. +var loadTraceEnabled = os.Getenv("MO_FULLTEXT2_LOAD_TRACE") == "1" + +// setLoadObserver is intentionally package-private. Tests and an in-process +// diagnostic harness can install a bounded observer without adding a public +// FULLTEXT2 configuration surface. +func setLoadObserver(observe func(LoadEvent)) func() { + if observe == nil { + loadObserver.Store(nil) + return func() {} + } + loadObserver.Store(&loadObserverHolder{observe: observe}) + return func() { loadObserver.Store(nil) } +} + +func loadObservationEnabled() bool { + return loadObserver.Load() != nil || loadTraceEnabled +} + +func emitLoadEvent(event LoadEvent) { + if h := loadObserver.Load(); h != nil && h.observe != nil { + h.observe(event) + } + if !loadTraceEnabled { + return + } + b, err := json.Marshal(event) + if err == nil { + logutil.Debugf("[ft2-load] %s", b) + } +} + +type loadTrace struct { + mu sync.Mutex + event LoadEvent + start time.Time + phase phaseTimes + ended atomic.Bool +} + +type phaseTimes struct { + internalSQL time.Duration + tempWrite time.Duration + mmap time.Duration + checksum time.Duration +} + +func newLoadTrace(index string, reason LoadMissReason) *loadTrace { + if !loadObservationEnabled() { + return nil + } + return &loadTrace{event: LoadEvent{Index: index, MissReason: reason}, start: time.Now()} +} + +func (t *loadTrace) addInternalSQL(d time.Duration) { + if t != nil { + t.mu.Lock() + defer t.mu.Unlock() + t.phase.internalSQL += d + } +} + +func (t *loadTrace) addTempWrite(d time.Duration) { + if t != nil { + t.mu.Lock() + defer t.mu.Unlock() + t.phase.tempWrite += d + } +} + +func (t *loadTrace) addMmap(d time.Duration) { + if t != nil { + t.mu.Lock() + defer t.mu.Unlock() + t.phase.mmap += d + } +} + +func (t *loadTrace) addChecksum(d time.Duration) { + if t != nil { + t.mu.Lock() + defer t.mu.Unlock() + t.phase.checksum += d + } +} + +func (t *loadTrace) addBaseBytes(n int64) { + if t != nil { + t.mu.Lock() + defer t.mu.Unlock() + t.event.BaseBytes += n + } +} + +func (t *loadTrace) addTailBytes(n int64) { + if t != nil { + t.mu.Lock() + defer t.mu.Unlock() + t.event.TailBytes += n + } +} + +func (t *loadTrace) setGeneration(base, tail int64) { + if t != nil { + t.mu.Lock() + defer t.mu.Unlock() + t.event.BaseGeneration = base + t.event.TailGeneration = tail + } +} + +func (t *loadTrace) finish(err error, canceled bool, waiters int64) { + if t == nil || !t.ended.CompareAndSwap(false, true) { + return + } + t.mu.Lock() + t.event.InternalSQLTimeMicros = t.phase.internalSQL.Microseconds() + t.event.TempFileWriteMicros = t.phase.tempWrite.Microseconds() + t.event.MmapMicros = t.phase.mmap.Microseconds() + t.event.ChecksumMicros = t.phase.checksum.Microseconds() + t.event.TotalLoadMicros = time.Since(t.start).Microseconds() + t.event.SingleflightWaiters = waiters + t.event.LoadError = err != nil && !canceled + t.event.LoadCancel = canceled + t.event.LoadSuccess = err == nil && !canceled + event := t.event + t.mu.Unlock() + emitLoadEvent(event) +} diff --git a/pkg/fulltext2/load_observer_test.go b/pkg/fulltext2/load_observer_test.go new file mode 100644 index 0000000000000..a67518817a4b5 --- /dev/null +++ b/pkg/fulltext2/load_observer_test.go @@ -0,0 +1,110 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "context" + "errors" + "sync" + "testing" + "time" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/stretchr/testify/require" +) + +func TestLoadTraceEmitsExactlyOnce(t *testing.T) { + var mu sync.Mutex + var events []LoadEvent + restore := setLoadObserver(func(event LoadEvent) { + mu.Lock() + events = append(events, event) + mu.Unlock() + }) + defer restore() + + trace := newLoadTrace("db.store", LoadMissCDCFlush) + trace.addInternalSQL(2 * time.Millisecond) + trace.addTempWrite(3 * time.Millisecond) + trace.addMmap(4 * time.Millisecond) + trace.addChecksum(5 * time.Millisecond) + trace.addBaseBytes(11) + trace.addTailBytes(7) + trace.setGeneration(12, 34) + trace.finish(nil, false, 2) + trace.finish(errors.New("late error"), false, 9) + + mu.Lock() + defer mu.Unlock() + require.Len(t, events, 1) + event := events[0] + require.Equal(t, "db.store", event.Index) + require.Equal(t, LoadMissCDCFlush, event.MissReason) + require.Equal(t, int64(12), event.BaseGeneration) + require.Equal(t, int64(34), event.TailGeneration) + require.Equal(t, int64(11), event.BaseBytes) + require.Equal(t, int64(7), event.TailBytes) + require.Equal(t, int64(2), event.SingleflightWaiters) + require.True(t, event.LoadSuccess) + require.False(t, event.LoadError) + require.False(t, event.LoadCancel) + require.GreaterOrEqual(t, event.TotalLoadMicros, int64(0)) +} + +func TestLoadTraceCancellationClassification(t *testing.T) { + var got LoadEvent + restore := setLoadObserver(func(event LoadEvent) { got = event }) + defer restore() + trace := newLoadTrace("store", LoadMissTTLExpired) + trace.finish(contextCanceledError{}, true, 0) + require.True(t, got.LoadCancel) + require.False(t, got.LoadError) + require.False(t, got.LoadSuccess) +} + +func TestLoadCancellationErrorRecognizesProductionErrors(t *testing.T) { + tests := []struct { + name string + err error + }{ + {name: "context canceled", err: context.Canceled}, + {name: "context deadline", err: context.DeadlineExceeded}, + {name: "query interrupted", err: moerr.NewQueryInterrupted(context.Background())}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.True(t, isLoadCancellationError(tt.err)) + }) + } + require.False(t, isLoadCancellationError(errors.New("storage failure"))) +} + +func TestFulltext2SearchFinishesPendingTraceAfterWaiterSample(t *testing.T) { + var got LoadEvent + restore := setLoadObserver(func(event LoadEvent) { got = event }) + defer restore() + s := NewFulltext2Search(TableConfig{DbName: "db", IndexTable: "store"}) + s.pendingTrace = newLoadTrace("store", LoadMissGenerationChange) + s.pendingLoadErr = nil + s.SetLoadWaiters(4) + s.FinishLoadObservation() + require.Equal(t, int64(4), got.SingleflightWaiters) + require.True(t, got.LoadSuccess) + s.FinishLoadObservation() +} + +type contextCanceledError struct{} + +func (contextCanceledError) Error() string { return "canceled" } diff --git a/pkg/fulltext2/load_reason.go b/pkg/fulltext2/load_reason.go new file mode 100644 index 0000000000000..77c97f1b84e15 --- /dev/null +++ b/pkg/fulltext2/load_reason.go @@ -0,0 +1,335 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "sync" + "sync/atomic" + "time" + + veccache "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" +) + +const ( + loadReasonTTL = 15 * time.Minute + loadReasonSize = 1024 + loadGenerationTTL = 15 * time.Minute + loadGenerationSize = 1024 +) + +type pendingLoadReason struct { + reason LoadMissReason + generation uint64 + at time.Time +} + +// loadGeneration identifies one cache-load attempt and the invalidation epoch +// it observed. The invalidation epoch is monotonic per index; the attempt is +// unique per index so an older replacement load cannot publish over a newer +// one even when both observe the same invalidation. +type loadGeneration struct { + index string + invalidation uint64 + attempt uint64 + owner uint64 +} + +type loadGenerationState struct { + invalidation uint64 + attempt uint64 + active uint32 + lastUsed time.Time +} + +var loadGenerations = struct { + sync.Mutex + m map[string]loadGenerationState +}{m: make(map[string]loadGenerationState)} + +var nextLoadOwner atomic.Uint64 + +// pendingLoadReasons bridges an invalidation event and the next cache miss. A +// failed load leaves the cause available for its retry; a successful load +// consumes only the entry observed by that load. It is bounded and expiring +// because an index may be invalidated without being queried again. The +// registry is touched only on invalidation/load paths, never by a warm search. +var pendingLoadReasons = struct { + sync.Mutex + m map[string]pendingLoadReason +}{m: make(map[string]pendingLoadReason)} + +var reusableLoadLifecycleOnce sync.Once + +func ensureReusableLoadLifecycle() { + reusableLoadLifecycleOnce.Do(func() { + veccache.RegisterLifecycleHook(func(shutdown bool) { + if shutdown { + loadedBasePool.clearAll() + loadedTailPool.clearAll() + clearLoadGenerationRegistry() + return + } + now := time.Now() + loadedBasePool.evict(now) + loadedTailPool.evict(now) + }) + }) +} + +// loadReasonKey is the bounded registry key used by cache invalidation hooks. +// The database qualifier avoids conflating identically named hidden stores in +// different databases while keeping the key free of query or primary-key data. +func loadReasonKey(db, index string) string { + if db == "" { + return index + } + return db + "." + index +} + +func rememberLoadReason(index string, reason LoadMissReason) { + now := time.Now() + pendingLoadReasons.Lock() + defer pendingLoadReasons.Unlock() + loadGenerations.Lock() + defer loadGenerations.Unlock() + state := loadGenerations.m[index] + state.invalidation++ + state.lastUsed = now + loadGenerations.m[index] = state + pruneLoadGenerationsLocked(now) + rememberLoadReasonLocked(index, reason, state.invalidation) +} + +func pruneLoadGenerationsLocked(now time.Time) { + for index, state := range loadGenerations.m { + if state.active == 0 && !state.lastUsed.IsZero() && now.Sub(state.lastUsed) >= loadGenerationTTL { + delete(loadGenerations.m, index) + } + } + for len(loadGenerations.m) > loadGenerationSize { + var oldestIndex string + var oldest time.Time + for index, state := range loadGenerations.m { + if state.active != 0 { + continue + } + if oldestIndex == "" || state.lastUsed.Before(oldest) { + oldestIndex, oldest = index, state.lastUsed + } + } + if oldestIndex == "" { + return + } + delete(loadGenerations.m, oldestIndex) + } +} + +func rememberLoadReasonLocked(index string, reason LoadMissReason, generation uint64) { + if !loadObservationEnabled() || index == "" || reason == "" || reason == LoadMissReason("process_shutdown") { + return + } + now := time.Now() + for k, v := range pendingLoadReasons.m { + if now.Sub(v.at) >= loadReasonTTL { + delete(pendingLoadReasons.m, k) + } + } + if len(pendingLoadReasons.m) >= loadReasonSize { + var oldestKey string + var oldest time.Time + for k, v := range pendingLoadReasons.m { + if oldestKey == "" || v.at.Before(oldest) { + oldestKey, oldest = k, v.at + } + } + if oldestKey != "" { + delete(pendingLoadReasons.m, oldestKey) + } + } + pendingLoadReasons.m[index] = pendingLoadReason{reason: reason, generation: generation, at: now} +} + +func peekLoadReason(index string) (LoadMissReason, uint64) { + if !loadObservationEnabled() { + return "", 0 + } + now := time.Now() + pendingLoadReasons.Lock() + defer pendingLoadReasons.Unlock() + v, ok := pendingLoadReasons.m[index] + if !ok { + return "", 0 + } + if now.Sub(v.at) >= loadReasonTTL { + delete(pendingLoadReasons.m, index) + return "", 0 + } + return v.reason, v.generation +} + +func consumeLoadReason(index string, generation uint64) { + if index == "" || generation == 0 { + return + } + pendingLoadReasons.Lock() + defer pendingLoadReasons.Unlock() + if v, ok := pendingLoadReasons.m[index]; ok && v.generation == generation { + delete(pendingLoadReasons.m, index) + } +} + +func consumeLoadReasonIfCurrent(index string, reasonGeneration uint64, current loadGeneration) bool { + if index == "" || reasonGeneration == 0 { + return false + } + pendingLoadReasons.Lock() + defer pendingLoadReasons.Unlock() + loadGenerations.Lock() + defer loadGenerations.Unlock() + state, ok := loadGenerations.m[current.index] + if !ok || state.invalidation != current.invalidation || state.attempt != current.attempt { + return false + } + if v, ok := pendingLoadReasons.m[index]; ok && v.generation == reasonGeneration { + delete(pendingLoadReasons.m, index) + state.lastUsed = time.Now() + loadGenerations.m[current.index] = state + return true + } + return false +} + +func beginLoadGeneration(index string) loadGeneration { + now := time.Now() + loadGenerations.Lock() + defer loadGenerations.Unlock() + pruneLoadGenerationsLocked(now) + state := loadGenerations.m[index] + state.attempt++ + state.active++ + state.lastUsed = now + loadGenerations.m[index] = state + return loadGeneration{ + index: index, + invalidation: state.invalidation, + attempt: state.attempt, + owner: nextLoadOwner.Add(1), + } +} + +func endLoadGeneration(g loadGeneration) { + if g.index == "" || g.attempt == 0 { + return + } + now := time.Now() + loadGenerations.Lock() + defer loadGenerations.Unlock() + state, ok := loadGenerations.m[g.index] + if !ok { + return + } + if state.active > 0 { + state.active-- + } + state.lastUsed = now + loadGenerations.m[g.index] = state + pruneLoadGenerationsLocked(now) +} + +func loadGenerationCurrent(g loadGeneration) bool { + if g.index == "" || g.attempt == 0 { + return true + } + loadGenerations.Lock() + defer loadGenerations.Unlock() + state, ok := loadGenerations.m[g.index] + if !ok || state.invalidation != g.invalidation || state.attempt != g.attempt { + return false + } + state.lastUsed = time.Now() + loadGenerations.m[g.index] = state + return true +} + +func clearLoadGeneration(index string) { + if index == "" { + return + } + pendingLoadReasons.Lock() + loadGenerations.Lock() + defer func() { + loadGenerations.Unlock() + pendingLoadReasons.Unlock() + }() + delete(pendingLoadReasons.m, index) + state, ok := loadGenerations.m[index] + if !ok { + return + } + if state.active == 0 { + delete(loadGenerations.m, index) + return + } + state.invalidation++ + state.lastUsed = time.Now() + loadGenerations.m[index] = state +} + +func clearLoadGenerationRegistry() { + pendingLoadReasons.Lock() + loadGenerations.Lock() + pendingLoadReasons.m = make(map[string]pendingLoadReason) + loadGenerations.m = make(map[string]loadGenerationState) + loadGenerations.Unlock() + pendingLoadReasons.Unlock() +} + +func clearReusableLoadGeneration(cfg TableConfig) { + index := loadReasonKey(cfg.DbName, cfg.IndexTable) + clearLoadGeneration(index) + clearReusableLoadPools(cfg) +} + +func clearReusableLoadPools(cfg TableConfig) { + index := loadReasonKey(cfg.DbName, cfg.IndexTable) + loadedBasePool.clearIndex(index) + loadedTailPool.clear(index) +} + +// invalidateLoadGeneration records why the next load will miss and clears +// reusable immutable state only when the base itself is known to have changed. +// It runs from the generic cache's invalidation hook, so the existing cache +// lifecycle remains the only integration surface for other index algorithms. +func invalidateLoadGeneration(cfg TableConfig, reason LoadMissReason) { + index := loadReasonKey(cfg.DbName, cfg.IndexTable) + rememberLoadReason(index, reason) + switch reason { + case LoadMissMerge, LoadMissRebuild, LoadMissReason("process_shutdown"): + if reason == LoadMissReason("process_shutdown") { + loadedBasePool.clearAll() + loadedTailPool.clearAll() + } else { + clearReusableLoadPools(cfg) + } + case LoadMissTTLExpired, LoadMissGenerationChange: + // Keep the current index's immutable base/tail reusable across CDC and + // TTL refreshes, but sweep stale entries belonging to indexes that have + // not been queried recently. This gives the process-global pools a + // bounded idle lifetime without making every CDC refresh cold-load its + // own base. + loadedBasePool.evict(time.Now()) + loadedTailPool.evict(time.Now()) + } +} diff --git a/pkg/fulltext2/load_reason_test.go b/pkg/fulltext2/load_reason_test.go new file mode 100644 index 0000000000000..262f606848970 --- /dev/null +++ b/pkg/fulltext2/load_reason_test.go @@ -0,0 +1,119 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestLoadReasonRegistryDisabledObserverIsNoOp(t *testing.T) { + cleanup := setLoadObserver(nil) + defer cleanup() + key := loadReasonKey("db", "store") + rememberLoadReason(key, LoadMissCDCFlush) + reason, generation := peekLoadReason(key) + require.Empty(t, reason) + require.Zero(t, generation) +} + +func TestLoadReasonRegistryStoresOnlyWhenEnabled(t *testing.T) { + cleanup := setLoadObserver(func(LoadEvent) {}) + defer cleanup() + key := loadReasonKey("db", "store") + rememberLoadReason(key, LoadMissCDCFlush) + reason, generation := peekLoadReason(key) + require.Equal(t, LoadMissCDCFlush, reason) + require.NotZero(t, generation) + consumeLoadReason(key, generation) + reason, generation = peekLoadReason(key) + require.Empty(t, reason) + require.Zero(t, generation) +} + +func TestLoadReasonRegistryBoundsExpiryAndInvalidation(t *testing.T) { + cleanupObserver := setLoadObserver(func(LoadEvent) {}) + defer cleanupObserver() + + pendingLoadReasons.Lock() + previous := pendingLoadReasons.m + pendingLoadReasons.m = make(map[string]pendingLoadReason) + pendingLoadReasons.Unlock() + t.Cleanup(func() { + pendingLoadReasons.Lock() + pendingLoadReasons.m = previous + pendingLoadReasons.Unlock() + }) + + require.Equal(t, "index", loadReasonKey("", "index")) + pendingLoadReasons.Lock() + pendingLoadReasons.m["expired"] = pendingLoadReason{reason: LoadMissTTLExpired, at: time.Now().Add(-loadReasonTTL - time.Second)} + pendingLoadReasons.m["oldest"] = pendingLoadReason{reason: LoadMissCDCFlush, at: time.Now().Add(-time.Second)} + for i := 0; i < loadReasonSize-1; i++ { + pendingLoadReasons.m["seed-"+string(rune(i))] = pendingLoadReason{reason: LoadMissMerge, at: time.Now()} + } + pendingLoadReasons.Unlock() + + rememberLoadReason("overflow", LoadMissRebuild) + reason, generation := peekLoadReason("expired") + require.Empty(t, reason) + require.Zero(t, generation) + reason, generation = peekLoadReason("oldest") + require.Empty(t, reason) + require.Zero(t, generation) + reason, generation = peekLoadReason("overflow") + require.Equal(t, LoadMissRebuild, reason) + consumeLoadReason("overflow", generation) + reason, generation = peekLoadReason("missing") + require.Empty(t, reason) + require.Zero(t, generation) + + cfg := TableConfig{DbName: "db", IndexTable: "store"} + invalidateLoadGeneration(cfg, LoadMissCDCFlush) + reason, generation = peekLoadReason("db.store") + require.Equal(t, LoadMissCDCFlush, reason) + consumeLoadReason("db.store", generation) + invalidateLoadGeneration(cfg, LoadMissReason("process_shutdown")) + reason, generation = peekLoadReason("db.store") + require.Empty(t, reason) + require.Zero(t, generation) +} + +func TestLoadReasonRegistryDoesNotConsumeNewerInvalidationAfterReasonReplacement(t *testing.T) { + cleanupObserver := setLoadObserver(func(LoadEvent) {}) + defer cleanupObserver() + + pendingLoadReasons.Lock() + previous := pendingLoadReasons.m + pendingLoadReasons.m = make(map[string]pendingLoadReason) + pendingLoadReasons.Unlock() + t.Cleanup(func() { + pendingLoadReasons.Lock() + pendingLoadReasons.m = previous + pendingLoadReasons.Unlock() + }) + + key := loadReasonKey("db", "store") + rememberLoadReason(key, LoadMissCDCFlush) + firstReason, firstGeneration := peekLoadReason(key) + require.Equal(t, LoadMissCDCFlush, firstReason) + rememberLoadReason(key, LoadMissRebuild) + consumeLoadReason(key, firstGeneration) + secondReason, secondGeneration := peekLoadReason(key) + require.Equal(t, LoadMissRebuild, secondReason) + consumeLoadReason(key, secondGeneration) +} diff --git a/pkg/fulltext2/plugin/compile/compile.go b/pkg/fulltext2/plugin/compile/compile.go index f14e49a83b710..22544ca757912 100644 --- a/pkg/fulltext2/plugin/compile/compile.go +++ b/pkg/fulltext2/plugin/compile/compile.go @@ -117,6 +117,7 @@ func buildAndRegisterCDC(ctx compileplugin.CompileContext, storeDef, metaDef *pl return err } } + fulltext2.NewFulltext2Search(cfg).OnCacheInvalidated(string(fulltext2.LoadMissRebuild)) cache.Cache.Remove(storeDef.IndexTableName) } // buildFromSource clears the prior tag=0 bases (idempotent) and rebuilds them. @@ -404,9 +405,21 @@ func (Hooks) ValidateReindexParams(old map[string]string, alter compileplugin.Re return merged, nil } -// HandleDropIndex — no algorithm-specific cleanup beyond the generic hidden-table -// deletion the SQL layer performs. -func (Hooks) HandleDropIndex(_ compileplugin.CompileContext, _ map[string]*plan.IndexDef) error { +// HandleDropIndex clears any pooled immutable generations as well as the +// generic cache entry. The cache entry may already be absent after TTL +// eviction, while its base pool is intentionally retained for a later warm +// load; DROP must not leave that storage mapping behind. +func (Hooks) HandleDropIndex(ctx compileplugin.CompileContext, indexDefs map[string]*plan.IndexDef) error { + if ctx == nil { + return nil + } + storeDef, ok := indexDefs[catalog.FullText2Index_TblType_Storage] + if !ok || storeDef == nil || storeDef.IndexTableName == "" { + return nil + } + cfg := fulltext2.TableConfig{DbName: ctx.QryDatabase(), IndexTable: storeDef.IndexTableName} + fulltext2.NewFulltext2Search(cfg).OnCacheInvalidated("") + cache.Cache.Remove(storeDef.IndexTableName) return nil } diff --git a/pkg/fulltext2/plugin/compile/parser_coverage_test.go b/pkg/fulltext2/plugin/compile/parser_coverage_test.go new file mode 100644 index 0000000000000..f361f8c8e1b56 --- /dev/null +++ b/pkg/fulltext2/plugin/compile/parser_coverage_test.go @@ -0,0 +1,27 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package compile + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParserFromParams(t *testing.T) { + require.Empty(t, parserFromParams("")) + require.Empty(t, parserFromParams("{")) + require.Equal(t, "gojieba", parserFromParams(`{"parser":"gojieba"}`)) +} diff --git a/pkg/fulltext2/reuse_pool_coverage_test.go b/pkg/fulltext2/reuse_pool_coverage_test.go new file mode 100644 index 0000000000000..ff8ae3658f902 --- /dev/null +++ b/pkg/fulltext2/reuse_pool_coverage_test.go @@ -0,0 +1,515 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "context" + "errors" + "fmt" + "sync" + "testing" + "time" + + "github.com/matrixorigin/matrixone/pkg/container/batch" + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/matrixorigin/matrixone/pkg/util/executor" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + veccache "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" + "github.com/stretchr/testify/require" +) + +func TestReusableBasePoolBoundaryStates(t *testing.T) { + p := &immutableBasePool{entries: make(map[baseKey]*baseEntry)} + maxEntries, maxBytes, idleTTL := p.limits() + require.Equal(t, basePoolMaxEntries, maxEntries) + require.Equal(t, basePoolMaxBytes, maxBytes) + require.Equal(t, basePoolIdleTTL, idleTTL) + require.Zero(t, baseEntryBytes(baseKey{})) + require.Equal(t, int64(7), baseEntryBytes(baseKey{filesize: 7})) + + zeroLease := &segmentLease{} + zeroLease.release() + zeroLease.retire() + lease := newSegmentLease(NewSegment("lease", 0)) + view := lease.acquire(0) + require.NotNil(t, view) + view.Free() + lease.retire() + lease.release() + lease.retire() + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + key := baseKey{index: "db.store", id: "cancelled", filesize: 1} + _, err := p.acquire(ctx, key, func() (*Segment, error) { + return nil, errors.New("cancelled context became the loader") + }, 0) + require.ErrorIs(t, err, context.Canceled) + + _, err = p.acquire(nil, key, func() (*Segment, error) { + return nil, nil + }, 0) + require.ErrorIs(t, err, errBaseLeaseGone) + require.Empty(t, p.entries) + + _, err = p.acquire(nil, key, func() (*Segment, error) { + panic("synthetic loader panic") + }, 0) + require.ErrorContains(t, err, "base load panic") + require.Empty(t, p.entries) + + p.entries[baseKey{index: "loading"}] = &baseEntry{loading: true} + p.entries[baseKey{index: "empty"}] = &baseEntry{} + p.evict(time.Now()) + loading := p.entries[baseKey{index: "loading"}] + require.NotNil(t, loading) + p.commit("loading", nil) + require.True(t, loading.retired) + loading.loading = false + p.clearAll() + require.Empty(t, p.entries) + + key = baseKey{index: "db.store", id: "kept", filesize: 1} + view, err = p.acquire(context.Background(), key, func() (*Segment, error) { + return NewSegment(key.id, 0), nil + }, 0) + require.NoError(t, err) + view.Free() + p.commit(key.index, map[baseKey]struct{}{key: {}}) + require.Contains(t, p.entries, key) + p.clearIndex(key.index) + require.Empty(t, p.entries) +} + +func TestLoadReasonRegistryBoundariesAndInvalidation(t *testing.T) { + restore := setLoadObserver(func(LoadEvent) {}) + defer restore() + + pendingLoadReasons.Lock() + old := pendingLoadReasons.m + pendingLoadReasons.m = make(map[string]pendingLoadReason) + pendingLoadReasons.Unlock() + t.Cleanup(func() { + pendingLoadReasons.Lock() + pendingLoadReasons.m = old + pendingLoadReasons.Unlock() + }) + + require.Equal(t, "store", loadReasonKey("", "store")) + rememberLoadReason("", LoadMissCDCFlush) + rememberLoadReason("store", "") + reason, generation := peekLoadReason("store") + require.Empty(t, reason) + require.Zero(t, generation) + + pendingLoadReasons.Lock() + pendingLoadReasons.m["expired"] = pendingLoadReason{ + reason: LoadMissCDCFlush, + at: time.Now().Add(-loadReasonTTL), + } + pendingLoadReasons.Unlock() + rememberLoadReason("fresh", LoadMissMerge) + reason, generation = peekLoadReason("expired") + require.Empty(t, reason) + require.Zero(t, generation) + reason, generation = peekLoadReason("fresh") + require.Equal(t, LoadMissMerge, reason) + consumeLoadReason("fresh", generation) + + for i := 0; i < loadReasonSize+1; i++ { + rememberLoadReason(fmt.Sprintf("db.%d", i), LoadMissCDCFlush) + } + pendingLoadReasons.Lock() + require.LessOrEqual(t, len(pendingLoadReasons.m), loadReasonSize) + pendingLoadReasons.Unlock() + + cfg := TableConfig{DbName: "db", IndexTable: "store"} + invalidateLoadGeneration(cfg, LoadMissCDCFlush) + reason, generation = peekLoadReason(loadReasonKey(cfg.DbName, cfg.IndexTable)) + require.Equal(t, LoadMissCDCFlush, reason) + consumeLoadReason(loadReasonKey(cfg.DbName, cfg.IndexTable), generation) + invalidateLoadGeneration(cfg, LoadMissTTLExpired) + invalidateLoadGeneration(cfg, LoadMissGenerationChange) + invalidateLoadGeneration(cfg, LoadMissMerge) + invalidateLoadGeneration(cfg, LoadMissRebuild) + invalidateLoadGeneration(cfg, LoadMissReason("process_shutdown")) +} + +func TestInvalidateLoadGenerationPreservesReplacementReasonAndGeneration(t *testing.T) { + restore := setLoadObserver(func(LoadEvent) {}) + defer restore() + + pendingLoadReasons.Lock() + oldReasons := pendingLoadReasons.m + pendingLoadReasons.m = make(map[string]pendingLoadReason) + pendingLoadReasons.Unlock() + loadGenerations.Lock() + oldGenerations := loadGenerations.m + loadGenerations.m = make(map[string]loadGenerationState) + loadGenerations.Unlock() + t.Cleanup(func() { + pendingLoadReasons.Lock() + pendingLoadReasons.m = oldReasons + pendingLoadReasons.Unlock() + loadGenerations.Lock() + loadGenerations.m = oldGenerations + loadGenerations.Unlock() + }) + + cfg := TableConfig{DbName: "db", IndexTable: "store"} + for generation, reason := range []LoadMissReason{LoadMissMerge, LoadMissRebuild} { + invalidateLoadGeneration(cfg, reason) + gotReason, gotGeneration := peekLoadReason(loadReasonKey(cfg.DbName, cfg.IndexTable)) + require.Equal(t, reason, gotReason) + require.Equal(t, uint64(generation+1), gotGeneration) + consumeLoadReason(loadReasonKey(cfg.DbName, cfg.IndexTable), gotGeneration) + } + NewFulltext2Search(cfg).OnCacheInvalidated("") + gotReason, gotGeneration := peekLoadReason(loadReasonKey(cfg.DbName, cfg.IndexTable)) + require.Empty(t, gotReason) + require.Zero(t, gotGeneration) +} + +func TestLoadReasonRegistryDoesNotConsumeNewerInvalidation(t *testing.T) { + restore := setLoadObserver(func(LoadEvent) {}) + defer restore() + + pendingLoadReasons.Lock() + old := pendingLoadReasons.m + pendingLoadReasons.m = make(map[string]pendingLoadReason) + pendingLoadReasons.Unlock() + t.Cleanup(func() { + pendingLoadReasons.Lock() + pendingLoadReasons.m = old + pendingLoadReasons.Unlock() + }) + + key := loadReasonKey("db", "store") + rememberLoadReason(key, LoadMissCDCFlush) + firstReason, firstGeneration := peekLoadReason(key) + require.Equal(t, LoadMissCDCFlush, firstReason) + rememberLoadReason(key, LoadMissRebuild) + consumeLoadReason(key, firstGeneration) + secondReason, secondGeneration := peekLoadReason(key) + require.Equal(t, LoadMissRebuild, secondReason) + consumeLoadReason(key, secondGeneration) +} + +func TestLoadGenerationRegistryBoundsInactiveKeys(t *testing.T) { + restoreObserver := setLoadObserver(nil) + defer restoreObserver() + + loadGenerations.Lock() + previous := loadGenerations.m + loadGenerations.m = make(map[string]loadGenerationState) + loadGenerations.Unlock() + defer func() { + loadGenerations.Lock() + loadGenerations.m = previous + loadGenerations.Unlock() + }() + + active := beginLoadGeneration("db.active") + for i := 0; i < loadGenerationSize*2; i++ { + generation := beginLoadGeneration(fmt.Sprintf("db.%d", i)) + endLoadGeneration(generation) + } + require.True(t, loadGenerationCurrent(active)) + + loadGenerations.Lock() + require.LessOrEqual(t, len(loadGenerations.m), loadGenerationSize) + require.Equal(t, uint32(1), loadGenerations.m[active.index].active) + loadGenerations.Unlock() + + endLoadGeneration(active) + loadGenerations.Lock() + require.LessOrEqual(t, len(loadGenerations.m), loadGenerationSize) + loadGenerations.Unlock() +} + +func TestReusableLoadLifecycleHookRunsHousekeepingAndShutdown(t *testing.T) { + ensureReusableLoadLifecycle() + c := veccache.NewVectorIndexCache() + c.HouseKeeping() + c.Destroy() +} + +func TestReasonlessDropInvalidatesGenerationBeforeClearingReusablePools(t *testing.T) { + loadedBasePool.clearAll() + loadedTailPool.clearAll() + + cfg := TableConfig{DbName: "db", IndexTable: "drop-race"} + index := loadReasonKey(cfg.DbName, cfg.IndexTable) + generation := beginLoadGeneration(index) + t.Cleanup(func() { endLoadGeneration(generation) }) + t.Cleanup(func() { + loadedBasePool.clearAll() + loadedTailPool.clearAll() + }) + + oldBaseKey := baseKey{index: index, id: "old-base", checksum: "sum", filesize: 1} + oldBaseLease := newSegmentLease(NewSegment(oldBaseKey.id, 0)) + loadedBasePool.mu.Lock() + loadedBasePool.entries[oldBaseKey] = &baseEntry{ + lease: oldBaseLease, + published: true, + lastUsed: time.Now(), + } + loadedBasePool.totalBytes += baseEntryBytes(oldBaseKey) + loadedBasePool.mu.Unlock() + oldBaseLease.mu.Lock() + + oldTail := newTailState(1, 1, []*Segment{NewSegment("old-tail", 0)}, nil) + oldTailLease := oldTail.segments[0] + loadedTailPool.mu.Lock() + loadedTailPool.states[index] = oldTail + loadedTailPool.totalBytes += oldTail.bytes + loadedTailPool.mu.Unlock() + oldTailLease.mu.Lock() + + var releaseBase, releaseTail sync.Once + unlockBase := func() { releaseBase.Do(func() { oldBaseLease.mu.Unlock() }) } + unlockTail := func() { releaseTail.Do(func() { oldTailLease.mu.Unlock() }) } + t.Cleanup(unlockBase) + t.Cleanup(unlockTail) + + done := make(chan struct{}) + go func() { + NewFulltext2Search(cfg).OnCacheInvalidated("") + close(done) + }() + + require.Eventually(t, func() bool { + loadedBasePool.mu.Lock() + _, ok := loadedBasePool.entries[oldBaseKey] + loadedBasePool.mu.Unlock() + return !ok + }, time.Second, time.Millisecond) + + newBaseKey := baseKey{index: index, id: "new-base", checksum: "sum", filesize: 1} + baseView, err := loadedBasePool.acquireOwned(context.Background(), newBaseKey, func() (*Segment, error) { + return NewSegment(newBaseKey.id, 0), nil + }, 0, generation.owner) + require.NoError(t, err) + baseView.Free() + basePublished := loadedBasePool.commitOwnedIfCurrent( + index, + map[baseKey]struct{}{newBaseKey: {}}, + generation.owner, + func() bool { return loadGenerationCurrent(generation) }, + ) + if !basePublished { + loadedBasePool.rollback(generation.owner) + } + require.False(t, basePublished, "DROP must invalidate an active base load before pool clearing") + + unlockBase() + require.Eventually(t, func() bool { + loadedTailPool.mu.Lock() + _, ok := loadedTailPool.states[index] + loadedTailPool.mu.Unlock() + return !ok + }, time.Second, time.Millisecond) + + newTail := newTailState(1, 2, []*Segment{NewSegment("new-tail", 0)}, nil) + views, _, tailPublished := loadedTailPool.installAndAcquireIfCurrent( + index, + newTail, + func() bool { return loadGenerationCurrent(generation) }, + ) + if tailPublished { + freeSegs(views) + } else { + retireTailStates([]*tailState{newTail}) + } + require.False(t, tailPublished, "DROP must invalidate an active tail load before pool clearing") + + unlockTail() + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("reasonless DROP did not finish") + } + + loadedBasePool.mu.Lock() + baseEntries := len(loadedBasePool.entries) + loadedBasePool.mu.Unlock() + loadedTailPool.mu.Lock() + tailStates := len(loadedTailPool.states) + loadedTailPool.mu.Unlock() + require.Zero(t, baseEntries) + require.Zero(t, tailStates) +} + +func TestLoadTailWithReuseReusesUnchangedState(t *testing.T) { + index := "db.store" + loadedTailPool.clear(index) + t.Cleanup(func() { loadedTailPool.clear(index) }) + state := newTailState(7, 11, []*Segment{NewSegment("tail", 11)}, nil) + views, _ := loadedTailPool.installAndAcquire(index, state) + freeSegs(views) + + segs, deletes, maxChunk, err := loadTailWithReuse(nil, TableConfig{DbName: "db", IndexTable: "store"}, 7, 11, nil) + require.NoError(t, err) + require.Len(t, segs, 1) + require.Nil(t, deletes) + require.Equal(t, int64(11), maxChunk) + freeSegs(segs) +} + +func TestTailPoolBoundaryAccountingAndReplacement(t *testing.T) { + require.Zero(t, estimateSegmentBytes(nil)) + seg := &Segment{ + pks: []any{int64(1)}, + pkOffsets: []int32{0}, + pkRaw: []byte("pk"), + docLen: []int32{2}, + includeTypes: []int32{int32(types.T_int64)}, + includeVals: [][]any{{int64(7)}}, + includeRaw: []byte("include"), + includeVarOffsets: []int32{0}, + terms: map[string]*termPostings{ + "term": {docIDs: []int64{1}, tfs: []uint8{1}, positions: [][]int32{{2, 3}}}, + }, + } + require.Positive(t, estimateSegmentBytes(seg)) + owned := *seg + owned.ownedBytes = 64 + require.Greater(t, estimateSegmentBytes(&owned), int64(64)) + require.Equal(t, int64(35), estimateDeleteKeyBytes("abc")) + require.Equal(t, int64(35), estimateDeleteKeyBytes([]byte("abc"))) + require.Equal(t, int64(32), estimateDeleteKeyBytes(7)) + require.Nil(t, cloneDeletes(nil)) + require.Equal(t, map[any]int64{"old": 2, "new": 1}, mergeTailDeletes( + map[any]int64{"old": 1}, map[any]int64{"old": 2, "new": 1})) + views, deletes := acquireTailViews(nil) + require.Nil(t, views) + require.Nil(t, deletes) + + p := &tailPool{states: make(map[string]*tailState)} + expected := newTailState(1, 10, []*Segment{NewSegment("old", 0)}, nil) + _, _, ok := p.acquireViews("missing", expected) + require.False(t, ok) + require.False(t, p.current("missing", expected)) + state := newTailState(1, 11, []*Segment{NewSegment("delta", 0)}, nil) + _, _, ok = p.installDeltaAndAcquire("missing", expected, state) + require.False(t, ok) + retireTailStates([]*tailState{state}) + + views, _ = p.installAndAcquire("db.store", expected) + for _, view := range views { + view.Free() + } + state = p.state("db.store") + require.Same(t, expected, state) + views, deletes, ok = p.acquireViews("db.store", expected) + require.True(t, ok) + require.Nil(t, deletes) + for _, view := range views { + view.Free() + } + require.True(t, p.current("db.store", expected)) + views, _ = p.installAndAcquire("db.store", expected) + for _, view := range views { + view.Free() + } + + replacement := newTailState(1, 12, []*Segment{NewSegment("replacement", 0)}, nil) + views, _ = p.installAndAcquire("db.store", replacement) + for _, view := range views { + view.Free() + } + p.clear("missing") + p.clear("db.store") + p.clearAll() +} + +func TestSearchLifecycleInvalidationAndPrefilterErrors(t *testing.T) { + s := NewFulltext2Search(TableConfig{DbName: "db", IndexTable: "store"}) + s.OnCacheInvalidated("") + s.OnCacheInvalidated(string(LoadMissCDCFlush)) + s.OnCacheInvalidated(string(LoadMissTTLExpired)) + s.OnCacheInvalidated(string(LoadMissGenerationChange)) + s.FinishLoadObservation() + s.SetLoadWaiters(3) + + proc := newSearchProc(t) + loaded := loadedSearch(t) + defer loaded.Destroy() + _, _, err := loaded.Search(proc, Fulltext2Query{ + Pattern: []byte("fox"), + IncludePredsJSON: []byte("{"), + }, vectorindex.RuntimeConfig{Limit: 10}) + require.Error(t, err) + keys, _, err := loaded.Search(proc, Fulltext2Query{ + Pattern: []byte("fox"), + Algo: BM25, + ScoreRange: &ScoreRange{HasMin: true, Min: -1}, + }, vectorindex.RuntimeConfig{Limit: 10}) + require.NoError(t, err) + require.NotEmpty(t, keys) +} + +func TestLoadTraceConcurrentPhaseUpdates(t *testing.T) { + events := make(chan LoadEvent, 1) + restore := setLoadObserver(func(event LoadEvent) { events <- event }) + defer restore() + + trace := newLoadTrace("db.store", LoadMissProcessStart) + var wg sync.WaitGroup + for i := 0; i < 4; i++ { + wg.Add(1) + go func() { + defer wg.Done() + trace.addInternalSQL(time.Microsecond) + trace.addTempWrite(time.Microsecond) + trace.addMmap(time.Microsecond) + trace.addChecksum(time.Microsecond) + trace.addBaseBytes(1) + trace.addTailBytes(1) + }() + } + wg.Wait() + trace.finish(nil, false, 2) + + event := <-events + require.Equal(t, int64(4), event.BaseBytes) + require.Equal(t, int64(4), event.TailBytes) + require.Equal(t, int64(2), event.SingleflightWaiters) + require.NotZero(t, event.InternalSQLTimeMicros) + require.NotZero(t, event.TempFileWriteMicros) + require.NotZero(t, event.MmapMicros) + require.NotZero(t, event.ChecksumMicros) +} + +func TestStorageBudgetAfterAndEmptyFilesize(t *testing.T) { + sp, mp := mockSqlProc(t) + cfg := testStorageCfg() + trace := &loadTrace{} + var seenSQL string + swapRunSql(t, func(_ *sqlexec.SqlProcess, sql string) (executor.Result, error) { + seenSQL = sql + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, 0)}}, nil + }) + require.NoError(t, checkTailLoadBudgetAfter(sp, cfg, 7, trace)) + require.Contains(t, seenSQL, "chunk_id > 7") + + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{Mp: mp, Batches: []*batch.Batch{metaBatch(mp, "sum", 0, 1)}}, nil + }) + _, err := LoadFromStorage(sp, cfg, "empty") + require.ErrorContains(t, err, "empty filesize") +} diff --git a/pkg/fulltext2/search_cache.go b/pkg/fulltext2/search_cache.go index 8bde7017b9aaf..9c1a0bf79565c 100644 --- a/pkg/fulltext2/search_cache.go +++ b/pkg/fulltext2/search_cache.go @@ -16,7 +16,9 @@ package fulltext2 import ( "context" + "errors" "math" + "sync/atomic" "time" "github.com/matrixorigin/matrixone/pkg/common/docfilter" @@ -75,13 +77,21 @@ type Fulltext2Search struct { // MAX(metadata.timestamp) (REBUILD/MERGE), loadedTail = MAX(tag=1 CdcTail chunk_id) (CDC // append). cnUUID/accountID are the durable handles to re-query in the background. // genValid is false when capture failed / no resolver (unit tests) → IsStale is a no-op. - cnUUID string - accountID uint32 - loadedTs int64 - loadedTail int64 - genValid bool + cnUUID string + accountID uint32 + loadedTs int64 + loadedTail int64 + genValid bool + loadWaiters atomic.Int64 + pendingTrace *loadTrace + pendingLoadErr error + pendingCancel bool } +var errLoadGenerationSuperseded = veccache.NewRetryableLoadError( + moerr.NewInvalidStateNoCtx("fulltext2 load superseded by a newer generation"), +) + var _ veccache.VectorIndexSearchIf = (*Fulltext2Search)(nil) // NewFulltext2Search returns an unloaded search handle; the cache calls Load before @@ -94,19 +104,79 @@ func NewFulltext2Search(cfg TableConfig) *Fulltext2Search { // tag=1 CdcTail delta frames (+ delete set), assembled into a queryable Index with // global stats and per-pk liveness. An index created on an empty table has no tag=0 // base, so segs may hold only tail segments (or be empty → a loaded, doc-less index). -func (s *Fulltext2Search) Load(sqlproc *sqlexec.SqlProcess) error { +func (s *Fulltext2Search) Load(sqlproc *sqlexec.SqlProcess) (err error) { + ensureReusableLoadLifecycle() + index := loadReasonKey(s.cfg.DbName, s.cfg.IndexTable) + generation := beginLoadGeneration(index) + defer endLoadGeneration(generation) + reason := LoadMissProcessStart + var reasonGeneration uint64 + if loadObservationEnabled() { + if observed, observedGeneration := peekLoadReason(index); observed != "" { + reason = observed + reasonGeneration = observedGeneration + } + } + trace := newLoadTrace(s.cfg.IndexTable, reason) + canceled := false + defer func() { + if err != nil { + canceled = isLoadCancellationError(err) + loadedBasePool.rollback(generation.owner) + } + // VectorIndexCache finishes the event after it samples the shared + // entry's waiter count, so waiters that arrived during this load are + // included in the diagnostic record. + s.pendingTrace = trace + s.pendingLoadErr = err + s.pendingCancel = canceled + }() // Fail fast on the QUERY path if the bases' per-doc metadata won't fit the heap // budget, rather than OOM-killing the CN (which takes down every query on the node). // This guard is on Load, NOT LoadAllBases, so CompactSegments (MERGE) — the remedy — // stays exempt and can still load the bases to reclaim dead docs. + var budgetStart time.Time + if trace != nil { + budgetStart = time.Now() + } if err := checkBaseLoadBudget(sqlproc, s.cfg); err != nil { + if trace != nil { + trace.addInternalSQL(time.Since(budgetStart)) + } return err } - bases, err := LoadAllBases(sqlproc, s.cfg) + if trace != nil { + trace.addInternalSQL(time.Since(budgetStart)) + } + var baseGeneration, observedTail int64 + var generationReady bool + var generationStart time.Time + if trace != nil { + generationStart = time.Now() + } + baseGeneration, observedTail, err = LoadGeneration(sqlproc, s.cfg) + if trace != nil { + trace.addInternalSQL(time.Since(generationStart)) + } + if err == nil { + generationReady = true + } + bases, err := loadAllBases(sqlproc, s.cfg, trace, generation) if err != nil { return err } - tails, deletes, err := LoadTailSegments(sqlproc, s.cfg) + if !loadGenerationCurrent(generation) { + freeSegs(bases) + return errLoadGenerationSuperseded + } + var tails []*Segment + var deletes map[any]int64 + var appliedTail int64 + if generationReady { + tails, deletes, appliedTail, err = loadTailWithReuseForGeneration(sqlproc, s.cfg, baseGeneration, observedTail, trace, generation) + } else { + tails, deletes, appliedTail, err = loadTailSegmentsAfter(sqlproc, s.cfg, -1, trace) + } if err != nil { freeSegs(bases) // munmap the base segments on a tail-load error (don't leak the mappings) return err @@ -114,6 +184,10 @@ func (s *Fulltext2Search) Load(sqlproc *sqlexec.SqlProcess) error { segs := append(bases, tails...) s.idx = NewIndex(segs, deletes) s.loaded = true + if loadGenerationCurrent(generation) { + consumeLoadReasonIfCurrent(index, reasonGeneration, generation) + } + trace.setGeneration(baseGeneration, appliedTail) // Capture the generation + durable handles for IsStale. Same txn as the load, so the // captured generation matches the loaded snapshot exactly. On any capture failure genValid @@ -121,13 +195,71 @@ func (s *Fulltext2Search) Load(sqlproc *sqlexec.SqlProcess) error { // retry capture) rather than pinning it in cache forever — see IsStale. s.cnUUID = sqlproc.GetService() if acc, e := sqlproc.GetAccountID(); e == nil { - if ts, tail, e2 := LoadGeneration(sqlproc, s.cfg); e2 == nil { - s.accountID, s.loadedTs, s.loadedTail, s.genValid = acc, ts, tail, true + var genStart time.Time + if trace != nil { + genStart = time.Now() + } + ts, _, e2 := LoadGeneration(sqlproc, s.cfg) + if trace != nil { + trace.addInternalSQL(time.Since(genStart)) + } + if e2 == nil { + s.accountID = acc + // Keep the generation actually applied to the assembled Index. If a + // writer committed while loading, IsStale will force a later refresh + // instead of falsely declaring the older snapshot current. + s.loadedTs, s.loadedTail, s.genValid = baseGeneration, appliedTail, true + if !generationReady { + s.loadedTs, s.loadedTail = ts, appliedTail + } + trace.setGeneration(s.loadedTs, s.loadedTail) } } return nil } +func isLoadCancellationError(err error) bool { + return errors.Is(err, context.Canceled) || + errors.Is(err, context.DeadlineExceeded) || + moerr.IsMoErrCode(err, moerr.ErrQueryInterrupted) +} + +// OnCacheInvalidated is called by the generic cache only on invalidation paths; +// it does not run on a warm query. The bounded registry lets the next load +// classify its miss without changing VectorIndexSearchIf. +func (s *Fulltext2Search) OnCacheInvalidated(reason string) { + if reason == "" { + // A reason-less Remove is used by DROP/restore and by generic cache + // callers. No next-load diagnostic event is needed, but reusable state + // for this index must not survive the cache ownership boundary. + clearReusableLoadGeneration(s.cfg) + return + } + invalidateLoadGeneration(s.cfg, LoadMissReason(reason)) +} + +// SetLoadWaiters is an internal cache hook used only to complete load +// observability. It is not part of the public VectorIndexSearchIf contract. +func (s *Fulltext2Search) SetLoadWaiters(n int64) { + s.loadWaiters.Store(n) +} + +// FinishLoadObservation is called by VectorIndexCache after Load returns and +// SetLoadWaiters has sampled the shared entry. It is intentionally outside +// VectorIndexSearchIf so other algorithms keep their existing contract. +func (s *Fulltext2Search) FinishLoadObservation() { + trace := s.pendingTrace + if trace == nil { + return + } + s.pendingTrace = nil + err := s.pendingLoadErr + canceled := s.pendingCancel + s.pendingLoadErr = nil + s.pendingCancel = false + trace.finish(err, canceled, s.loadWaiters.Load()) +} + // IsStale reports whether the underlying index has changed since this entry was loaded, by // comparing the load-time generation to the current one (a REBUILD/MERGE bumps the metadata // timestamp; a CDC append bumps the tag=1 tail chunk_id). Run on the cache housekeeping @@ -374,10 +506,11 @@ func (s *Fulltext2Search) SearchFloat32(proc *sqlexec.SqlProcess, query any, rt return moerr.NewInternalError(proc.GetContext(), "fulltext2 search: SearchFloat32 not supported") } -// Destroy munmaps the loaded index's base segments (their posting blocks + positions -// are views into those mappings), then drops it. The cache holds the write lock -// around this, so no search is in flight. +// Destroy releases this generation's base/tail views. Immutable base mappings +// remain alive in the bounded pool while another generation can reuse them; +// the cache holds the write lock, so no search is in flight on this generation. func (s *Fulltext2Search) Destroy() { + s.FinishLoadObservation() s.idx.Free() s.idx = nil s.loaded = false diff --git a/pkg/fulltext2/search_cache_unit_test.go b/pkg/fulltext2/search_cache_unit_test.go index ad73bcb246447..01b2b1d68cbcd 100644 --- a/pkg/fulltext2/search_cache_unit_test.go +++ b/pkg/fulltext2/search_cache_unit_test.go @@ -15,9 +15,15 @@ package fulltext2 import ( + "context" "math" + "strings" + "sync" + "sync/atomic" "testing" + "time" + "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/mpool" "github.com/matrixorigin/matrixone/pkg/container/batch" "github.com/matrixorigin/matrixone/pkg/container/types" @@ -25,6 +31,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/testutil" "github.com/matrixorigin/matrixone/pkg/util/executor" "github.com/matrixorigin/matrixone/pkg/vectorindex" + veccache "github.com/matrixorigin/matrixone/pkg/vectorindex/cache" "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" "github.com/stretchr/testify/require" ) @@ -61,6 +68,368 @@ func TestFulltext2SearchNewAndUnloaded(t *testing.T) { require.ErrorContains(t, s.SearchFloat32(proc, nil, vectorindex.RuntimeConfig{}, nil, nil), "not supported") } +func TestFulltext2SearchLoadFailurePreservesOtherGeneration(t *testing.T) { + loadedBasePool.clearAll() + loadedTailPool.clearAll() + t.Cleanup(func() { + loadedBasePool.clearAll() + loadedTailPool.clearAll() + }) + + cfg := testStorageCfg() + baseKey := baseKey{index: "db.__store", id: "base-0", checksum: "sum", filesize: 1} + view, err := loadedBasePool.acquire(context.Background(), baseKey, func() (*Segment, error) { + return NewSegment("base-0", 0), nil + }, 0) + require.NoError(t, err) + view.Free() + tailViews, _ := loadedTailPool.installAndAcquire("db.__store", newTailState(1, 1, []*Segment{NewSegment("tail-1", 0)}, nil)) + freeSegs(tailViews) + + sp, mp := mockSqlProc(t) + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, int64(1)<<50)}}, nil + }) + s := NewFulltext2Search(cfg) + require.Error(t, s.Load(sp)) + + loadedBasePool.mu.Lock() + baseEntries := len(loadedBasePool.entries) + loadedBasePool.mu.Unlock() + loadedTailPool.mu.Lock() + tailStates := len(loadedTailPool.states) + loadedTailPool.mu.Unlock() + require.Equal(t, 1, baseEntries) + require.Equal(t, 1, tailStates) +} + +func TestFulltext2SearchLoadConsumesInvalidationReasonAfterSuccess(t *testing.T) { + for _, reason := range []LoadMissReason{LoadMissCDCFlush, LoadMissMerge, LoadMissRebuild} { + t.Run(string(reason), func(t *testing.T) { + sp := &sqlexec.SqlProcess{SqlCtx: sqlexec.NewSqlContext(context.Background(), "cn-1", nil, 7, nil)} + mp := mpool.MustNewZero() + cfg := testStorageCfg() + var events []LoadEvent + restore := setLoadObserver(func(event LoadEvent) { events = append(events, event) }) + defer restore() + + pendingLoadReasons.Lock() + oldReasons := pendingLoadReasons.m + pendingLoadReasons.m = make(map[string]pendingLoadReason) + pendingLoadReasons.Unlock() + loadGenerations.Lock() + oldGenerations := loadGenerations.m + loadGenerations.m = make(map[string]loadGenerationState) + loadGenerations.Unlock() + t.Cleanup(func() { + pendingLoadReasons.Lock() + pendingLoadReasons.m = oldReasons + pendingLoadReasons.Unlock() + loadGenerations.Lock() + loadGenerations.m = oldGenerations + loadGenerations.Unlock() + }) + + swapRunSql(t, func(_ *sqlexec.SqlProcess, sql string) (executor.Result, error) { + switch { + case strings.Contains(sql, "CAST(COALESCE"): + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, 0)}}, nil + case strings.Contains(sql, "MAX(timestamp)"): + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, 11)}}, nil + case strings.Contains(sql, "MAX(chunk_id)"): + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, 22)}}, nil + case strings.Contains(sql, "LENGTH("): + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, 0)}}, nil + default: + return executor.Result{Mp: mp}, nil + } + }) + invalidateLoadGeneration(cfg, reason) + + s := NewFulltext2Search(cfg) + require.NoError(t, s.Load(sp)) + s.FinishLoadObservation() + require.Len(t, events, 1) + require.Equal(t, reason, events[0].MissReason) + require.Equal(t, int64(11), events[0].BaseGeneration) + require.Equal(t, int64(22), events[0].TailGeneration) + require.True(t, events[0].LoadSuccess) + gotReason, generation := peekLoadReason(loadReasonKey(cfg.DbName, cfg.IndexTable)) + require.Empty(t, gotReason) + require.Zero(t, generation) + s.Destroy() + }) + } +} + +func TestFulltext2SearchLoadClassifiesQueryInterruptionAsCancel(t *testing.T) { + sp := &sqlexec.SqlProcess{SqlCtx: sqlexec.NewSqlContext(context.Background(), "cn-1", nil, 7, nil)} + var event LoadEvent + restore := setLoadObserver(func(got LoadEvent) { event = got }) + defer restore() + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{}, moerr.NewQueryInterrupted(context.Background()) + }) + + s := NewFulltext2Search(testStorageCfg()) + require.Error(t, s.Load(sp)) + s.FinishLoadObservation() + require.True(t, event.LoadCancel) + require.False(t, event.LoadError) + require.False(t, event.LoadSuccess) +} + +func TestFulltext2SearchLoadRetainsInvalidationReasonAfterFailure(t *testing.T) { + for _, reason := range []LoadMissReason{LoadMissCDCFlush, LoadMissMerge, LoadMissRebuild} { + t.Run(string(reason), func(t *testing.T) { + sp := &sqlexec.SqlProcess{SqlCtx: sqlexec.NewSqlContext(context.Background(), "cn-1", nil, 7, nil)} + cfg := testStorageCfg() + var events []LoadEvent + restore := setLoadObserver(func(got LoadEvent) { events = append(events, got) }) + defer restore() + + pendingLoadReasons.Lock() + oldReasons := pendingLoadReasons.m + pendingLoadReasons.m = make(map[string]pendingLoadReason) + pendingLoadReasons.Unlock() + loadGenerations.Lock() + oldGenerations := loadGenerations.m + loadGenerations.m = make(map[string]loadGenerationState) + loadGenerations.Unlock() + t.Cleanup(func() { + pendingLoadReasons.Lock() + pendingLoadReasons.m = oldReasons + pendingLoadReasons.Unlock() + loadGenerations.Lock() + loadGenerations.m = oldGenerations + loadGenerations.Unlock() + }) + + swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { + return executor.Result{}, moerr.NewQueryInterrupted(context.Background()) + }) + invalidateLoadGeneration(cfg, reason) + + s := NewFulltext2Search(cfg) + require.Error(t, s.Load(sp)) + s.FinishLoadObservation() + require.Error(t, s.Load(sp)) + s.FinishLoadObservation() + + require.Len(t, events, 2) + require.Equal(t, reason, events[0].MissReason) + require.Equal(t, reason, events[1].MissReason) + require.True(t, events[0].LoadCancel) + require.True(t, events[1].LoadCancel) + s.Destroy() + }) + } +} + +func TestFulltext2SearchInvalidationEvictionRecordsOneReason(t *testing.T) { + cleanupObserver := setLoadObserver(func(LoadEvent) {}) + defer cleanupObserver() + + pendingLoadReasons.Lock() + previousReasons := pendingLoadReasons.m + pendingLoadReasons.m = make(map[string]pendingLoadReason) + pendingLoadReasons.Unlock() + t.Cleanup(func() { + pendingLoadReasons.Lock() + pendingLoadReasons.m = previousReasons + pendingLoadReasons.Unlock() + }) + + previousCache := veccache.Cache + veccache.Cache = veccache.NewVectorIndexCache() + t.Cleanup(func() { veccache.Cache = previousCache }) + + cfg := testStorageCfg() + old := NewFulltext2Search(cfg) + old.idx = NewIndex(nil, nil) + old.loaded = true + oldEntry := &veccache.VectorIndexSearch{Algo: old} + oldEntry.Cond = sync.NewCond(oldEntry.Mutex.RLocker()) + veccache.Cache.IndexMap.Store(cfg.IndexTable, oldEntry) + + // Production mutation order: record the reason before evicting the local + // entry. Hold the old entry lock so Remove has deleted the old map entry but + // is blocked before destruction; a replacement can therefore start while + // the old eviction is still in flight. + oldEntry.Mutex.Lock() + old.OnCacheInvalidated(string(LoadMissCDCFlush)) + removed := make(chan struct{}) + go func() { + veccache.Cache.Remove(cfg.IndexTable) + close(removed) + }() + require.Eventually(t, func() bool { + _, loaded := veccache.Cache.IndexMap.Load(cfg.IndexTable) + return !loaded + }, time.Second, time.Millisecond) + replacement := NewFulltext2Search(cfg) + replacement.idx = NewIndex(nil, nil) + replacement.loaded = true + replacementEntry := &veccache.VectorIndexSearch{Algo: replacement} + replacementEntry.Cond = sync.NewCond(replacementEntry.Mutex.RLocker()) + veccache.Cache.IndexMap.Store(cfg.IndexTable, replacementEntry) + + reason, generation := peekLoadReason(loadReasonKey(cfg.DbName, cfg.IndexTable)) + require.Equal(t, LoadMissCDCFlush, reason) + require.NotZero(t, generation) + consumeLoadReason(loadReasonKey(cfg.DbName, cfg.IndexTable), generation) + oldEntry.Mutex.Unlock() + select { + case <-removed: + case <-time.After(time.Second): + t.Fatal("old cache eviction did not finish") + } + reason, generation = peekLoadReason(loadReasonKey(cfg.DbName, cfg.IndexTable)) + require.Empty(t, reason) + require.Zero(t, generation) + veccache.Cache.Remove(cfg.IndexTable) +} + +func TestHouseKeepingPublishesGenerationBeforeReplacementLoad(t *testing.T) { + cleanupObserver := setLoadObserver(func(LoadEvent) {}) + defer cleanupObserver() + + previousCache := veccache.Cache + veccache.Cache = veccache.NewVectorIndexCache() + defer func() { veccache.Cache = previousCache }() + + loadGenerations.Lock() + previousGenerations := loadGenerations.m + loadGenerations.m = make(map[string]loadGenerationState) + loadGenerations.Unlock() + defer func() { + loadGenerations.Lock() + loadGenerations.m = previousGenerations + loadGenerations.Unlock() + }() + + cfg := testStorageCfg() + old := NewFulltext2Search(cfg) + old.idx = NewIndex(nil, nil) + old.loaded = true + oldEntry := &veccache.VectorIndexSearch{Algo: old} + oldEntry.Cond = sync.NewCond(oldEntry.Mutex.RLocker()) + oldEntry.ExpireAt.Store(time.Now().Add(-time.Second).UnixMicro()) + veccache.Cache.IndexMap.Store(cfg.IndexTable, oldEntry) + + oldEntry.Mutex.Lock() + done := make(chan struct{}) + go func() { + veccache.Cache.HouseKeeping() + close(done) + }() + require.Eventually(t, func() bool { + _, loaded := veccache.Cache.IndexMap.Load(cfg.IndexTable) + return !loaded + }, time.Second, time.Millisecond) + reason, observedGeneration := peekLoadReason(loadReasonKey(cfg.DbName, cfg.IndexTable)) + require.Equal(t, LoadMissTTLExpired, reason) + require.NotZero(t, observedGeneration) + consumeLoadReason(loadReasonKey(cfg.DbName, cfg.IndexTable), observedGeneration) + + replacement := NewFulltext2Search(cfg) + replacement.idx = NewIndex(nil, nil) + replacement.loaded = true + replacementEntry := &veccache.VectorIndexSearch{Algo: replacement} + replacementEntry.Cond = sync.NewCond(replacementEntry.Mutex.RLocker()) + veccache.Cache.IndexMap.Store(cfg.IndexTable, replacementEntry) + + loadGen := beginLoadGeneration(loadReasonKey(cfg.DbName, cfg.IndexTable)) + require.True(t, loadGenerationCurrent(loadGen)) + endLoadGeneration(loadGen) + + oldEntry.Mutex.Unlock() + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("housekeeping eviction did not finish") + } + require.True(t, loadGenerationCurrent(loadGen)) + veccache.Cache.Remove(cfg.IndexTable) +} + +func TestFulltext2SupersededLoadIsRetryable(t *testing.T) { + require.True(t, veccache.IsRetryableLoadError(errLoadGenerationSuperseded)) + require.Contains(t, errLoadGenerationSuperseded.Error(), "fulltext2 load superseded by a newer generation") +} + +func TestVectorIndexCacheRetriesSupersededFulltext2Load(t *testing.T) { + for _, tc := range []struct { + name string + call func(*sqlexec.SqlProcess, *Fulltext2Search, TableConfig) error + }{ + { + name: "Search", + call: func(proc *sqlexec.SqlProcess, loader *Fulltext2Search, cfg TableConfig) error { + _, _, err := veccache.Cache.Search(proc, cfg.IndexTable, loader, + Fulltext2Query{Pattern: []byte("x")}, vectorindex.RuntimeConfig{Limit: 1}) + return err + }, + }, + { + name: "SearchInto", + call: func(proc *sqlexec.SqlProcess, loader *Fulltext2Search, cfg TableConfig) error { + return veccache.Cache.SearchInto(proc, cfg.IndexTable, loader, + Fulltext2Query{Pattern: []byte("x")}, vectorindex.RuntimeConfig{Limit: 1}, &vectorindex.SearchOutput{}) + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + previousCache := veccache.Cache + veccache.Cache = veccache.NewVectorIndexCache() + t.Cleanup(func() { veccache.Cache = previousCache }) + + pendingLoadReasons.Lock() + previousReasons := pendingLoadReasons.m + pendingLoadReasons.m = make(map[string]pendingLoadReason) + pendingLoadReasons.Unlock() + t.Cleanup(func() { + pendingLoadReasons.Lock() + pendingLoadReasons.m = previousReasons + pendingLoadReasons.Unlock() + }) + loadGenerations.Lock() + previousGenerations := loadGenerations.m + loadGenerations.m = make(map[string]loadGenerationState) + loadGenerations.Unlock() + t.Cleanup(func() { + loadGenerations.Lock() + loadGenerations.m = previousGenerations + loadGenerations.Unlock() + }) + + cfg := testStorageCfg() + proc, mp := mockSqlProc(t) + var invalidated atomic.Bool + swapRunSql(t, func(_ *sqlexec.SqlProcess, sql string) (executor.Result, error) { + if strings.Contains(sql, "index_id") && strings.Contains(sql, "__meta") { + if invalidated.CompareAndSwap(false, true) { + // This is the production publication-before-remove window: the + // loading cache entry still owns the key while the generation bump + // becomes visible to its Fulltext2Search.Load. + NewFulltext2Search(cfg).OnCacheInvalidated(string(LoadMissCDCFlush)) + } + return executor.Result{Mp: mp}, nil + } + if strings.Contains(sql, "chunk_id, data") { + return executor.Result{Mp: mp}, nil + } + return executor.Result{Mp: mp, Batches: []*batch.Batch{int64Batch(mp, 0)}}, nil + }) + + loader := NewFulltext2Search(cfg) + require.NoError(t, tc.call(proc, loader, cfg)) + require.True(t, invalidated.Load()) + veccache.Cache.Remove(cfg.IndexTable) + }) + } +} + // TestStaleGenSqls pins the cache-freshness generation queries: MAX(timestamp) over the // metadata table (REBUILD/MERGE signal) and MAX(chunk_id) over the tag=1 CdcTail (CDC-append // signal), scoped to (CdcTailId, tag=1) so a base sub-index cannot mask a fresh append. diff --git a/pkg/fulltext2/segment.go b/pkg/fulltext2/segment.go index ce280cf00b118..8754d0f222258 100644 --- a/pkg/fulltext2/segment.go +++ b/pkg/fulltext2/segment.go @@ -395,6 +395,11 @@ type Segment struct { // in-memory (tail) segment, whose bytes are GC-managed Go slices. mmapData []byte mmapPath string + // ownedBytes is the unique serialized archive retained by a loaded segment. + // The tail pool counts this once because pkRaw, FST, ranking, blocks, and + // positions are views into the same archive. + ownedBytes int64 + lease *segmentLease // non-nil for a view into the immutable base pool } // numDocs is the segment's document count, valid for both a build-side segment (== len(pks)) @@ -660,6 +665,20 @@ func cbAppendNull(k *vectorindex.ColumnBuffer, fixedW int, fixed bool) { // reading. The loaded posting blocks (blockData) are views into mmapData, so // munmap reclaims them — there is no off-heap buffer to deallocate. func (s *Segment) Free() { + if s.lease != nil { + lease := s.lease + s.lease = nil + // The view does not own the mmap. Clear its aliases as well so a + // defensive second Free cannot munmap the shared mapping directly. + s.mmapData, s.mmapPath = nil, "" + s.ranking, s.blocks, s.positions = nil, nil, nil + lease.release() + return + } + s.freeOwned() +} + +func (s *Segment) freeOwned() { if s.mmapData != nil { _ = munmap(s.mmapData) s.mmapData = nil @@ -669,6 +688,7 @@ func (s *Segment) Free() { s.mmapPath = "" } s.ranking, s.blocks, s.positions = nil, nil, nil + s.ownedBytes = 0 } // freeSegs frees every segment's off-heap buffers (nil-safe on build-side segs). diff --git a/pkg/fulltext2/serialize.go b/pkg/fulltext2/serialize.go index 3a53015af6525..6ffdaf244bf99 100644 --- a/pkg/fulltext2/serialize.go +++ b/pkg/fulltext2/serialize.go @@ -152,6 +152,7 @@ func (s *Segment) decodeSegment(data []byte) error { return err } s.dict = dict + s.ownedBytes = int64(len(data)) // s.N is set by decodeDocmap (loaded segments have pks==nil, so len(s.pks) is 0). return nil } diff --git a/pkg/fulltext2/storage.go b/pkg/fulltext2/storage.go index 7626f9f8d1583..0f8d6b4109f60 100644 --- a/pkg/fulltext2/storage.go +++ b/pkg/fulltext2/storage.go @@ -22,6 +22,7 @@ import ( "path/filepath" "strings" "sync" + "time" "github.com/google/uuid" "github.com/matrixorigin/matrixone/pkg/catalog" @@ -318,9 +319,66 @@ func checkBaseLoadBudget(sqlproc *sqlexec.SqlProcess, cfg TableConfig) error { // suggests, but MERGE must load the bases to reclaim dead docs. The query path calls // the guard itself (Fulltext2Search.Load); MERGE stays exempt so it can always run. func LoadAllBases(sqlproc *sqlexec.SqlProcess, cfg TableConfig) ([]*Segment, error) { + return loadAllBasesUncached(sqlproc, cfg, nil) +} + +func loadAllBasesUncached(sqlproc *sqlexec.SqlProcess, cfg TableConfig, trace *loadTrace) ([]*Segment, error) { + idSQL := fmt.Sprintf("SELECT %s FROM %s", + catalog.FullText2Index_TblCol_Metadata_Index_Id, sqlquote.QualifiedIdent(cfg.DbName, cfg.MetadataTable)) + var sqlStart time.Time + if trace != nil { + sqlStart = time.Now() + } + res, err := runSql(sqlproc, idSQL) + if trace != nil { + trace.addInternalSQL(time.Since(sqlStart)) + } + if err != nil { + return nil, err + } + var ids []string + for _, bat := range res.Batches { + if bat == nil { + continue + } + for i := 0; i < bat.RowCount(); i++ { + ids = append(ids, bat.Vecs[0].GetStringAt(i)) + } + } + res.Close() + bases := make([]*Segment, 0, len(ids)) + for _, id := range ids { + m, lerr := loadFromStorage(sqlproc, cfg, id, trace) + if lerr != nil { + freeSegs(bases) + return nil, lerr + } + bases = append(bases, m) + } + return bases, nil +} + +func loadAllBases(sqlproc *sqlexec.SqlProcess, cfg TableConfig, trace *loadTrace, generation loadGeneration) ([]*Segment, error) { + indexKey := cfg.DbName + "." + cfg.IndexTable + committed := false + defer func() { + if !committed { + // A later base can fail after earlier segments have been acquired. + // Roll back only claims held by this load; another generation may have + // reused or committed the same immutable base in the meantime. + loadedBasePool.rollback(generation.owner) + } + }() idSQL := fmt.Sprintf("SELECT %s FROM %s", catalog.FullText2Index_TblCol_Metadata_Index_Id, sqlquote.QualifiedIdent(cfg.DbName, cfg.MetadataTable)) + var sqlStart time.Time + if trace != nil { + sqlStart = time.Now() + } res, err := runSql(sqlproc, idSQL) + if trace != nil { + trace.addInternalSQL(time.Since(sqlStart)) + } if err != nil { return nil, err } @@ -336,8 +394,32 @@ func LoadAllBases(sqlproc *sqlexec.SqlProcess, cfg TableConfig) ([]*Segment, err res.Close() bases := make([]*Segment, 0, len(ids)) + used := make(map[baseKey]struct{}, len(ids)) for _, id := range ids { - m, lerr := LoadFromStorage(sqlproc, cfg, id) + var metaStart time.Time + if trace != nil { + metaStart = time.Now() + } + checksum, filesize, recency, found, lerr := readMetadata(sqlproc, cfg, id) + if trace != nil { + trace.addInternalSQL(time.Since(metaStart)) + } + if lerr != nil { + freeSegs(bases) + return nil, lerr + } + if !found { + freeSegs(bases) + return nil, moerr.NewInternalError(sqlproc.GetContext(), fmt.Sprintf("fulltext2 index %s metadata not found", id)) + } + if filesize <= 0 { + freeSegs(bases) + return nil, moerr.NewInternalError(sqlproc.GetContext(), fmt.Sprintf("fulltext2 index %s has empty filesize", id)) + } + key := baseKey{index: indexKey, id: id, checksum: checksum, filesize: filesize} + m, lerr := loadedBasePool.acquireOwned(sqlproc.GetContext(), key, func() (*Segment, error) { + return loadFromStorageMetadata(sqlproc, cfg, id, checksum, filesize, recency, trace) + }, recency, generation.owner) if lerr != nil { // Free the segments already mapped this call before bailing: each owns an // mmap (+ a linked /tmp spill file on the fallback path), so returning without @@ -347,7 +429,19 @@ func LoadAllBases(sqlproc *sqlexec.SqlProcess, cfg TableConfig) ([]*Segment, err return nil, lerr } bases = append(bases, m) + used[key] = struct{}{} + } + if !loadGenerationCurrent(generation) { + loadedBasePool.rollback(generation.owner) + } else { + published := loadedBasePool.commitOwnedIfCurrent(indexKey, used, generation.owner, func() bool { + return loadGenerationCurrent(generation) + }) + if !published { + loadedBasePool.rollback(generation.owner) + } } + committed = true return bases, nil } @@ -355,7 +449,18 @@ func LoadAllBases(sqlproc *sqlexec.SqlProcess, cfg TableConfig) ([]*Segment, err // and deserializes it. Chunks stream by chunk_id offset into a temp file so the // mpool never holds the whole index. func LoadFromStorage(sqlproc *sqlexec.SqlProcess, cfg TableConfig, id string) (*Segment, error) { + return loadFromStorage(sqlproc, cfg, id, nil) +} + +func loadFromStorage(sqlproc *sqlexec.SqlProcess, cfg TableConfig, id string, trace *loadTrace) (*Segment, error) { + var sqlStart time.Time + if trace != nil { + sqlStart = time.Now() + } checksum, filesize, recency, found, err := readMetadata(sqlproc, cfg, id) + if trace != nil { + trace.addInternalSQL(time.Since(sqlStart)) + } if err != nil { return nil, err } @@ -365,6 +470,13 @@ func LoadFromStorage(sqlproc *sqlexec.SqlProcess, cfg TableConfig, id string) (* if filesize <= 0 { return nil, moerr.NewInternalError(sqlproc.GetContext(), fmt.Sprintf("fulltext2 index %s has empty filesize", id)) } + return loadFromStorageMetadata(sqlproc, cfg, id, checksum, filesize, recency, trace) +} + +func loadFromStorageMetadata(sqlproc *sqlexec.SqlProcess, cfg TableConfig, id, checksum string, filesize, recency int64, trace *loadTrace) (*Segment, error) { + if trace != nil { + trace.addBaseBytes(filesize) + } // Materialize the segment on the fast LOCAL (SSD) fileservice so mmap page faults // come off the 2 GB/s mount, not /tmp (128 MB/s on AWS). path=="" means an @@ -384,13 +496,20 @@ func LoadFromStorage(sqlproc *sqlexec.SqlProcess, cfg TableConfig, id string) (* cleanup() return nil, err } - if err = streamChunksToFile(sqlproc, cfg, id, filesize, fp); err != nil { + if err = streamChunksToFile(sqlproc, cfg, id, filesize, fp, trace); err != nil { cleanup() return nil, err } // mmap the file read-only (shared across queries; FST + positions are views into // it, page-cache-backed). The fd is not needed once mapped. + var mmapStart time.Time + if trace != nil { + mmapStart = time.Now() + } data, err := mmapReadOnly(fp) + if trace != nil { + trace.addMmap(time.Since(mmapStart)) + } fp.Close() if err != nil { if path != "" { @@ -399,7 +518,15 @@ func LoadFromStorage(sqlproc *sqlexec.SqlProcess, cfg TableConfig, id string) (* return nil, err } // Checksum the mapped bytes (the anonymous SSD file has no path to CheckSum). - if vectorindex.CheckSumFromBuffer(data) != checksum { + var checksumStart time.Time + if trace != nil { + checksumStart = time.Now() + } + actualChecksum := vectorindex.CheckSumFromBuffer(data) + if trace != nil { + trace.addChecksum(time.Since(checksumStart)) + } + if actualChecksum != checksum { _ = munmap(data) if path != "" { os.Remove(path) @@ -503,11 +630,27 @@ func createLocalTempFile(sqlproc *sqlexec.SqlProcess, name string) (*os.File, st // gates an INCREMENTAL load. The 0.8 headroom absorbs the (small) transient query-mpool // usage the formula omits. func checkTailLoadBudget(sqlproc *sqlexec.SqlProcess, cfg TableConfig) error { - sql := fmt.Sprintf("SELECT COALESCE(SUM(LENGTH(%s)), 0) FROM %s WHERE %s = %s AND %s = %d", - catalog.FullText2Index_TblCol_Storage_Data, sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + return checkTailLoadBudgetAfter(sqlproc, cfg, -1, nil) +} + +func checkTailLoadBudgetAfter(sqlproc *sqlexec.SqlProcess, cfg TableConfig, after int64, trace *loadTrace) error { + where := fmt.Sprintf("%s = %s AND %s = %d", catalog.FullText2Index_TblCol_Storage_Index_Id, sqlquote.String(vectorindex.CdcTailId), catalog.FullText2Index_TblCol_Storage_Tag, int(vectorindex.Tag_CdcEvents)) + if after >= 0 { + where += fmt.Sprintf(" AND %s > %d", catalog.FullText2Index_TblCol_Storage_Chunk_Id, after) + } + sql := fmt.Sprintf("SELECT COALESCE(SUM(LENGTH(%s)), 0) FROM %s WHERE %s", + catalog.FullText2Index_TblCol_Storage_Data, sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + where) + var sqlStart time.Time + if trace != nil { + sqlStart = time.Now() + } res, err := runSql(sqlproc, sql) + if trace != nil { + trace.addInternalSQL(time.Since(sqlStart)) + } if err != nil { return err } @@ -542,42 +685,71 @@ func checkTailLoadBudget(sqlproc *sqlexec.SqlProcess, cfg TableConfig) error { // Recency = the frame's first chunk_id). Delete frames are folded into the pk // tombstone map. Empty tail → (nil, nil, nil). func LoadTailSegments(sqlproc *sqlexec.SqlProcess, cfg TableConfig) ([]*Segment, map[any]int64, error) { + return loadTailSegments(sqlproc, cfg, nil) +} + +func loadTailSegments(sqlproc *sqlexec.SqlProcess, cfg TableConfig, trace *loadTrace) ([]*Segment, map[any]int64, error) { + segs, deletes, _, err := loadTailSegmentsAfter(sqlproc, cfg, -1, trace) + return segs, deletes, err +} + +func loadTailSegmentsAfter(sqlproc *sqlexec.SqlProcess, cfg TableConfig, after int64, trace *loadTrace) ([]*Segment, map[any]int64, int64, error) { // Fail fast if the tail can't fit in the memory budget, rather than letting it // OOM-kill the CN as it decodes into the Go heap. - if err := checkTailLoadBudget(sqlproc, cfg); err != nil { - return nil, nil, err + if err := checkTailLoadBudgetAfter(sqlproc, cfg, after, trace); err != nil { + return nil, nil, 0, err } - sql := fmt.Sprintf("SELECT %s, %s FROM %s WHERE %s = %s AND %s = %d", - catalog.FullText2Index_TblCol_Storage_Chunk_Id, catalog.FullText2Index_TblCol_Storage_Data, - sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + where := fmt.Sprintf("%s = %s AND %s = %d", catalog.FullText2Index_TblCol_Storage_Index_Id, sqlquote.String(vectorindex.CdcTailId), catalog.FullText2Index_TblCol_Storage_Tag, int(vectorindex.Tag_CdcEvents)) + if after >= 0 { + where += fmt.Sprintf(" AND %s > %d", catalog.FullText2Index_TblCol_Storage_Chunk_Id, after) + } + sql := fmt.Sprintf("SELECT %s, %s FROM %s WHERE %s", + catalog.FullText2Index_TblCol_Storage_Chunk_Id, catalog.FullText2Index_TblCol_Storage_Data, + sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), + where) + var sqlStart time.Time + if trace != nil { + sqlStart = time.Now() + } res, err := runSql(sqlproc, sql) + if trace != nil { + trace.addInternalSQL(time.Since(sqlStart)) + } if err != nil { - return nil, nil, err + return nil, nil, 0, err } var chunks []TailChunk + maxChunk := after for _, bat := range res.Batches { if bat == nil { continue } cids := vector.MustFixedColNoTypeCheck[int64](bat.Vecs[0]) for i := 0; i < bat.RowCount(); i++ { - chunks = append(chunks, TailChunk{ChunkId: cids[i], Data: append([]byte(nil), bat.Vecs[1].GetRawBytesAt(i)...)}) + data := bat.Vecs[1].GetRawBytesAt(i) + if trace != nil { + trace.addTailBytes(int64(len(data))) + } + if cids[i] > maxChunk { + maxChunk = cids[i] + } + chunks = append(chunks, TailChunk{ChunkId: cids[i], Data: append([]byte(nil), data...)}) } } res.Close() if len(chunks) == 0 { - return nil, nil, nil + return nil, nil, maxChunk, nil } ordered, err := orderTailChunks(chunks) if err != nil { - return nil, nil, err + return nil, nil, 0, err } frames, err := reassembleFrames(ordered) if err != nil { - return nil, nil, err + return nil, nil, 0, err } var segs []*Segment @@ -585,35 +757,38 @@ func LoadTailSegments(sqlproc *sqlexec.SqlProcess, cfg TableConfig) ([]*Segment, for _, f := range frames { records, _, nInserts, nDeletes, _, uerr := cuvscdc.UnframeCdcChunk(f.Data) if uerr != nil { - return nil, nil, uerr + freeSegs(segs) + return nil, nil, 0, uerr } switch { case nInserts > 0: seg, derr := Deserialize(fmt.Sprintf("tail-%d", f.ChunkId), bytes.NewReader(records)) if derr != nil { - return nil, nil, derr + freeSegs(segs) + return nil, nil, 0, derr } seg.Recency = f.ChunkId segs = append(segs, seg) case nDeletes > 0: recs, derr := DecodeDeleteLog(records) if derr != nil { - return nil, nil, derr + freeSegs(segs) + return nil, nil, 0, derr } deletes = foldDeleteFrame(deletes, recs, f.ChunkId) } } - return segs, deletes, nil + return segs, deletes, maxChunk, nil } // streamChunksToFile streams a tag=0 index's chunk rows, writing each at // chunk_id*MaxChunkSize into fp; the assembled bytes must fill filesize exactly. -func streamChunksToFile(sqlproc *sqlexec.SqlProcess, cfg TableConfig, id string, filesize int64, fp *os.File) error { +func streamChunksToFile(sqlproc *sqlexec.SqlProcess, cfg TableConfig, id string, filesize int64, fp *os.File, trace *loadTrace) error { sql := fmt.Sprintf("SELECT %s, %s FROM %s WHERE %s = %s", catalog.FullText2Index_TblCol_Storage_Chunk_Id, catalog.FullText2Index_TblCol_Storage_Data, sqlquote.QualifiedIdent(cfg.DbName, cfg.IndexTable), catalog.FullText2Index_TblCol_Storage_Index_Id, sqlquote.String(id)) - written, _, err := streamChunkRowsToFile(sqlproc, sql, 0, filesize, fp) + written, _, err := streamChunkRowsToFile(sqlproc, sql, 0, filesize, fp, trace) if err != nil { return err } @@ -627,7 +802,7 @@ func streamChunksToFile(sqlproc *sqlexec.SqlProcess, cfg TableConfig, id string, // streamChunkRowsToFile streams the (chunk_id, data) rows of sql and writes each // at (chunk_id-baseChunk)*MaxChunkSize into fp, bounding the mpool to the stream // buffer. Returns bytes written + chunk-row count. -func streamChunkRowsToFile(sqlproc *sqlexec.SqlProcess, sql string, baseChunk, bound int64, fp *os.File) (written, nchunks int64, err error) { +func streamChunkRowsToFile(sqlproc *sqlexec.SqlProcess, sql string, baseChunk, bound int64, fp *os.File, trace *loadTrace) (written, nchunks int64, err error) { streamCh := make(chan executor.Result, 2) errorCh := make(chan error, 2) ctx, cancel := context.WithCancelCause(sqlproc.GetTopContext()) @@ -640,9 +815,16 @@ func streamChunkRowsToFile(sqlproc *sqlexec.SqlProcess, sql string, baseChunk, b close(streamCh) wg.Done() }() + var sqlStart time.Time + if trace != nil { + sqlStart = time.Now() + } if _, e := runStreamingSql(ctx, sqlproc, sql, streamCh, errorCh); e != nil { errorCh <- e } + if trace != nil { + trace.addInternalSQL(time.Since(sqlStart)) + } }() var loopErr error @@ -678,10 +860,17 @@ func streamChunkRowsToFile(sqlproc *sqlexec.SqlProcess, sql string, baseChunk, b break } seen[cid] = struct{}{} + var writeStart time.Time + if trace != nil { + writeStart = time.Now() + } if _, e := fp.WriteAt(data, off); e != nil { loopErr = e break } + if trace != nil { + trace.addTempWrite(time.Since(writeStart)) + } written += int64(len(data)) nchunks++ } diff --git a/pkg/fulltext2/storage_mock_test.go b/pkg/fulltext2/storage_mock_test.go index 7b89a643dc99e..22d0684b7a0f2 100644 --- a/pkg/fulltext2/storage_mock_test.go +++ b/pkg/fulltext2/storage_mock_test.go @@ -18,6 +18,7 @@ import ( "context" "strings" "testing" + "time" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/mpool" @@ -183,7 +184,8 @@ func TestLoadAllBasesEmpty(t *testing.T) { swapRunSql(t, func(_ *sqlexec.SqlProcess, _ string) (executor.Result, error) { return executor.Result{Mp: mp, Batches: nil}, nil }) - bases, err := LoadAllBases(sp, cfg) + trace := &loadTrace{start: time.Now()} + bases, err := loadAllBasesUncached(sp, cfg, trace) require.NoError(t, err) require.Empty(t, bases) } @@ -200,10 +202,12 @@ func TestLoadTailSegmentsEmpty(t *testing.T) { } return executor.Result{Mp: mp, Batches: nil}, nil }) - segs, deletes, err := LoadTailSegments(sp, cfg) + trace := &loadTrace{start: time.Now()} + segs, deletes, _, err := loadTailSegmentsAfter(sp, cfg, 7, trace) require.NoError(t, err) require.Empty(t, segs) require.Empty(t, deletes) + require.GreaterOrEqual(t, trace.phase.internalSQL, time.Duration(0)) } // TestLoadFromStorageRoundTrip mocks the metadata read + base-chunk stream with a REAL @@ -232,11 +236,14 @@ func TestLoadFromStorageRoundTrip(t *testing.T) { return executor.Result{}, nil }) - loaded, err := LoadFromStorage(sp, cfg, "seg0") + trace := &loadTrace{start: time.Now()} + loaded, err := loadFromStorage(sp, cfg, "seg0", trace) require.NoError(t, err) require.Equal(t, "seg0", loaded.Id) require.Equal(t, int64(5), loaded.Recency) require.Equal(t, seg.N, loaded.N) + require.Equal(t, filesize, trace.event.BaseBytes) + require.Positive(t, trace.phase.tempWrite) loaded.Free() // a checksum mismatch (corrupt stream) is detected and rejected. @@ -309,6 +316,15 @@ func TestCompactSegmentsFoldsTail(t *testing.T) { } }) + trace := &loadTrace{start: time.Now()} + tracedSegs, tracedDeletes, maxChunk, err := loadTailSegmentsAfter(sp, cfg, -1, trace) + require.NoError(t, err) + require.Len(t, tracedSegs, 1) + require.Empty(t, tracedDeletes) + require.Equal(t, chunks[len(chunks)-1].ChunkId, maxChunk) + require.Positive(t, trace.event.TailBytes) + freeSegs(tracedSegs) + nlive, err := CompactSegments(sp, cfg, 0, 0) require.NoError(t, err) require.Equal(t, 2, nlive) // both docs are live → folded into the fresh base @@ -389,7 +405,8 @@ func TestLoadAllBasesFreesOnPartialFailure(t *testing.T) { return executor.Result{}, nil }) - bases, err := LoadAllBases(sp, cfg) + trace := &loadTrace{start: time.Now()} + bases, err := loadAllBasesUncached(sp, cfg, trace) require.Error(t, err, "s1 fails to load") require.Nil(t, bases, "no partial slice is returned (s0 was freed)") } diff --git a/pkg/fulltext2/tail_pool.go b/pkg/fulltext2/tail_pool.go new file mode 100644 index 0000000000000..c7090fc577354 --- /dev/null +++ b/pkg/fulltext2/tail_pool.go @@ -0,0 +1,458 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "sync" + "time" + + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" +) + +const ( + tailPoolMaxEntries = 256 + tailPoolMaxBytes = int64(1 << 30) + tailPoolIdleTTL = 15 * time.Minute +) + +type tailState struct { + baseGeneration int64 + maxChunk int64 + segments []*segmentLease + deletes map[any]int64 + bytes int64 + lastUsed time.Time +} + +type tailPool struct { + mu sync.Mutex + states map[string]*tailState + totalBytes int64 + maxEntries int + maxBytes int64 + idleTTL time.Duration +} + +var loadedTailPool = newTailPool() + +func newTailPool() *tailPool { + return &tailPool{ + states: make(map[string]*tailState), + maxEntries: tailPoolMaxEntries, + maxBytes: tailPoolMaxBytes, + idleTTL: tailPoolIdleTTL, + } +} + +func (p *tailPool) limits() (int, int64, time.Duration) { + maxEntries, maxBytes, idleTTL := p.maxEntries, p.maxBytes, p.idleTTL + if maxEntries <= 0 { + maxEntries = tailPoolMaxEntries + } + if maxBytes <= 0 { + maxBytes = tailPoolMaxBytes + } + if idleTTL <= 0 { + idleTTL = tailPoolIdleTTL + } + return maxEntries, maxBytes, idleTTL +} + +// estimateSegmentBytes is intentionally conservative. Tail segments are +// build-side Go objects, so their exact allocator footprint is runtime-specific; +// this estimate still gives the pool a deterministic byte bound instead of +// treating a state containing millions of postings as one small map entry. +func estimateSegmentBytes(s *Segment) int64 { + if s == nil { + return 0 + } + n := s.ownedBytes + if n == 0 { + n = int64(len(s.pks))*24 + int64(len(s.pkOffsets))*4 + int64(len(s.pkRaw)) + + int64(len(s.docLen))*4 + int64(len(s.includeTypes))*4 + int64(len(s.includeRaw)) + + int64(len(s.includeVarOffsets))*4 + } else { + // pkRaw, includeRaw, the FST, ranking, blocks, and positions are all + // views into ownedBytes and must not be counted again. + n += int64(len(s.pkOffsets))*4 + int64(len(s.docLen))*4 + + int64(len(s.includeTypes))*4 + int64(len(s.includeVarOffsets))*4 + } + for _, values := range s.includeVals { + n += int64(len(values)) * 16 + } + for word, postings := range s.terms { + n += int64(len(word)) + 64 + if postings == nil { + continue + } + n += int64(len(postings.docIDs))*8 + int64(len(postings.tfs))*1 + + int64(len(postings.positions))*24 + for _, positions := range postings.positions { + n += int64(len(positions)) * 4 + } + } + return n +} + +func estimateDeleteKeyBytes(key any) int64 { + const mapEntryOverhead = int64(32) + switch v := key.(type) { + case string: + return mapEntryOverhead + int64(len(v)) + case []byte: + return mapEntryOverhead + int64(len(v)) + default: + return mapEntryOverhead + } +} + +func estimateTailStateBytes(segs []*Segment, deletes map[any]int64) int64 { + n := int64(len(segs)) * 8 + for _, seg := range segs { + n += estimateSegmentBytes(seg) + } + for key := range deletes { + n += estimateDeleteKeyBytes(key) + } + return n +} + +func retireTailStates(states []*tailState) { + for _, state := range states { + for _, lease := range state.segments { + lease.retire() + } + } +} + +func (p *tailPool) evictLocked(now time.Time) (retire []*tailState) { + _, _, idleTTL := p.limits() + remove := func(index string, state *tailState) { + delete(p.states, index) + p.totalBytes -= state.bytes + if p.totalBytes < 0 { + p.totalBytes = 0 + } + retire = append(retire, state) + } + for index, state := range p.states { + if state.lastUsed.IsZero() { + continue + } + if now.Sub(state.lastUsed) >= idleTTL { + remove(index, state) + } + } + maxEntries, maxBytes, _ := p.limits() + for len(p.states) > maxEntries || p.totalBytes > maxBytes { + var oldestIndex string + var oldest *tailState + for index, state := range p.states { + if oldest == nil || state.lastUsed.Before(oldest.lastUsed) { + oldestIndex, oldest = index, state + } + } + if oldest == nil { + break + } + remove(oldestIndex, oldest) + } + return retire +} + +func (p *tailPool) evict(now time.Time) { + p.mu.Lock() + retire := p.evictLocked(now) + p.mu.Unlock() + retireTailStates(retire) +} + +func cloneDeletes(src map[any]int64) map[any]int64 { + if len(src) == 0 { + return nil + } + dst := make(map[any]int64, len(src)) + for k, v := range src { + dst[k] = v + } + return dst +} + +func mergeTailDeletes(base, delta map[any]int64) map[any]int64 { + merged := cloneDeletes(base) + if merged == nil && len(delta) > 0 { + merged = make(map[any]int64, len(delta)) + } + for k, v := range delta { + if cur, ok := merged[k]; !ok || v > cur { + merged[k] = v + } + } + return merged +} + +func (p *tailPool) state(index string) *tailState { + p.mu.Lock() + retire := p.evictLocked(time.Now()) + state := p.states[index] + if state != nil { + state.lastUsed = time.Now() + } + p.mu.Unlock() + retireTailStates(retire) + return state +} + +func (p *tailPool) acquireViews(index string, expected *tailState) ([]*Segment, map[any]int64, bool) { + p.mu.Lock() + retire := p.evictLocked(time.Now()) + if p.states[index] != expected { + p.mu.Unlock() + retireTailStates(retire) + return nil, nil, false + } + expected.lastUsed = time.Now() + segs, deletes := acquireTailViews(expected) + p.mu.Unlock() + retireTailStates(retire) + return segs, deletes, true +} + +func (p *tailPool) current(index string, expected *tailState) bool { + p.mu.Lock() + ok := p.states[index] == expected + if ok { + expected.lastUsed = time.Now() + } + p.mu.Unlock() + return ok +} + +func (p *tailPool) installDeltaAndAcquire(index string, expected, state *tailState) ([]*Segment, map[any]int64, bool) { + return p.installDeltaAndAcquireIfCurrent(index, expected, state, nil) +} + +func (p *tailPool) installDeltaAndAcquireIfCurrent(index string, expected, state *tailState, current func() bool) ([]*Segment, map[any]int64, bool) { + p.mu.Lock() + retire := p.evictLocked(time.Now()) + if current != nil && !current() { + p.mu.Unlock() + retireTailStates(retire) + return nil, nil, false + } + if p.states[index] != expected { + p.mu.Unlock() + retireTailStates(retire) + return nil, nil, false + } + state.lastUsed = time.Now() + p.totalBytes -= expected.bytes + p.states[index] = state + p.totalBytes += state.bytes + segs, deletes := acquireTailViews(state) + retire = append(retire, p.evictLocked(time.Now())...) + p.mu.Unlock() + retireTailStates(retire) + return segs, deletes, true +} + +func (p *tailPool) installAndAcquire(index string, state *tailState) ([]*Segment, map[any]int64) { + segs, deletes, _ := p.installAndAcquireIfCurrent(index, state, nil) + return segs, deletes +} + +func (p *tailPool) installAndAcquireIfCurrent(index string, state *tailState, current func() bool) ([]*Segment, map[any]int64, bool) { + p.mu.Lock() + retire := p.evictLocked(time.Now()) + if current != nil && !current() { + p.mu.Unlock() + retireTailStates(retire) + return nil, nil, false + } + old := p.states[index] + if old != nil { + p.totalBytes -= old.bytes + } + state.lastUsed = time.Now() + p.states[index] = state + p.totalBytes += state.bytes + segs, deletes := acquireTailViews(state) + retire = append(retire, p.evictLocked(time.Now())...) + p.mu.Unlock() + if old == nil || old == state { + retireTailStates(retire) + return segs, deletes, true + } + // Appending a delta transfers old segment leases into the new state. A full + // reset (MERGE/REBUILD or tail regression) has no shared leases to transfer. + if len(state.segments) > 0 && len(old.segments) > 0 && state.segments[0] == old.segments[0] { + retireTailStates(retire) + return segs, deletes, true + } + retire = append(retire, old) + retireTailStates(retire) + return segs, deletes, true +} + +func (p *tailPool) clear(index string) { + p.mu.Lock() + old := p.states[index] + delete(p.states, index) + if old != nil { + p.totalBytes -= old.bytes + if p.totalBytes < 0 { + p.totalBytes = 0 + } + } + p.mu.Unlock() + if old != nil { + retireTailStates([]*tailState{old}) + } +} + +func (p *tailPool) clearAll() { + p.mu.Lock() + states := make([]*tailState, 0, len(p.states)) + for index, state := range p.states { + delete(p.states, index) + states = append(states, state) + } + p.totalBytes = 0 + p.mu.Unlock() + retireTailStates(states) +} + +func acquireTailViews(state *tailState) ([]*Segment, map[any]int64) { + if state == nil { + return nil, nil + } + segs := make([]*Segment, 0, len(state.segments)) + for _, lease := range state.segments { + if view := lease.acquire(0); view != nil { + segs = append(segs, view) + } + } + return segs, cloneDeletes(state.deletes) +} + +func newTailState(baseGeneration, maxChunk int64, segs []*Segment, deletes map[any]int64) *tailState { + leases := make([]*segmentLease, 0, len(segs)) + for _, seg := range segs { + leases = append(leases, newSegmentLease(seg)) + } + state := &tailState{ + baseGeneration: baseGeneration, + maxChunk: maxChunk, + segments: leases, + deletes: cloneDeletes(deletes), + lastUsed: time.Now(), + } + state.bytes = estimateTailStateBytes(segs, state.deletes) + return state +} + +// loadTailWithReuse reuses the prior tail generation when the durable tail +// maximum is unchanged, or reads only chunks after the prior maximum when it +// advanced. A base generation change or tail regression forces a full tail +// reload and retires the old tail leases after active readers release them. +func loadTailWithReuse(sqlproc *sqlexec.SqlProcess, cfg TableConfig, baseGeneration, tailMax int64, trace *loadTrace) ([]*Segment, map[any]int64, int64, error) { + return loadTailWithReuseForGeneration(sqlproc, cfg, baseGeneration, tailMax, trace, loadGeneration{}) +} + +func loadTailWithReuseForGeneration(sqlproc *sqlexec.SqlProcess, cfg TableConfig, baseGeneration, tailMax int64, trace *loadTrace, generation loadGeneration) ([]*Segment, map[any]int64, int64, error) { + index := cfg.DbName + "." + cfg.IndexTable + current := func() bool { + return generation.attempt == 0 || loadGenerationCurrent(generation) + } + old := loadedTailPool.state(index) + if old != nil && old.baseGeneration == baseGeneration && tailMax == old.maxChunk { + segs, deletes, ok := loadedTailPool.acquireViews(index, old) + if ok { + return segs, deletes, old.maxChunk, nil + } + old = loadedTailPool.state(index) + } + + after := int64(-1) + if old != nil && old.baseGeneration == baseGeneration && tailMax > old.maxChunk { + if loadedTailPool.current(index, old) { + after = old.maxChunk + } else { + old = nil + } + } + newSegs, newDeletes, appliedMax, err := loadTailSegmentsAfter(sqlproc, cfg, after, trace) + if err != nil { + if after < 0 { + return nil, nil, 0, err + } + // A delta can begin in the middle of a multi-chunk frame when the + // previous snapshot ended before the frame became visible. Retry the + // complete tail so the frame header/prefix is available; persistent + // corruption still fails on this second pass. + newSegs, newDeletes, appliedMax, err = loadTailSegmentsAfter(sqlproc, cfg, -1, trace) + if err != nil { + return nil, nil, 0, err + } + old = nil + after = -1 + } + if appliedMax < tailMax { + // Keep the durable generation observed by the same load snapshot even + // when that snapshot has no tail rows to materialize. + appliedMax = tailMax + } + state := newTailState(baseGeneration, appliedMax, newSegs, newDeletes) + if after >= 0 && old != nil { + // Transfer old leases into the new state and append only the new frames. + state.segments = append(append([]*segmentLease(nil), old.segments...), state.segments...) + state.deletes = mergeTailDeletes(old.deletes, newDeletes) + state.bytes = estimateTailStateBytes(nil, state.deletes) + for _, lease := range state.segments { + lease.mu.Lock() + template := lease.template + lease.mu.Unlock() + state.bytes += estimateSegmentBytes(template) + } + } + if after >= 0 && old != nil { + segs, deletes, ok := loadedTailPool.installDeltaAndAcquireIfCurrent(index, old, state, current) + if ok { + return segs, deletes, appliedMax, nil + } + // Another invalidation replaced the state while the delta was being + // read. Do not publish a delta-only state without its predecessor. + for _, lease := range state.segments[len(old.segments):] { + lease.retire() + } + if !current() { + return nil, nil, 0, errLoadGenerationSuperseded + } + newSegs, newDeletes, appliedMax, err = loadTailSegmentsAfter(sqlproc, cfg, -1, trace) + if err != nil { + return nil, nil, 0, err + } + if appliedMax < tailMax { + appliedMax = tailMax + } + state = newTailState(baseGeneration, appliedMax, newSegs, newDeletes) + } + segs, deletes, ok := loadedTailPool.installAndAcquireIfCurrent(index, state, current) + if !ok { + retireTailStates([]*tailState{state}) + return nil, nil, 0, errLoadGenerationSuperseded + } + return segs, deletes, appliedMax, nil +} diff --git a/pkg/fulltext2/tail_pool_test.go b/pkg/fulltext2/tail_pool_test.go new file mode 100644 index 0000000000000..329ab21a36ecc --- /dev/null +++ b/pkg/fulltext2/tail_pool_test.go @@ -0,0 +1,124 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fulltext2 + +import ( + "bytes" + "strings" + "testing" + "time" + + "github.com/matrixorigin/matrixone/pkg/container/types" + "github.com/stretchr/testify/require" +) + +func TestTailPoolTransfersOnlyCurrentState(t *testing.T) { + p := &tailPool{states: make(map[string]*tailState)} + first := newTailState(1, 10, []*Segment{NewSegment("tail-10", 0)}, map[any]int64{"old": 10}) + second := newTailState(1, 11, []*Segment{NewSegment("tail-11", 0)}, map[any]int64{"old": 10, "new": 11}) + index := "db.store" + p.mu.Lock() + p.states[index] = first + p.mu.Unlock() + + second.segments = append(append([]*segmentLease(nil), first.segments...), second.segments...) + views, deletes, ok := p.installDeltaAndAcquire(index, first, second) + require.True(t, ok) + require.Len(t, views, 2) + require.Equal(t, int64(10), deletes["old"]) + require.Equal(t, int64(11), deletes["new"]) + for _, view := range views { + view.Free() + } + + p.clear(index) + require.Nil(t, first.segments[0].template) + require.Nil(t, second.segments[1].template) +} + +func TestTailPoolRejectsStaleDeltaInstall(t *testing.T) { + p := &tailPool{states: make(map[string]*tailState)} + index := "db.store" + first := newTailState(1, 10, []*Segment{NewSegment("tail-10", 0)}, nil) + other := newTailState(1, 12, []*Segment{NewSegment("tail-12", 0)}, nil) + state := newTailState(1, 11, []*Segment{NewSegment("tail-11", 0)}, nil) + state.segments = append(append([]*segmentLease(nil), first.segments...), state.segments...) + p.mu.Lock() + p.states[index] = other + p.mu.Unlock() + + views, _, ok := p.installDeltaAndAcquire(index, first, state) + require.False(t, ok) + require.Nil(t, views) + for _, lease := range state.segments[1:] { + lease.retire() + } + p.clear(index) +} + +func TestMergeTailDeletesInitializesInsertOnlyDelta(t *testing.T) { + merged := mergeTailDeletes(nil, map[any]int64{"new": 11}) + require.Equal(t, map[any]int64{"new": int64(11)}, merged) +} + +func TestTailPoolBoundsIndexesAndEvictsIdle(t *testing.T) { + p := &tailPool{ + states: make(map[string]*tailState), + maxEntries: 1, + maxBytes: 1 << 20, + idleTTL: time.Hour, + } + for i, index := range []string{"db.one", "db.two"} { + state := newTailState(1, int64(i), []*Segment{NewSegment(index, 0)}, nil) + views, _ := p.installAndAcquire(index, state) + for _, view := range views { + view.Free() + } + } + require.LessOrEqual(t, len(p.states), 1) + + p.idleTTL = time.Nanosecond + p.evict(time.Now().Add(time.Second)) + require.Empty(t, p.states) + p.clearAll() +} + +func TestTailPoolByteBoundCountsDeserializedArchiveAndDeletePayload(t *testing.T) { + b := NewBuilder("tail", int32(types.T_int64)) + words := make([]string, 100) + for i := range words { + words[i] = "repeated" + } + for doc := int64(0); doc < 100; doc++ { + feed(t, b, doc, words...) + } + seg, err := b.Finish() + require.NoError(t, err) + archive, err := seg.Serialize() + require.NoError(t, err) + loaded, err := Deserialize("tail", bytes.NewReader(archive)) + require.NoError(t, err) + t.Cleanup(func() { + _ = loaded.dict.Close() + loaded.Free() + }) + + require.Equal(t, int64(len(archive)), loaded.ownedBytes) + deleteKey := strings.Repeat("varchar-pk-", 128) + state := newTailState(1, 1, []*Segment{loaded}, map[any]int64{deleteKey: 1}) + require.GreaterOrEqual(t, state.bytes, int64(len(archive))) + require.GreaterOrEqual(t, state.bytes, int64(len(archive)+len(deleteKey)+32)) + state.segments[0].retire() +} diff --git a/pkg/iscp/fulltext2_consumer.go b/pkg/iscp/fulltext2_consumer.go index 7348cc458987b..24907fdc8c2eb 100644 --- a/pkg/iscp/fulltext2_consumer.go +++ b/pkg/iscp/fulltext2_consumer.go @@ -127,6 +127,7 @@ func RunFulltext2(c *IndexConsumer, ctx context.Context, errch chan error, r Dat // a known cache-layer gap deferred to a follow-up PR (see the Decision // block on veccache.VectorIndexCache.Remove). if len(segs) > 0 { + fulltext2.NewFulltext2Search(w.cfg).OnCacheInvalidated(string(fulltext2.LoadMissCDCFlush)) veccache.Cache.Remove(w.cfg.IndexTable) logutil.Debugf("[ftv2-sink] evicted search cache for index=%s", w.cfg.IndexTable) // per-flush: Debug, not Info } diff --git a/pkg/sql/colexec/table_function/fulltext2_compact.go b/pkg/sql/colexec/table_function/fulltext2_compact.go index ceb9e771df735..c2178cd757da4 100644 --- a/pkg/sql/colexec/table_function/fulltext2_compact.go +++ b/pkg/sql/colexec/table_function/fulltext2_compact.go @@ -124,6 +124,7 @@ func (u *fulltext2CompactState) end(tf *TableFunction, proc *process.Process) er // The tag=0 base changed (tail folded into a fresh merged base) — evict any cached // search index so the next query reloads the merged base instead of the stale one // held until the TTL. + fulltext2.NewFulltext2Search(u.tblcfg).OnCacheInvalidated(string(fulltext2.LoadMissMerge)) veccache.Cache.Remove(u.tblcfg.IndexTable) return nil } diff --git a/pkg/sql/colexec/table_function/fulltext2_create.go b/pkg/sql/colexec/table_function/fulltext2_create.go index 7b360fcde9fb4..9d17b2df1a0d6 100644 --- a/pkg/sql/colexec/table_function/fulltext2_create.go +++ b/pkg/sql/colexec/table_function/fulltext2_create.go @@ -359,6 +359,7 @@ func (u *fulltext2CreateState) end(tf *TableFunction, proc *process.Process) err // A fresh tag=0 was written (CREATE build, or a REBUILD reusing this TVF) — evict // any cached search index so the next query reloads the new base(s) instead of the // stale one held until the TTL. Local to this CN's cache. + fulltext2.NewFulltext2Search(u.tblcfg).OnCacheInvalidated(string(fulltext2.LoadMissRebuild)) veccache.Cache.Remove(u.tblcfg.IndexTable) return nil } diff --git a/pkg/vectorindex/cache/cache.go b/pkg/vectorindex/cache/cache.go index 40fd18e966408..84a3f92537e02 100644 --- a/pkg/vectorindex/cache/cache.go +++ b/pkg/vectorindex/cache/cache.go @@ -15,6 +15,7 @@ package cache import ( + "errors" "os" "os/signal" "strings" @@ -61,6 +62,66 @@ var ( Cache *VectorIndexCache = NewVectorIndexCache() ) +type retryableLoadError struct { + cause error +} + +func (e retryableLoadError) Error() string { + return e.cause.Error() +} + +func (e retryableLoadError) Unwrap() error { + return e.cause +} + +// NewRetryableLoadError marks a cache-internal load outcome that can be retried +// after the exact failed entry is destroyed. Ordinary algorithm load errors, +// including moerr.ErrInvalidState, must not use this marker. +func NewRetryableLoadError(err error) error { + if err == nil { + return nil + } + return retryableLoadError{cause: err} +} + +func IsRetryableLoadError(err error) bool { + var marker retryableLoadError + return errors.As(err, &marker) +} + +var lifecycleHooks struct { + sync.RWMutex + hooks []func(shutdown bool) +} + +// RegisterLifecycleHook lets an algorithm-owned pool attach cleanup to the +// cache's housekeeping and process-shutdown lifecycle. Hooks are invoked +// outside the cache map operations, including when the map is empty. +func RegisterLifecycleHook(hook func(shutdown bool)) { + if hook == nil { + return + } + lifecycleHooks.Lock() + lifecycleHooks.hooks = append(lifecycleHooks.hooks, hook) + lifecycleHooks.Unlock() +} + +func runLifecycleHooks(shutdown bool) { + lifecycleHooks.RLock() + hooks := append([]func(bool){}, lifecycleHooks.hooks...) + lifecycleHooks.RUnlock() + for _, hook := range hooks { + func() { + defer func() { + if r := recover(); r != nil { + logutil.Errorf("[veccache] lifecycle hook panicked (shutdown=%v): %v", shutdown, r) + } + }() + hook(shutdown) + }() + } +} + // Various vector index algorithm wants to share with VectorIndexCache need to implement VectorIndexSearchIf interface (see HnswSearch) type VectorIndexSearchIf interface { Search(proc *sqlexec.SqlProcess, query any, rt vectorindex.RuntimeConfig) (keys any, distances []float64, err error) @@ -83,6 +144,21 @@ type VectorIndexSearchIf interface { Destroy() } +// cacheInvalidationAware is an optional lifecycle hook. It is intentionally +// not part of VectorIndexSearchIf so existing vector algorithms keep their +// contract unchanged; FULLTEXT2 uses it to classify the next load miss. +type cacheInvalidationAware interface { + OnCacheInvalidated(reason string) +} + +type loadWaiterAware interface { + SetLoadWaiters(int64) +} + +type loadObservationFinisher interface { + FinishLoadObservation() +} + // StaleChecker is an OPTIONAL capability an algo's search impl may implement (currently // fulltext2). It reports whether the loaded index has fallen behind the persisted index — // e.g. a CDC append / REBUILD applied on ANOTHER CN, which the local process-scoped Remove @@ -109,38 +185,102 @@ type StaleChecker interface { // base VectorIndex Search structure for VectorIndexSearchIf (see HnswSearch) type VectorIndexSearch struct { - Mutex sync.RWMutex - ExpireAt atomic.Int64 - LastUpdate atomic.Int64 - Status atomic.Int32 // 0 - NOT INIT, 1 - LOADED, 2 - marked as outdated, 3 - DESTROYED, 4 or above ERRCODE - Algo VectorIndexSearchIf - Cond *sync.Cond // NOTE: this is RWCond. Wait() will use mutex.RLock() and mutex.RUnlock() - stale atomic.Bool // set by the IsStale freshness check; reclaimed next sweep. Separate from + Mutex sync.RWMutex + ExpireAt atomic.Int64 + LastUpdate atomic.Int64 + Status atomic.Int32 // 0 - NOT INIT, 1 - LOADED, 2 - marked as outdated, 3 - DESTROYED, 4 or above ERRCODE + Algo VectorIndexSearchIf + Cond *sync.Cond // NOTE: this is RWCond. Wait() will use mutex.RLock() and mutex.RUnlock() + loadWaiters atomic.Int64 + ttlMu sync.Mutex // serializes sliding TTL renewal with eviction claims + stale atomic.Bool // set by the IsStale freshness check; reclaimed next sweep. Separate from // ExpireAt so a concurrent Search's extend() (sliding TTL) can't un-mark a stale entry. + evicting atomic.Bool + invalidationOnce sync.Once } func (s *VectorIndexSearch) Destroy() { + s.DestroyWithReason("") +} + +func (s *VectorIndexSearch) notifyCacheInvalidated(reason string) { + if reason == "" { + return + } + s.invalidationOnce.Do(func() { + if aware, ok := s.Algo.(cacheInvalidationAware); ok { + aware.OnCacheInvalidated(reason) + } + }) +} + +func (s *VectorIndexSearch) beginEviction(recheckTTL bool) bool { + s.ttlMu.Lock() + defer s.ttlMu.Unlock() + if recheckTTL { + // Search renews ExpireAt under the same gate. The final TTL check and + // claim are therefore atomic with respect to a sliding renewal. + if !s.stale.Load() && !s.Expired() { + return false + } + } + return s.evicting.CompareAndSwap(false, true) +} + +func (s *VectorIndexSearch) DestroyWithReason(reason string) { s.Mutex.Lock() defer func() { s.Mutex.Unlock() s.Cond.Broadcast() }() + s.notifyCacheInvalidated(reason) s.Algo.Destroy() // destroyed s.Status.Store(STATUS_DESTROYED) } +// destroyFailedLoad releases an entry whose load failed after the caller has +// removed that exact entry from the map. It deliberately skips the optional +// invalidation hook: FULLTEXT2 clears reusable state in Load's error defer, and +// invoking a key-wide hook after a replacement load starts could clear the new +// generation. The lock still serializes destruction with waiters/searchers. +func (s *VectorIndexSearch) destroyFailedLoad() { + s.Mutex.Lock() + defer func() { + s.Mutex.Unlock() + s.Cond.Broadcast() + }() + s.Algo.Destroy() + s.Status.Store(STATUS_DESTROYED) +} + func (s *VectorIndexSearch) Load(sqlproc *sqlexec.SqlProcess) error { s.Mutex.Lock() defer func() { s.Mutex.Unlock() s.Cond.Broadcast() }() + if s.evicting.Load() { + return moerr.NewInvalidStateNoCtx("Index destroyed") + } err := s.Algo.Load(sqlproc) + if aware, ok := s.Algo.(loadWaiterAware); ok { + aware.SetLoadWaiters(s.loadWaiters.Load()) + } + if finisher, ok := s.Algo.(loadObservationFinisher); ok { + finisher.FinishLoadObservation() + } if err != nil { - // load error - s.Status.Store(STATUS_ERROR) + // Superseded loads are retryable for both the initiating caller and + // waiters already blocked on this entry. Publish the destroyed state + // before Broadcast so every waiter takes the retry path. Other load + // errors, including ordinary ErrInvalidState, remain terminal. + if IsRetryableLoadError(err) { + s.Status.Store(STATUS_DESTROYED) + } else { + s.Status.Store(STATUS_ERROR) + } return err } // Loaded @@ -168,6 +308,12 @@ func (s *VectorIndexSearch) markStale() { } func (s *VectorIndexSearch) extend(update bool) { + s.ttlMu.Lock() + defer s.ttlMu.Unlock() + s.extendLocked(update) +} + +func (s *VectorIndexSearch) extendLocked(update bool) { now := time.Now() if update { s.LastUpdate.Store(now.UnixMicro()) @@ -176,12 +322,34 @@ func (s *VectorIndexSearch) extend(update bool) { s.ExpireAt.Store(ts) } +func (s *VectorIndexSearch) extendForSearch() bool { + s.ttlMu.Lock() + defer s.ttlMu.Unlock() + if s.evicting.Load() { + return false + } + s.extendLocked(false) + return true +} + func (s *VectorIndexSearch) Search(sqlproc *sqlexec.SqlProcess, newalgo VectorIndexSearchIf, query any, rt vectorindex.RuntimeConfig) (keys any, distances []float64, err error) { + preloadWaiter := s.Status.Load() == STATUS_NOT_INIT + if preloadWaiter { + s.loadWaiters.Add(1) + } s.Cond.L.Lock() defer s.Cond.L.Unlock() + if preloadWaiter { + s.loadWaiters.Add(-1) + } + if s.evicting.Load() { + return nil, nil, moerr.NewInvalidStateNoCtx("Index destroyed") + } for s.Status.Load() == 0 { + s.loadWaiters.Add(1) s.Cond.Wait() + s.loadWaiters.Add(-1) } // entry may be removed already @@ -198,17 +366,31 @@ func (s *VectorIndexSearch) Search(sqlproc *sqlexec.SqlProcess, newalgo VectorIn // evicts the entry via Cache.Remove), so there is nothing to refresh from newalgo // here. Search is therefore pure-read under the shared read lock — no mutation of the // cached algo, so concurrent searches on one entry cannot race on its config. - s.extend(false) + if !s.extendForSearch() { + return nil, nil, moerr.NewInvalidStateNoCtx("Index destroyed") + } return s.Algo.Search(sqlproc, query, rt) } // SearchInto mirrors Search but routes to the box-free SearchInto (caller-owned out // SearchResult). Same shared-read-lock / status discipline. func (s *VectorIndexSearch) SearchInto(sqlproc *sqlexec.SqlProcess, query any, rt vectorindex.RuntimeConfig, out *vectorindex.SearchOutput) error { + preloadWaiter := s.Status.Load() == STATUS_NOT_INIT + if preloadWaiter { + s.loadWaiters.Add(1) + } s.Cond.L.Lock() defer s.Cond.L.Unlock() + if preloadWaiter { + s.loadWaiters.Add(-1) + } + if s.evicting.Load() { + return moerr.NewInvalidStateNoCtx("Index destroyed") + } for s.Status.Load() == 0 { + s.loadWaiters.Add(1) s.Cond.Wait() + s.loadWaiters.Add(-1) } status := s.Status.Load() if status >= STATUS_DESTROYED { @@ -217,7 +399,9 @@ func (s *VectorIndexSearch) SearchInto(sqlproc *sqlexec.SqlProcess, query any, r } return moerr.NewInternalErrorNoCtx("Load index error") } - s.extend(false) + if !s.extendForSearch() { + return moerr.NewInvalidStateNoCtx("Index destroyed") + } return s.Algo.SearchInto(sqlproc, query, rt, out) } @@ -288,28 +472,66 @@ func (c *VectorIndexCache) Once() { c.once.Do(func() { c.serve() }) } +func (c *VectorIndexCache) evictEntry(key string, expected *VectorIndexSearch, reason string) bool { + value, loaded := c.IndexMap.Load(key) + if !loaded { + return false + } + algo, ok := value.(*VectorIndexSearch) + if !ok || (expected != nil && algo != expected) || !algo.beginEviction(reason == "ttl_expired") { + return false + } + value, loaded = c.IndexMap.Load(key) + if !loaded || value != algo { + return false + } + // Publish the invalidation while the old entry still occupies the key. A + // replacement can only be inserted after CompareAndDelete, so it cannot + // observe a later generation bump from the old entry's blocked destroy. + algo.notifyCacheInvalidated(reason) + if !c.IndexMap.CompareAndDelete(key, algo) { + return false + } + algo.Destroy() + return true +} + +func (c *VectorIndexCache) discardFailedLoad(key string, algo *VectorIndexSearch) { + if c.IndexMap.CompareAndDelete(key, algo) { + algo.destroyFailedLoad() + } +} + // house keeping to check expired keys and delete from cache func (c *VectorIndexCache) HouseKeeping() { - expiredkeys := make([]string, 0, 16) + type expiredEntry struct { + key string + algo *VectorIndexSearch + } + expiredkeys := make([]expiredEntry, 0, 16) c.IndexMap.Range(func(key, value any) bool { algo := value.(*VectorIndexSearch) if algo.Expired() || algo.stale.Load() { - expiredkeys = append(expiredkeys, key.(string)) + expiredkeys = append(expiredkeys, expiredEntry{key: key.(string), algo: algo}) } return true }) - for _, k := range expiredkeys { - value, loaded := c.IndexMap.LoadAndDelete(k) - if loaded { - algo := value.(*VectorIndexSearch) - algo.Destroy() - algo = nil - logutil.Debugf("[veccache] evicted expired/stale index %s from cache", k) + for _, entry := range expiredkeys { + if !entry.algo.Expired() && !entry.algo.stale.Load() { + continue + } + reason := "ttl_expired" + if entry.algo.stale.Load() { + reason = "generation_changed" + } + if c.evictEntry(entry.key, entry.algo, reason) { + logutil.Debugf("[veccache] evicted expired/stale index %s from cache", entry.key) } } + runLifecycleHooks(false) } // checkStale asks every loaded StaleChecker entry whether it is stale and marks the stale ones @@ -364,12 +586,12 @@ func (c *VectorIndexCache) Destroy() { } // remove all keys c.IndexMap.Range(func(key, value any) bool { - c.IndexMap.Delete(key) - algo := value.(*VectorIndexSearch) - algo.Destroy() - algo = nil + if k, ok := key.(string); ok { + c.evictEntry(k, value.(*VectorIndexSearch), "process_shutdown") + } return true }) + runLifecycleHooks(true) } // Get index from cache and return VectorIndexSearchIf interface @@ -382,10 +604,20 @@ func (c *VectorIndexCache) Search(sqlproc *sqlexec.SqlProcess, key string, newal value, loaded := c.IndexMap.LoadOrStore(key, s) algo := value.(*VectorIndexSearch) if !loaded { - // load model from database and if error during loading, remove the entry from gIndexMap + // Remove only this exact failed entry, then destroy it without a + // key-wide invalidation hook; the loader owns reusable-state rollback. err := algo.Load(sqlproc) if err != nil { - c.IndexMap.Delete(key) + if algo.evicting.Load() { + continue + } + if IsRetryableLoadError(err) { + c.discardFailedLoad(key, algo) + continue + } + if c.IndexMap.CompareAndDelete(key, algo) { + algo.destroyFailedLoad() + } return nil, nil, err } } @@ -404,7 +636,7 @@ func (c *VectorIndexCache) Search(sqlproc *sqlexec.SqlProcess, key string, newal // SearchInto is the box-free twin of Search: it fills the caller-owned out SearchResult // (pk/scores/includes as reusable ColumnBuffers) instead of returning boxed []any keys. -// Same LoadOrStore / Load / ErrInvalidState-retry discipline as Search. +// Same LoadOrStore / retryable-load discipline as Search. func (c *VectorIndexCache) SearchInto(sqlproc *sqlexec.SqlProcess, key string, newalgo VectorIndexSearchIf, query any, rt vectorindex.RuntimeConfig, out *vectorindex.SearchOutput) error { for { @@ -414,7 +646,16 @@ func (c *VectorIndexCache) SearchInto(sqlproc *sqlexec.SqlProcess, key string, n algo := value.(*VectorIndexSearch) if !loaded { if err := algo.Load(sqlproc); err != nil { - c.IndexMap.Delete(key) + if algo.evicting.Load() { + continue + } + if IsRetryableLoadError(err) { + c.discardFailedLoad(key, algo) + continue + } + if c.IndexMap.CompareAndDelete(key, algo) { + algo.destroyFailedLoad() + } return err } } @@ -436,12 +677,13 @@ func (c *VectorIndexCache) SearchInto(sqlproc *sqlexec.SqlProcess, key string, n // coherence is handled by the pull-based freshness check (StaleChecker/IsStale via // HouseKeeping), which evicts a remote CN's warm-but-stale entry on its own. func (c *VectorIndexCache) Remove(key string) { - value, loaded := c.IndexMap.LoadAndDelete(key) - if loaded { - algo := value.(*VectorIndexSearch) - algo.Destroy() - algo = nil - } + c.RemoveWithReason(key, "") +} + +// RemoveWithReason is the internal reason-aware variant used by FULLTEXT2. +// The empty reason preserves the historical behavior for all other algorithms. +func (c *VectorIndexCache) RemoveWithReason(key, reason string) { + c.evictEntry(key, nil, reason) } // RemovePrefix removes every cached index whose key starts with prefix. diff --git a/pkg/vectorindex/cache/cache_observer_test.go b/pkg/vectorindex/cache/cache_observer_test.go new file mode 100644 index 0000000000000..bb638dbb59ab1 --- /dev/null +++ b/pkg/vectorindex/cache/cache_observer_test.go @@ -0,0 +1,419 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cache + +import ( + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/stretchr/testify/require" +) + +type observerMock struct { + MockSearch + waiters int64 + finished bool + invalidated string + invalidatedCalls int + invalidatedReady chan struct{} + allowInvalidation chan struct{} + destroyStarted chan struct{} +} + +type invalidationMock struct { + MockSearch + reasons []string +} + +func (m *invalidationMock) OnCacheInvalidated(reason string) { + m.reasons = append(m.reasons, reason) +} + +func (m *observerMock) SetLoadWaiters(n int64) { m.waiters = n } +func (m *observerMock) FinishLoadObservation() { m.finished = true } +func (m *observerMock) OnCacheInvalidated(reason string) { + m.invalidated = reason + m.invalidatedCalls++ + if m.invalidatedReady != nil { + close(m.invalidatedReady) + <-m.allowInvalidation + } +} +func (m *observerMock) Destroy() { + if m.destroyStarted != nil { + close(m.destroyStarted) + } +} + +func TestVectorIndexSearchCompletesLoadObserverAfterWaiterSample(t *testing.T) { + mock := &observerMock{} + s := &VectorIndexSearch{Algo: mock} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + s.loadWaiters.Store(5) + require.NoError(t, s.Load(nil)) + require.Equal(t, int64(5), mock.waiters) + require.True(t, mock.finished) + s.Destroy() +} + +func TestVectorIndexSearchDoesNotNotifyEmptyInvalidationReason(t *testing.T) { + mock := &invalidationMock{} + s := &VectorIndexSearch{Algo: mock} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + s.Destroy() + require.Empty(t, mock.reasons) +} + +func TestVectorIndexCacheLifecycleHookRunsForEmptyShutdown(t *testing.T) { + var shutdown atomic.Bool + RegisterLifecycleHook(func(isShutdown bool) { + if isShutdown { + shutdown.Store(true) + } + }) + + c := NewVectorIndexCache() + c.Destroy() + require.True(t, shutdown.Load()) +} + +func TestVectorIndexCacheLifecycleHookPanicDoesNotStopLaterHooks(t *testing.T) { + var laterCalled atomic.Bool + lifecycleHooks.Lock() + previous := lifecycleHooks.hooks + lifecycleHooks.hooks = []func(bool){ + func(bool) { panic("synthetic lifecycle hook panic") }, + func(shutdown bool) { + if shutdown { + laterCalled.Store(true) + } + }, + } + lifecycleHooks.Unlock() + t.Cleanup(func() { + lifecycleHooks.Lock() + lifecycleHooks.hooks = previous + lifecycleHooks.Unlock() + }) + + require.NotPanics(t, func() { NewVectorIndexCache().Destroy() }) + require.True(t, laterCalled.Load()) +} + +type blockingInvalidationMock struct { + invalidationMock + started chan struct{} + release chan struct{} +} + +func (m *blockingInvalidationMock) OnCacheInvalidated(reason string) { + close(m.started) + <-m.release + m.invalidationMock.OnCacheInvalidated(reason) +} + +func TestVectorIndexCacheHouseKeepingPublishesBeforeDelete(t *testing.T) { + mock := &blockingInvalidationMock{ + started: make(chan struct{}), + release: make(chan struct{}), + } + s := &VectorIndexSearch{Algo: mock} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + s.ExpireAt.Store(time.Now().Add(-time.Second).UnixMicro()) + c := NewVectorIndexCache() + c.IndexMap.Store("key", s) + + done := make(chan struct{}) + go func() { + c.HouseKeeping() + close(done) + }() + + <-mock.started + replacement := &VectorIndexSearch{Algo: &MockSearch{}} + replacement.Cond = sync.NewCond(replacement.Mutex.RLocker()) + value, loaded := c.IndexMap.LoadOrStore("key", replacement) + require.True(t, loaded) + require.Same(t, s, value) + + close(mock.release) + <-done + _, loaded = c.IndexMap.Load("key") + require.False(t, loaded) + require.Equal(t, []string{"ttl_expired"}, mock.reasons) +} + +func TestVectorIndexSearchDestroyWithReasonNotifiesOptionalHook(t *testing.T) { + mock := &observerMock{} + s := &VectorIndexSearch{Algo: mock} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + s.DestroyWithReason("cdc_flush") + require.Equal(t, "cdc_flush", mock.invalidated) + + mock = &observerMock{} + s = &VectorIndexSearch{Algo: mock} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + s.Destroy() + require.Empty(t, mock.invalidated) +} + +func TestVectorIndexCacheRemoveDoesNotNotifyOptionalHook(t *testing.T) { + mock := &observerMock{} + s := &VectorIndexSearch{Algo: mock} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + c := NewVectorIndexCache() + c.IndexMap.Store("key", s) + + c.Remove("key") + require.Empty(t, mock.invalidated) +} + +func TestVectorIndexCacheExplicitInvalidationThenRemoveNotifiesOnce(t *testing.T) { + mock := &observerMock{} + s := &VectorIndexSearch{Algo: mock} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + c := NewVectorIndexCache() + c.IndexMap.Store("key", s) + + mock.OnCacheInvalidated("merge") + c.Remove("key") + + require.Equal(t, 1, mock.invalidatedCalls) + require.Equal(t, "merge", mock.invalidated) +} + +func TestVectorIndexCacheHouseKeepingPublishesReasonBeforeRemovingEntry(t *testing.T) { + mock := &observerMock{ + invalidatedReady: make(chan struct{}), + allowInvalidation: make(chan struct{}), + destroyStarted: make(chan struct{}), + } + s := &VectorIndexSearch{Algo: mock} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + s.ExpireAt.Store(time.Now().Add(-time.Second).UnixMicro()) + c := NewVectorIndexCache() + c.IndexMap.Store("key", s) + + s.Mutex.Lock() + mutexHeld := true + releasedInvalidation := false + done := make(chan struct{}) + defer func() { + if !releasedInvalidation { + close(mock.allowInvalidation) + } + if mutexHeld { + s.Mutex.Unlock() + } + select { + case <-done: + case <-time.After(time.Second): + } + }() + go func() { + c.HouseKeeping() + close(done) + }() + + select { + case <-mock.invalidatedReady: + case <-time.After(time.Second): + t.Fatal("housekeeping did not publish the invalidation before waiting on the old entry") + } + value, loaded := c.IndexMap.Load("key") + require.True(t, loaded, "the old entry must still occupy the key while its reason is published") + require.Same(t, s, value) + replacement := &VectorIndexSearch{Algo: &observerMock{}} + replacement.Cond = sync.NewCond(replacement.Mutex.RLocker()) + actual, loaded := c.IndexMap.LoadOrStore("key", replacement) + require.True(t, loaded, "a replacement must not start before the old entry is removed") + require.Same(t, s, actual) + + close(mock.allowInvalidation) + releasedInvalidation = true + require.Eventually(t, func() bool { + _, ok := c.IndexMap.Load("key") + return !ok + }, time.Second, time.Millisecond) + select { + case <-mock.destroyStarted: + t.Fatal("destruction must remain behind the active reader lock") + default: + } + s.Mutex.Unlock() + mutexHeld = false + + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("housekeeping did not finish after the active reader released") + } + require.Equal(t, 1, mock.invalidatedCalls) + require.Equal(t, "ttl_expired", mock.invalidated) +} + +func TestVectorIndexCacheHouseKeepingSkipsReplacedSnapshotEntry(t *testing.T) { + makeExpired := func(mock *observerMock) *VectorIndexSearch { + s := &VectorIndexSearch{Algo: mock} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + s.ExpireAt.Store(time.Now().Add(-time.Second).UnixMicro()) + return s + } + oldA := makeExpired(&observerMock{ + invalidatedReady: make(chan struct{}), + allowInvalidation: make(chan struct{}), + }) + oldB := makeExpired(&observerMock{ + invalidatedReady: make(chan struct{}), + allowInvalidation: make(chan struct{}), + }) + c := NewVectorIndexCache() + c.IndexMap.Store("a", oldA) + c.IndexMap.Store("b", oldB) + + done := make(chan struct{}) + go func() { + c.HouseKeeping() + close(done) + }() + + var capturedKey string + var capturedMock *observerMock + select { + case <-oldA.Algo.(*observerMock).invalidatedReady: + capturedKey = "a" + capturedMock = oldA.Algo.(*observerMock) + c.Remove("b") + case <-oldB.Algo.(*observerMock).invalidatedReady: + capturedKey = "b" + capturedMock = oldB.Algo.(*observerMock) + c.Remove("a") + } + + replacementKey := "a" + if capturedKey == "a" { + replacementKey = "b" + } + replacement := &VectorIndexSearch{Algo: &observerMock{}} + replacement.Cond = sync.NewCond(replacement.Mutex.RLocker()) + replacement.ExpireAt.Store(time.Now().Add(time.Hour).UnixMicro()) + c.IndexMap.Store(replacementKey, replacement) + + close(capturedMock.allowInvalidation) + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("housekeeping did not finish after the captured entry was released") + } + + value, loaded := c.IndexMap.Load(replacementKey) + require.True(t, loaded) + require.Same(t, replacement, value) + _, loaded = c.IndexMap.Load(capturedKey) + require.False(t, loaded, "the captured expired entry should be evicted") +} + +func TestVectorIndexCacheHouseKeepingSkipsConcurrentlyRenewedSnapshotEntry(t *testing.T) { + makeExpiredLoaded := func(mock *observerMock) *VectorIndexSearch { + s := &VectorIndexSearch{Algo: mock} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + s.Status.Store(STATUS_LOADED) + s.ExpireAt.Store(time.Now().Add(-time.Second).UnixMicro()) + return s + } + firstMock := &observerMock{ + invalidatedReady: make(chan struct{}), + allowInvalidation: make(chan struct{}), + } + secondMock := &observerMock{ + invalidatedReady: make(chan struct{}), + allowInvalidation: make(chan struct{}), + } + first := makeExpiredLoaded(firstMock) + second := makeExpiredLoaded(secondMock) + c := NewVectorIndexCache() + c.IndexMap.Store("first", first) + c.IndexMap.Store("second", second) + + done := make(chan struct{}) + go func() { + c.HouseKeeping() + close(done) + }() + + var renewedKey string + select { + case <-firstMock.invalidatedReady: + renewedKey = "second" + case <-secondMock.invalidatedReady: + renewedKey = "first" + case <-time.After(time.Second): + t.Fatal("housekeeping did not start evicting an expired snapshot entry") + } + + _, _, err := c.Search(nil, renewedKey, &MockSearch{}, nil, vectorindex.RuntimeConfig{}) + require.NoError(t, err) + value, loaded := c.IndexMap.Load(renewedKey) + require.True(t, loaded) + renewed := value.(*VectorIndexSearch) + require.False(t, renewed.Expired(), "the concurrent search must renew the snapshot entry") + + close(firstMock.allowInvalidation) + close(secondMock.allowInvalidation) + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("housekeeping did not finish after invalidation was released") + } + + value, loaded = c.IndexMap.Load(renewedKey) + require.True(t, loaded, "a successful concurrent search must prevent snapshot eviction") + require.Same(t, renewed, value) + c.Remove("first") + c.Remove("second") +} + +func TestVectorIndexCacheEvictEntrySkipsRenewedTTLAfterHousekeepingCheck(t *testing.T) { + c := NewVectorIndexCache() + entry := &VectorIndexSearch{Algo: &MockSearch{}} + entry.Cond = sync.NewCond(entry.Mutex.RLocker()) + entry.Status.Store(STATUS_LOADED) + entry.ExpireAt.Store(time.Now().Add(-time.Second).UnixMicro()) + c.IndexMap.Store("renewed", entry) + + // HouseKeeping already observed expiry. A search in the gap must renew the + // entry, and the eviction claim must recheck TTL while excluding renewal. + require.True(t, entry.Expired()) + _, _, err := c.Search(nil, "renewed", &MockSearch{}, nil, vectorindex.RuntimeConfig{}) + require.NoError(t, err) + require.False(t, entry.Expired()) + require.False(t, c.evictEntry("renewed", entry, "ttl_expired")) + value, loaded := c.IndexMap.Load("renewed") + require.True(t, loaded) + require.Same(t, entry, value) + + stale := &VectorIndexSearch{Algo: &MockSearch{}} + stale.Cond = sync.NewCond(stale.Mutex.RLocker()) + stale.Status.Store(STATUS_LOADED) + stale.ExpireAt.Store(time.Now().Add(time.Minute).UnixMicro()) + stale.markStale() + c.IndexMap.Store("stale", stale) + require.True(t, c.evictEntry("stale", stale, "generation_changed")) + + require.True(t, c.evictEntry("renewed", entry, "")) +} diff --git a/pkg/vectorindex/cache/cache_retry_test.go b/pkg/vectorindex/cache/cache_retry_test.go new file mode 100644 index 0000000000000..930183d167565 --- /dev/null +++ b/pkg/vectorindex/cache/cache_retry_test.go @@ -0,0 +1,233 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cache + +import ( + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" + "github.com/stretchr/testify/require" +) + +type retryingLoadSearch struct { + loads atomic.Int32 + destroys atomic.Int32 +} + +type blockedInvalidStateLoadSearch struct { + started chan struct{} + release chan struct{} + destroys atomic.Int32 +} + +type permanentInvalidStateLoadSearch struct { + loads atomic.Int32 + destroys atomic.Int32 +} + +func (m *permanentInvalidStateLoadSearch) Search(*sqlexec.SqlProcess, any, vectorindex.RuntimeConfig) (any, []float64, error) { + return []int64{1}, []float64{1}, nil +} + +func (m *permanentInvalidStateLoadSearch) SearchFloat32(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ []int64, _ []float32) error { + return nil +} + +func (m *permanentInvalidStateLoadSearch) SearchInto(*sqlexec.SqlProcess, any, vectorindex.RuntimeConfig, *vectorindex.SearchOutput) error { + return nil +} + +func (m *permanentInvalidStateLoadSearch) Load(*sqlexec.SqlProcess) error { + if m.loads.Add(1) <= 4 { + return moerr.NewInvalidStateNoCtx("permanent invalid state") + } + return moerr.NewInternalErrorNoCtx("bounded probe terminator") +} + +func (m *permanentInvalidStateLoadSearch) Destroy() { + m.destroys.Add(1) +} + +func (m *blockedInvalidStateLoadSearch) Search(*sqlexec.SqlProcess, any, vectorindex.RuntimeConfig) (any, []float64, error) { + return []int64{1}, []float64{1}, nil +} + +func (m *blockedInvalidStateLoadSearch) SearchFloat32(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, _ []int64, _ []float32) error { + return nil +} + +func (m *blockedInvalidStateLoadSearch) SearchInto(*sqlexec.SqlProcess, any, vectorindex.RuntimeConfig, *vectorindex.SearchOutput) error { + return nil +} + +func (m *blockedInvalidStateLoadSearch) Load(*sqlexec.SqlProcess) error { + close(m.started) + <-m.release + return NewRetryableLoadError(moerr.NewInvalidStateNoCtx("load superseded")) +} + +func (m *blockedInvalidStateLoadSearch) Destroy() { + m.destroys.Add(1) +} + +func (m *retryingLoadSearch) Search(*sqlexec.SqlProcess, any, vectorindex.RuntimeConfig) (any, []float64, error) { + return []int64{1}, []float64{1}, nil +} + +func (m *retryingLoadSearch) SearchFloat32(_ *sqlexec.SqlProcess, _ any, _ vectorindex.RuntimeConfig, outKeys []int64, outDists []float32) error { + outKeys[0] = 1 + outDists[0] = 1 + return nil +} + +func (m *retryingLoadSearch) SearchInto(*sqlexec.SqlProcess, any, vectorindex.RuntimeConfig, *vectorindex.SearchOutput) error { + return nil +} + +func (m *retryingLoadSearch) Load(*sqlexec.SqlProcess) error { + if m.loads.Add(1) == 1 { + return NewRetryableLoadError(moerr.NewInvalidStateNoCtx("load superseded")) + } + return nil +} + +func (m *retryingLoadSearch) Destroy() { + m.destroys.Add(1) +} + +func TestVectorIndexCacheSearchRetriesSupersededLoad(t *testing.T) { + c := NewVectorIndexCache() + algo := &retryingLoadSearch{} + keys, distances, err := c.Search(nil, "key", algo, nil, vectorindex.RuntimeConfig{}) + require.NoError(t, err) + require.Equal(t, []int64{1}, keys) + require.Equal(t, []float64{1}, distances) + require.Equal(t, int32(2), algo.loads.Load()) + require.Equal(t, int32(1), algo.destroys.Load(), "the failed mapped entry must be destroyed before retry") + + c.Remove("key") + require.Equal(t, int32(2), algo.destroys.Load()) +} + +func TestVectorIndexCacheSearchIntoRetriesSupersededLoad(t *testing.T) { + c := NewVectorIndexCache() + algo := &retryingLoadSearch{} + err := c.SearchInto(nil, "key", algo, nil, vectorindex.RuntimeConfig{}, &vectorindex.SearchOutput{}) + require.NoError(t, err) + require.Equal(t, int32(2), algo.loads.Load()) + require.Equal(t, int32(1), algo.destroys.Load(), "the failed mapped entry must be destroyed before retry") + + c.Remove("key") + require.Equal(t, int32(2), algo.destroys.Load()) +} + +func TestVectorIndexSearchWaitersRetrySupersededLoad(t *testing.T) { + tests := []struct { + name string + search func(*VectorIndexSearch) error + }{ + { + name: "Search", + search: func(s *VectorIndexSearch) error { + _, _, err := s.Search(nil, nil, nil, vectorindex.RuntimeConfig{}) + return err + }, + }, + { + name: "SearchInto", + search: func(s *VectorIndexSearch) error { + return s.SearchInto(nil, nil, vectorindex.RuntimeConfig{}, &vectorindex.SearchOutput{}) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + algo := &blockedInvalidStateLoadSearch{ + started: make(chan struct{}), + release: make(chan struct{}), + } + s := &VectorIndexSearch{Algo: algo} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + var releaseOnce sync.Once + release := func() { + releaseOnce.Do(func() { close(algo.release) }) + } + t.Cleanup(release) + + loadErr := make(chan error, 1) + go func() { + loadErr <- s.Load(nil) + }() + <-algo.started + + waiterErr := make(chan error, 1) + go func() { + waiterErr <- tt.search(s) + }() + require.Eventually(t, func() bool { + return s.loadWaiters.Load() > 0 + }, time.Second, time.Millisecond) + + release() + require.Error(t, <-loadErr) + err := <-waiterErr + require.Error(t, err) + require.True(t, moerr.IsMoErrCode(err, moerr.ErrInvalidState), err) + + s.destroyFailedLoad() + require.Equal(t, int32(1), algo.destroys.Load()) + }) + } +} + +func TestVectorIndexCacheReturnsPermanentInvalidStateLoadError(t *testing.T) { + for _, tt := range []struct { + name string + call func(*VectorIndexCache, VectorIndexSearchIf) error + }{ + { + name: "Search", + call: func(c *VectorIndexCache, algo VectorIndexSearchIf) error { + _, _, err := c.Search(nil, "key", algo, nil, vectorindex.RuntimeConfig{}) + return err + }, + }, + { + name: "SearchInto", + call: func(c *VectorIndexCache, algo VectorIndexSearchIf) error { + return c.SearchInto(nil, "key", algo, nil, vectorindex.RuntimeConfig{}, &vectorindex.SearchOutput{}) + }, + }, + } { + t.Run(tt.name, func(t *testing.T) { + c := NewVectorIndexCache() + algo := &permanentInvalidStateLoadSearch{} + + err := tt.call(c, algo) + require.Error(t, err) + require.True(t, moerr.IsMoErrCode(err, moerr.ErrInvalidState), err) + require.Equal(t, int32(1), algo.loads.Load()) + require.Equal(t, int32(1), algo.destroys.Load()) + _, loaded := c.IndexMap.Load("key") + require.False(t, loaded) + }) + } +} diff --git a/pkg/vectorindex/cache/search_into_coverage_test.go b/pkg/vectorindex/cache/search_into_coverage_test.go new file mode 100644 index 0000000000000..2d55841a5c1f9 --- /dev/null +++ b/pkg/vectorindex/cache/search_into_coverage_test.go @@ -0,0 +1,55 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cache + +import ( + "errors" + "sync" + "testing" + + "github.com/matrixorigin/matrixone/pkg/vectorindex" + "github.com/matrixorigin/matrixone/pkg/vectorindex/sqlexec" + "github.com/stretchr/testify/require" +) + +type searchIntoError struct { + MockSearchSearchError +} + +func (*searchIntoError) SearchInto(*sqlexec.SqlProcess, any, vectorindex.RuntimeConfig, *vectorindex.SearchOutput) error { + return errors.New("search into error") +} + +func TestVectorIndexSearchIntoStatusAndDelegation(t *testing.T) { + mock := &MockSearch{} + s := &VectorIndexSearch{Algo: mock} + s.Cond = sync.NewCond(s.Mutex.RLocker()) + require.NoError(t, s.Load(nil)) + require.NoError(t, s.SearchInto(nil, nil, vectorindex.RuntimeConfig{}, &vectorindex.SearchOutput{})) + + s.Status.Store(STATUS_DESTROYED) + require.ErrorContains(t, s.SearchInto(nil, nil, vectorindex.RuntimeConfig{}, &vectorindex.SearchOutput{}), "Index destroyed") + s.Status.Store(STATUS_ERROR) + require.ErrorContains(t, s.SearchInto(nil, nil, vectorindex.RuntimeConfig{}, &vectorindex.SearchOutput{}), "Load index error") + s.Destroy() +} + +func TestVectorIndexCacheSearchIntoSuccessAndError(t *testing.T) { + c := NewVectorIndexCache() + require.NoError(t, c.SearchInto(nil, "ok", &MockSearch{}, nil, vectorindex.RuntimeConfig{}, &vectorindex.SearchOutput{})) + require.NoError(t, c.SearchInto(nil, "ok", &MockSearch{}, nil, vectorindex.RuntimeConfig{}, &vectorindex.SearchOutput{})) + require.Error(t, c.SearchInto(nil, "bad", &searchIntoError{}, nil, vectorindex.RuntimeConfig{}, &vectorindex.SearchOutput{})) + c.Destroy() +} From e3a7440e2ac32bac780669f33c656960e22cff22 Mon Sep 17 00:00:00 2001 From: XuPeng-SH Date: Wed, 26 Aug 2026 19:49:20 +0800 Subject: [PATCH 08/17] test(fulltext2): gate BVT on readiness signals Backport-of: e07a7a792749db4228c1acf96d72827161747e60 --- .../fulltext2/fulltext2_async.result | 64 +++++++++++++-- .../fulltext2/fulltext2_async.sql | 65 +++++++++++++-- .../fulltext2/fulltext2_bugfix.result | 32 ++++++-- .../fulltext2/fulltext2_bugfix.sql | 31 ++++++- .../fulltext2/fulltext2_datalink.result | 46 +++++++++-- .../fulltext2/fulltext2_datalink.sql | 43 ++++++++-- .../fulltext2_datalink_tenant.result | 13 ++- .../fulltext2/fulltext2_datalink_tenant.sql | 12 ++- .../fulltext2/fulltext2_include_async.result | 82 +++++++++++++++++-- .../fulltext2/fulltext2_include_async.sql | 79 ++++++++++++++++-- .../fulltext2_position_free_merge.result | 75 +++++++++++++++-- .../fulltext2_position_free_merge.sql | 69 +++++++++++++++- 12 files changed, 548 insertions(+), 63 deletions(-) diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.result index 6f7677cc7c7f4..81e9a48c53c4d 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.result +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.result @@ -10,9 +10,30 @@ insert into src values (0, 'color is red', 't1'), (1, 'car is yellow', 'crazy ca (10, NULL, NULL); create table src2 (id1 varchar, id2 bigint, body char(128), title text, primary key (id1, id2), FULLTEXT2 ftidx(body, title)); insert into src2 values ('id0', 0, 'red', 't1'), ('id1', 1, 'yellow', 't2'), ('id2', 2, 'blue', 't3'), ('id3', 3, 'blue red', 't4'), ('id4', 4, 'bright red null', NULL); -select sleep(30); -sleep(30) -0 +set @src_ft2_index = ( +select index_table_name from mo_catalog.mo_indexes +where name = 'ftidx' and algo = 'fulltext2' and algo_table_type = 'ftv2_index' +and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'src') +limit 1 +); +set @src2_ft2_index = ( +select index_table_name from mo_catalog.mo_indexes +where name = 'ftidx' and algo = 'fulltext2' and algo_table_type = 'ftv2_index' +and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'src2') +limit 1 +); +set @wait_initial_ft2_sql = concat( +'select ', +'(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @src_ft2_index, +'` where index_id = ''cdc_tail'' and tag = 1) as src_ready, ', +'(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @src2_ft2_index, +'` where index_id = ''cdc_tail'' and tag = 1) as src2_ready' +); +prepare wait_initial_ft2 from @wait_initial_ft2_sql; +execute wait_initial_ft2; +➤ src_ready[-7,1,0] ¦ src2_ready[-7,1,0] 𝄀 +1 ¦ 1 +deallocate prepare wait_initial_ft2; show create table src; Table Create Table src CREATE TABLE `src` (\n `id` bigint NOT NULL,\n `body` varchar(65535) DEFAULT NULL,\n `title` text DEFAULT NULL,\n PRIMARY KEY (`id`),\n FULLTEXT2 `ftidx`(`body`,`title`) WITH PARSER ngram\n) @@ -23,11 +44,42 @@ id3 3 show create table src2; Table Create Table src2 CREATE TABLE `src2` (\n `id1` varchar(65535) NOT NULL,\n `id2` bigint NOT NULL,\n `body` char(128) DEFAULT NULL,\n `title` text DEFAULT NULL,\n PRIMARY KEY (`id1`,`id2`),\n FULLTEXT2 `ftidx`(`body`,`title`) WITH PARSER ngram\n) +set @capture_src_tail_sql = concat( +'select coalesce(max(chunk_id), -1) into @src_tail_before_mutation from `', database(), '`.`', @src_ft2_index, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_src_tail from @capture_src_tail_sql; +execute capture_src_tail; +deallocate prepare capture_src_tail; update src set body = 'color is green' where id = 0; +set @wait_src_mutation_sql = concat( +'select coalesce(max(chunk_id), -1) > ', @src_tail_before_mutation, +' as src_update_ready from `', database(), '`.`', @src_ft2_index, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_src_mutation from @wait_src_mutation_sql; +execute wait_src_mutation; +➤ src_update_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_src_mutation; +set @capture_src_tail_sql = concat( +'select coalesce(max(chunk_id), -1) into @src_tail_before_mutation from `', database(), '`.`', @src_ft2_index, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_src_tail from @capture_src_tail_sql; +execute capture_src_tail; +deallocate prepare capture_src_tail; delete from src where id = 3; -select sleep(30); -sleep(30) -0 +set @wait_src_mutation_sql = concat( +'select coalesce(max(chunk_id), -1) > ', @src_tail_before_mutation, +' as src_delete_ready from `', database(), '`.`', @src_ft2_index, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_src_mutation from @wait_src_mutation_sql; +execute wait_src_mutation; +➤ src_delete_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_src_mutation; select id from src where match(body, title) against('red') order by id; id select id from src where match(body, title) against('green') order by id; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.sql index ee733803ffdfa..0a99bfd519013 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.sql +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.sql @@ -1,8 +1,8 @@ -- fulltext2 CDC (always-async) — ported from pessimistic_transaction/fulltext -- (fulltext_async). fulltext2 is AlwaysAsync (no ASYNC keyword): the inline index -- builds an empty tag=0 base at CREATE, then post-create INSERT/UPDATE/DELETE flow --- into the tag=1 CdcTail via ISCP CDC. sleep() waits for CDC to settle, then MATCH --- reflects the mutations. +-- into the tag=1 CdcTail via ISCP CDC. Poll that durable tail before MATCH so the +-- test waits only as long as needed and cannot silently search before CDC settles. -- CREATE FULLTEXT2 INDEX is gated behind experimental_fulltext2_index (default off). set experimental_fulltext2_index = 1; @@ -24,8 +24,32 @@ create table src2 (id1 varchar, id2 bigint, body char(128), title text, primary insert into src2 values ('id0', 0, 'red', 't1'), ('id1', 1, 'yellow', 't2'), ('id2', 2, 'blue', 't3'), ('id3', 3, 'blue red', 't4'), ('id4', 4, 'bright red null', NULL); --- wait for the initial CDC sync of src and src2 -select sleep(30); +-- Wait for the initial CDC sync of src and src2. Do not poll MATCH: an early +-- MATCH can pin a stale per-CN cache. A cdc_tail chunk is the durable readiness +-- signal produced in the same transaction that advances the CDC watermark. +set @src_ft2_index = ( + select index_table_name from mo_catalog.mo_indexes + where name = 'ftidx' and algo = 'fulltext2' and algo_table_type = 'ftv2_index' + and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'src') + limit 1 +); +set @src2_ft2_index = ( + select index_table_name from mo_catalog.mo_indexes + where name = 'ftidx' and algo = 'fulltext2' and algo_table_type = 'ftv2_index' + and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'src2') + limit 1 +); +set @wait_initial_ft2_sql = concat( + 'select ', + '(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @src_ft2_index, + '` where index_id = ''cdc_tail'' and tag = 1) as src_ready, ', + '(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @src2_ft2_index, + '` where index_id = ''cdc_tail'' and tag = 1) as src2_ready' +); +prepare wait_initial_ft2 from @wait_initial_ft2_sql; +-- @wait_expect(2, 120) +execute wait_initial_ft2; +deallocate prepare wait_initial_ft2; -- src's initial-sync state is verified via src2 below (a never-re-mutated table). -- src itself is deliberately NOT searched here: fulltext2's per-CN index cache is @@ -40,9 +64,40 @@ select id1, id2 from src2 where match(body, title) against('red') order by id1; show create table src2; -- post-create UPDATE + DELETE flow through CDC too +set @capture_src_tail_sql = concat( + 'select coalesce(max(chunk_id), -1) into @src_tail_before_mutation from `', database(), '`.`', @src_ft2_index, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_src_tail from @capture_src_tail_sql; +execute capture_src_tail; +deallocate prepare capture_src_tail; update src set body = 'color is green' where id = 0; +set @wait_src_mutation_sql = concat( + 'select coalesce(max(chunk_id), -1) > ', @src_tail_before_mutation, + ' as src_update_ready from `', database(), '`.`', @src_ft2_index, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_src_mutation from @wait_src_mutation_sql; +-- @wait_expect(2, 120) +execute wait_src_mutation; +deallocate prepare wait_src_mutation; +set @capture_src_tail_sql = concat( + 'select coalesce(max(chunk_id), -1) into @src_tail_before_mutation from `', database(), '`.`', @src_ft2_index, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_src_tail from @capture_src_tail_sql; +execute capture_src_tail; +deallocate prepare capture_src_tail; delete from src where id = 3; -select sleep(30); +set @wait_src_mutation_sql = concat( + 'select coalesce(max(chunk_id), -1) > ', @src_tail_before_mutation, + ' as src_delete_ready from `', database(), '`.`', @src_ft2_index, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_src_mutation from @wait_src_mutation_sql; +-- @wait_expect(2, 120) +execute wait_src_mutation; +deallocate prepare wait_src_mutation; -- doc 0's old text (red) is superseded, doc 3 is deleted -> 'red' now empty. -- This is src's FIRST MATCH, so its per-CN cache loads the fresh post-update index. diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.result index 10296ae27452d..e39ffa8421a84 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.result +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.result @@ -14,9 +14,22 @@ insert into j1 values (1,'{"title":"quantum physics","tag":"science"}'), (2,'{"t create table j2 (id bigint primary key, a json, b json); create fulltext2 index ftidx on j2(a,b) with parser json; insert into j2 values (1,'{"x":"hello world"}','{"y":"foo bar"}'), (2,'{"x":"lorem ipsum"}','{"y":"dolor sit"}'); -select sleep(45); -sleep(45) -0 +set @pk_dt_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'pk_dt') limit 1); +set @pk_dec_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'pk_dec') limit 1); +set @j1_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'j1') limit 1); +set @j2_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'j2') limit 1); +set @wait_bugfix_initial_sql = concat( +'select ', +'(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @pk_dt_ft2, '` where index_id = ''cdc_tail'' and tag = 1) as pk_dt_ready, ', +'(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @pk_dec_ft2, '` where index_id = ''cdc_tail'' and tag = 1) as pk_dec_ready, ', +'(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @j1_ft2, '` where index_id = ''cdc_tail'' and tag = 1) as j1_ready, ', +'(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @j2_ft2, '` where index_id = ''cdc_tail'' and tag = 1) as j2_ready' +); +prepare wait_bugfix_initial from @wait_bugfix_initial_sql; +execute wait_bugfix_initial; +➤ pk_dt_ready[-7,1,0] ¦ pk_dec_ready[-7,1,0] ¦ j1_ready[-7,1,0] ¦ j2_ready[-7,1,0] 𝄀 +1 ¦ 1 ¦ 1 ¦ 1 +deallocate prepare wait_bugfix_initial; select id from pk_dt where match(body) against('beta') order by id; id 2020-01-02 03:04:05 @@ -46,9 +59,16 @@ id create table reb (id bigint primary key, body text); create fulltext2 index ftidx on reb(body); insert into reb values (1,'stale zebra'),(2,'stale zebra'); -select sleep(45); -sleep(45) -0 +set @reb_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'reb') limit 1); +set @wait_reb_initial_sql = concat( +'select coalesce(max(chunk_id), -1) >= 0 as reb_ready from `', database(), '`.`', @reb_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_reb_initial from @wait_reb_initial_sql; +execute wait_reb_initial; +➤ reb_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_reb_initial; select count(*) from reb where match(body) against('zebra'); count(*) 2 diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.sql index d064f614521b2..3c773773e350c 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.sql +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_bugfix.sql @@ -8,7 +8,7 @@ -- #6 REBUILD over an emptied table drops the stale tag=0 base — no longer serves -- deleted docs when the rebuild sees zero source rows. -- All four are CDC/async-path bugs, so the index is created on an empty table and the --- rows flow in through ISCP CDC; sleep() waits for CDC to settle. +-- rows flow in through ISCP CDC; durable tail polling waits for the exact work. set experimental_fulltext2_index = 1; drop database if exists ft2_bugfix; create database ft2_bugfix; @@ -34,8 +34,23 @@ create table j2 (id bigint primary key, a json, b json); create fulltext2 index ftidx on j2(a,b) with parser json; insert into j2 values (1,'{"x":"hello world"}','{"y":"foo bar"}'), (2,'{"x":"lorem ipsum"}','{"y":"dolor sit"}'); --- wait for the initial CDC drain of all four tables -select sleep(45); +-- Wait for every table's committed CDC tail. One completed table is not enough: +-- each independent writer must persist a chunk before its semantic checks run. +set @pk_dt_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'pk_dt') limit 1); +set @pk_dec_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'pk_dec') limit 1); +set @j1_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'j1') limit 1); +set @j2_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'j2') limit 1); +set @wait_bugfix_initial_sql = concat( + 'select ', + '(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @pk_dt_ft2, '` where index_id = ''cdc_tail'' and tag = 1) as pk_dt_ready, ', + '(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @pk_dec_ft2, '` where index_id = ''cdc_tail'' and tag = 1) as pk_dec_ready, ', + '(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @j1_ft2, '` where index_id = ''cdc_tail'' and tag = 1) as j1_ready, ', + '(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @j2_ft2, '` where index_id = ''cdc_tail'' and tag = 1) as j2_ready' +); +prepare wait_bugfix_initial from @wait_bugfix_initial_sql; +-- @wait_expect(2, 120) +execute wait_bugfix_initial; +deallocate prepare wait_bugfix_initial; -- #1: both temporal/decimal-PK tables searchable => the consumer did not crash select id from pk_dt where match(body) against('beta') order by id; @@ -55,7 +70,15 @@ select id from j2 where match(a,b) against('dolor') order by id; create table reb (id bigint primary key, body text); create fulltext2 index ftidx on reb(body); insert into reb values (1,'stale zebra'),(2,'stale zebra'); -select sleep(45); +set @reb_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'reb') limit 1); +set @wait_reb_initial_sql = concat( + 'select coalesce(max(chunk_id), -1) >= 0 as reb_ready from `', database(), '`.`', @reb_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_reb_initial from @wait_reb_initial_sql; +-- @wait_expect(2, 120) +execute wait_reb_initial; +deallocate prepare wait_reb_initial; -- both rows searchable before the rebuild select count(*) from reb where match(body) against('zebra'); -- empty the table, then REBUILD (rebuilds the base from the now-empty source) diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.result index 5ce67b63c9b5d..3b23ad2e0a416 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.result +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.result @@ -2,9 +2,16 @@ set experimental_fulltext2_index = 1; create stage ft2stage URL='file://$resources/fulltext/'; create table dl (id bigint primary key, fpath datalink, FULLTEXT2 ftidx(fpath)); insert into dl values (0, 'stage://ft2stage/mo.pdf'), (1, 'stage://ft2stage/chinese.pdf'); -select sleep(30); -➤ sleep(30)[-6,8,0] 𝄀 -0 +set @dl_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'dl') limit 1); +set @wait_dl_initial_sql = concat( +'select coalesce(max(chunk_id), -1) >= 0 as dl_ready from `', database(), '`.`', @dl_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_dl_initial from @wait_dl_initial_sql; +execute wait_dl_initial; +➤ dl_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_dl_initial; select id from dl where match(fpath) against('matrixone'); ➤ id[-5,64,0] 𝄀 0 @@ -17,13 +24,34 @@ select id from dl where match(fpath) against('mo'); ➤ id[-5,64,0] create table dl2 (id bigint primary key, fpath datalink, FULLTEXT2 ftidx(fpath)); insert into dl2 values (0, 'stage://ft2stage/mo.pdf'); -select sleep(30); -➤ sleep(30)[-6,8,0] 𝄀 -0 +set @dl2_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'dl2') limit 1); +set @wait_dl2_initial_sql = concat( +'select coalesce(max(chunk_id), -1) >= 0 as dl2_ready from `', database(), '`.`', @dl2_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_dl2_initial from @wait_dl2_initial_sql; +execute wait_dl2_initial; +➤ dl2_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_dl2_initial; +set @capture_dl2_tail_sql = concat( +'select coalesce(max(chunk_id), -1) into @dl2_tail_before_update from `', database(), '`.`', @dl2_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_dl2_tail from @capture_dl2_tail_sql; +execute capture_dl2_tail; +deallocate prepare capture_dl2_tail; update dl2 set fpath='stage://ft2stage/file-sample_100kB.docx' where id=0; -select sleep(30); -➤ sleep(30)[-6,8,0] 𝄀 -0 +set @wait_dl2_update_sql = concat( +'select coalesce(max(chunk_id), -1) > ', @dl2_tail_before_update, +' as dl2_update_ready from `', database(), '`.`', @dl2_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_dl2_update from @wait_dl2_update_sql; +execute wait_dl2_update; +➤ dl2_update_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_dl2_update; select id from dl2 where match(fpath) against('Nulla facilisi' in natural language mode); ➤ id[-5,64,0] 𝄀 0 diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.sql index ea828e858a314..332fce1c9ca6f 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.sql +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.sql @@ -4,9 +4,9 @@ -- inline index builds an empty tag=0 base at CREATE and the datalink rows below flow through -- ISCP CDC; the fulltext2 CDC writer resolves each datalink via SELECT load_text(...) run by -- the internal SQL executor (which has a full proc: FileService + catalog for stage://). --- sleep() waits for CDC to settle (the per-CN search cache is not cross-invalidated, so a --- too-early MATCH can pin a stale snapshot), then MATCH against the file CONTENT must find --- them. Regression guard for the CDC-vs-build datalink parity fix. +-- durable cdc_tail polling waits for CDC to settle (the per-CN search cache is not +-- cross-invalidated, so a too-early MATCH can pin a stale snapshot), then MATCH against +-- the file CONTENT must find them. Regression guard for CDC-vs-build datalink parity. set experimental_fulltext2_index = 1; create stage ft2stage URL='file://$resources/fulltext/'; @@ -16,7 +16,15 @@ create stage ft2stage URL='file://$resources/fulltext/'; -- chinese.pdf contains '慢慢地'. create table dl (id bigint primary key, fpath datalink, FULLTEXT2 ftidx(fpath)); insert into dl values (0, 'stage://ft2stage/mo.pdf'), (1, 'stage://ft2stage/chinese.pdf'); -select sleep(30); +set @dl_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'dl') limit 1); +set @wait_dl_initial_sql = concat( + 'select coalesce(max(chunk_id), -1) >= 0 as dl_ready from `', database(), '`.`', @dl_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_dl_initial from @wait_dl_initial_sql; +-- @wait_expect(2, 120) +execute wait_dl_initial; +deallocate prepare wait_dl_initial; -- CDC must have indexed the resolved CONTENT, not the URL string. select id from dl where match(fpath) against('matrixone'); @@ -32,9 +40,32 @@ select id from dl where match(fpath) against('mo'); -- new file's terms. create table dl2 (id bigint primary key, fpath datalink, FULLTEXT2 ftidx(fpath)); insert into dl2 values (0, 'stage://ft2stage/mo.pdf'); -select sleep(30); +set @dl2_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'dl2') limit 1); +set @wait_dl2_initial_sql = concat( + 'select coalesce(max(chunk_id), -1) >= 0 as dl2_ready from `', database(), '`.`', @dl2_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_dl2_initial from @wait_dl2_initial_sql; +-- @wait_expect(2, 120) +execute wait_dl2_initial; +deallocate prepare wait_dl2_initial; +set @capture_dl2_tail_sql = concat( + 'select coalesce(max(chunk_id), -1) into @dl2_tail_before_update from `', database(), '`.`', @dl2_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_dl2_tail from @capture_dl2_tail_sql; +execute capture_dl2_tail; +deallocate prepare capture_dl2_tail; update dl2 set fpath='stage://ft2stage/file-sample_100kB.docx' where id=0; -select sleep(30); +set @wait_dl2_update_sql = concat( + 'select coalesce(max(chunk_id), -1) > ', @dl2_tail_before_update, + ' as dl2_update_ready from `', database(), '`.`', @dl2_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_dl2_update from @wait_dl2_update_sql; +-- @wait_expect(2, 120) +execute wait_dl2_update; +deallocate prepare wait_dl2_update; -- id 0's content is now the docx ('Nulla facilisi'); the old mo.pdf 'matrixone' is gone. select id from dl2 where match(fpath) against('Nulla facilisi' in natural language mode); diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.result index 4a8156fe3dc67..5f60c2a8b13fd 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.result +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.result @@ -6,9 +6,16 @@ use ft2db; create stage ft2stage URL='file://$resources/fulltext/'; create table dl (id bigint primary key, fpath datalink, FULLTEXT2 ftidx(fpath)); insert into dl values (0, 'stage://ft2stage/mo.pdf'), (1, 'stage://ft2stage/chinese.pdf'); -select sleep(30); -➤ sleep(30)[-6,8,0] 𝄀 -0 +set @dl_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'dl') limit 1); +set @wait_tenant_dl_sql = concat( +'select coalesce(max(chunk_id), -1) >= 0 as tenant_dl_ready from `', database(), '`.`', @dl_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_tenant_dl from @wait_tenant_dl_sql; +execute wait_tenant_dl; +➤ tenant_dl_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_tenant_dl; select id from dl where match(fpath) against('matrixone'); ➤ id[-5,64,0] 𝄀 0 diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.sql index 996fa5015e36e..ab0acd565795a 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.sql +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink_tenant.sql @@ -17,7 +17,17 @@ use ft2db; create stage ft2stage URL='file://$resources/fulltext/'; create table dl (id bigint primary key, fpath datalink, FULLTEXT2 ftidx(fpath)); insert into dl values (0, 'stage://ft2stage/mo.pdf'), (1, 'stage://ft2stage/chinese.pdf'); -select sleep(30); +-- Poll the tenant-owned hidden tail from the tenant session. This proves that +-- its CDC writer resolved and durably indexed the rows before the first MATCH. +set @dl_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'dl') limit 1); +set @wait_tenant_dl_sql = concat( + 'select coalesce(max(chunk_id), -1) >= 0 as tenant_dl_ready from `', database(), '`.`', @dl_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_tenant_dl from @wait_tenant_dl_sql; +-- @wait_expect(2, 120) +execute wait_tenant_dl; +deallocate prepare wait_tenant_dl; -- CDC must have resolved the datalink CONTENT under this tenant's mo_stages: 'matrixone' -- (mo.pdf), '慢慢地' (chinese.pdf). If the resolver ran under System_Account, the tenant's diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.result index 3910926b90d77..eee3d05be1d05 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.result +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.result @@ -12,9 +12,18 @@ insert into src_upd values (10, 'silver fox hunts', 'active', 100), (11, 'golden fox naps', 'active', 200), (12, 'bronze fox digs', 'archived', 300); -select sleep(30); -➤ sleep(30)[-6,8,0] 𝄀 -0 +set @src_ins_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'src_ins') limit 1); +set @src_upd_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'src_upd') limit 1); +set @wait_include_initial_sql = concat( +'select ', +'(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @src_ins_ft2, '` where index_id = ''cdc_tail'' and tag = 1) as src_ins_ready, ', +'(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @src_upd_ft2, '` where index_id = ''cdc_tail'' and tag = 1) as src_upd_ready' +); +prepare wait_include_initial from @wait_include_initial_sql; +execute wait_include_initial; +➤ src_ins_ready[-7,1,0] ¦ src_upd_ready[-7,1,0] 𝄀 +1 ¦ 1 +deallocate prepare wait_include_initial; select id, status, prio from src_ins where match(body) against('fox') order by id; ➤ id[-5,64,0] ¦ status[12,20,0] ¦ prio[4,32,0] 𝄀 1 ¦ active ¦ 10 𝄀 @@ -28,11 +37,42 @@ select id, prio from src_ins where match(body) against('fox') and prio > 15 orde ➤ id[-5,64,0] ¦ prio[4,32,0] 𝄀 2 ¦ 20 𝄀 3 ¦ 30 +set @capture_src_upd_tail_sql = concat( +'select coalesce(max(chunk_id), -1) into @src_upd_tail_before_mutation from `', database(), '`.`', @src_upd_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_src_upd_tail from @capture_src_upd_tail_sql; +execute capture_src_upd_tail; +deallocate prepare capture_src_upd_tail; update src_upd set status = 'archived', prio = 999 where id = 10; +set @wait_src_upd_mutation_sql = concat( +'select coalesce(max(chunk_id), -1) > ', @src_upd_tail_before_mutation, +' as src_upd_update_ready from `', database(), '`.`', @src_upd_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_src_upd_mutation from @wait_src_upd_mutation_sql; +execute wait_src_upd_mutation; +➤ src_upd_update_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_src_upd_mutation; +set @capture_src_upd_tail_sql = concat( +'select coalesce(max(chunk_id), -1) into @src_upd_tail_before_mutation from `', database(), '`.`', @src_upd_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_src_upd_tail from @capture_src_upd_tail_sql; +execute capture_src_upd_tail; +deallocate prepare capture_src_upd_tail; delete from src_upd where id = 11; -select sleep(30); -➤ sleep(30)[-6,8,0] 𝄀 -0 +set @wait_src_upd_mutation_sql = concat( +'select coalesce(max(chunk_id), -1) > ', @src_upd_tail_before_mutation, +' as src_upd_delete_ready from `', database(), '`.`', @src_upd_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_src_upd_mutation from @wait_src_upd_mutation_sql; +execute wait_src_upd_mutation; +➤ src_upd_delete_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_src_upd_mutation; select id, status, prio from src_upd where match(body) against('fox') order by id; ➤ id[-5,64,0] ¦ status[12,20,0] ¦ prio[4,32,0] 𝄀 10 ¦ archived ¦ 999 𝄀 @@ -51,13 +91,37 @@ FULLTEXT2 ftidx (body) INCLUDE (status, prio)); insert into src_phantom values (20, 'silver fox alpha', 'active', 500), (21, 'golden fox beta', 'active', 600); +set @src_phantom_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'src_phantom') limit 1); +set @wait_phantom_initial_sql = concat( +'select coalesce(max(chunk_id), -1) >= 0 as src_phantom_ready from `', database(), '`.`', @src_phantom_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_phantom_initial from @wait_phantom_initial_sql; +execute wait_phantom_initial; +➤ src_phantom_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_phantom_initial; +set @capture_phantom_tail_sql = concat( +'select coalesce(max(chunk_id), -1) into @phantom_tail_before_mutation from `', database(), '`.`', @src_phantom_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_phantom_tail from @capture_phantom_tail_sql; +execute capture_phantom_tail; +deallocate prepare capture_phantom_tail; begin; update src_phantom set body = 'silver fox gamma', status = 'updated', prio = 999 where id = 20; delete from src_phantom where id = 20; commit; -select sleep(30); -➤ sleep(30)[-6,8,0] 𝄀 -0 +set @wait_phantom_mutation_sql = concat( +'select coalesce(max(chunk_id), -1) > ', @phantom_tail_before_mutation, +' as src_phantom_mutation_ready from `', database(), '`.`', @src_phantom_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_phantom_mutation from @wait_phantom_mutation_sql; +execute wait_phantom_mutation; +➤ src_phantom_mutation_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_phantom_mutation; explain select id, status, prio from src_phantom where match(body) against('fox'); ➤ TP QUERY PLAN[12,0,0] 𝄀 Project 𝄀 diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.sql index b1d314eac78fe..6c83f1c54446b 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.sql +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.sql @@ -1,7 +1,7 @@ -- fulltext2 INCLUDE columns over CDC (always-async) — the actual include values -- (status, prio) must travel through the ISCP CDC tail on INSERT/UPDATE/DELETE, so -- the in-index prefilter + covering projection reflect the mutations. Modeled on --- fulltext2_async: inline FULLTEXT2 is AlwaysAsync, sleep() waits for CDC to settle. +-- fulltext2_async: inline FULLTEXT2 is AlwaysAsync, so durable tail state is polled. -- Cache-safe (per-CN index cache is not cross-invalidated): each table is searched -- only AFTER its final mutation has settled, so its cache loads fresh. set experimental_fulltext2_index = 1; @@ -23,8 +23,19 @@ insert into src_upd values (11, 'golden fox naps', 'active', 200), (12, 'bronze fox digs', 'archived', 300); --- wait for the initial CDC sync -select sleep(30); +-- Wait for both initial CDC tails; the semantic MATCH checks stay single-shot so +-- an early probe cannot pin a stale per-CN search cache. +set @src_ins_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'src_ins') limit 1); +set @src_upd_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'src_upd') limit 1); +set @wait_include_initial_sql = concat( + 'select ', + '(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @src_ins_ft2, '` where index_id = ''cdc_tail'' and tag = 1) as src_ins_ready, ', + '(select coalesce(max(chunk_id), -1) >= 0 from `', database(), '`.`', @src_upd_ft2, '` where index_id = ''cdc_tail'' and tag = 1) as src_upd_ready' +); +prepare wait_include_initial from @wait_include_initial_sql; +-- @wait_expect(2, 120) +execute wait_include_initial; +deallocate prepare wait_include_initial; -- src_ins is never re-mutated: safe to search now (first search loads fresh cache). -- covering projection carries include values; cat(4) has no 'fox'. @@ -34,9 +45,40 @@ select id from src_ins where match(body) against('fox') and status = 'active' or select id, prio from src_ins where match(body) against('fox') and prio > 15 order by id; -- mutate src_upd: UPDATE an include value (LWW) + DELETE, BEFORE its first search. +set @capture_src_upd_tail_sql = concat( + 'select coalesce(max(chunk_id), -1) into @src_upd_tail_before_mutation from `', database(), '`.`', @src_upd_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_src_upd_tail from @capture_src_upd_tail_sql; +execute capture_src_upd_tail; +deallocate prepare capture_src_upd_tail; update src_upd set status = 'archived', prio = 999 where id = 10; +set @wait_src_upd_mutation_sql = concat( + 'select coalesce(max(chunk_id), -1) > ', @src_upd_tail_before_mutation, + ' as src_upd_update_ready from `', database(), '`.`', @src_upd_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_src_upd_mutation from @wait_src_upd_mutation_sql; +-- @wait_expect(2, 120) +execute wait_src_upd_mutation; +deallocate prepare wait_src_upd_mutation; +set @capture_src_upd_tail_sql = concat( + 'select coalesce(max(chunk_id), -1) into @src_upd_tail_before_mutation from `', database(), '`.`', @src_upd_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_src_upd_tail from @capture_src_upd_tail_sql; +execute capture_src_upd_tail; +deallocate prepare capture_src_upd_tail; delete from src_upd where id = 11; -select sleep(30); +set @wait_src_upd_mutation_sql = concat( + 'select coalesce(max(chunk_id), -1) > ', @src_upd_tail_before_mutation, + ' as src_upd_delete_ready from `', database(), '`.`', @src_upd_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_src_upd_mutation from @wait_src_upd_mutation_sql; +-- @wait_expect(2, 120) +execute wait_src_upd_mutation; +deallocate prepare wait_src_upd_mutation; -- src_upd's FIRST search (fresh cache post-mutation): id10 include values updated -- (active/100 -> archived/999), id11 gone, id12 unchanged. @@ -57,14 +99,41 @@ create table src_phantom (id bigint primary key, body varchar(200), status varch insert into src_phantom values (20, 'silver fox alpha', 'active', 500), (21, 'golden fox beta', 'active', 600); +set @src_phantom_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ftidx' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 'src_phantom') limit 1); +set @wait_phantom_initial_sql = concat( + 'select coalesce(max(chunk_id), -1) >= 0 as src_phantom_ready from `', database(), '`.`', @src_phantom_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_phantom_initial from @wait_phantom_initial_sql; +-- @wait_expect(2, 120) +execute wait_phantom_initial; +deallocate prepare wait_phantom_initial; +set @capture_phantom_tail_sql = concat( + 'select coalesce(max(chunk_id), -1) into @phantom_tail_before_mutation from `', database(), '`.`', @src_phantom_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_phantom_tail from @capture_phantom_tail_sql; +execute capture_phantom_tail; +deallocate prepare capture_phantom_tail; -- upsert then delete the SAME pk in one txn: the exact upsert->delete phantom case. begin; update src_phantom set body = 'silver fox gamma', status = 'updated', prio = 999 where id = 20; delete from src_phantom where id = 20; commit; -select sleep(30); +set @wait_phantom_mutation_sql = concat( + 'select coalesce(max(chunk_id), -1) > ', @phantom_tail_before_mutation, + ' as src_phantom_mutation_ready from `', database(), '`.`', @src_phantom_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_phantom_mutation from @wait_phantom_mutation_sql; +-- @wait_expect(2, 120) +execute wait_phantom_mutation; +deallocate prepare wait_phantom_mutation; -- covered fast path (0-JOIN: Project -> Sort(score) -> Table Function, no base Table Scan/Join). +-- Compare every plan row exactly while ignoring only JDBC metadata: the plan +-- column's reported precision can be 0 or -1 without changing the physical path. +-- @metacmp(false) explain select id, status, prio from src_phantom where match(body) against('fox'); -- pk 20 must be GONE (no phantom, no stale 'updated'/999); only pk 21 remains. select id, status, prio from src_phantom where match(body) against('fox') order by match(body) against('fox') desc, id; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.result index 1085cf3072f00..853e99fd3f738 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.result +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.result @@ -5,11 +5,43 @@ use ft2_posfree_cdc; create table t (id bigint primary key, body text); insert into t values (1,'我家有三个人'),(2,'三个人都住在我家'),(3,'我家的花园很漂亮'); create fulltext2 index ft on t(body) with parser gojieba position_free = true; +set @t_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ft' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 't') limit 1); +set @capture_t_tail_sql = concat( +'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_t_tail from @capture_t_tail_sql; +execute capture_t_tail; +deallocate prepare capture_t_tail; insert into t values (4,'教室里有三个人'),(5,'昨天我家有三个人来吃饭'); +set @wait_t_mutation_sql = concat( +'select coalesce(max(chunk_id), -1) > ', @t_tail_before_mutation, +' as t_insert_ready from `', database(), '`.`', @t_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_t_mutation from @wait_t_mutation_sql; +execute wait_t_mutation; +➤ t_insert_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_t_mutation; +set @capture_t_tail_sql = concat( +'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_t_tail from @capture_t_tail_sql; +execute capture_t_tail; +deallocate prepare capture_t_tail; delete from t where id = 3; -select sleep(30); -➤ sleep(30)[-6,8,0] 𝄀 -0 +set @wait_t_mutation_sql = concat( +'select coalesce(max(chunk_id), -1) > ', @t_tail_before_mutation, +' as t_delete_ready from `', database(), '`.`', @t_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_t_mutation from @wait_t_mutation_sql; +execute wait_t_mutation; +➤ t_delete_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_t_mutation; select id from t where match(body) against('我家' in bm25 mode) order by id; ➤ id[-5,64,0] 𝄀 1 𝄀 @@ -29,11 +61,42 @@ select id from t where match(body) against('我家' in bm25 mode) order by id; 5 select id from t where match(body) against('我家' in boolean mode) order by id; invalid input: fulltext2 index "ft" is POSITION_FREE (bag-of-words only): query it with MATCH(...) AGAINST(... IN BM25 MODE); it has no positions for natural-language / boolean phrase matching +set @capture_t_tail_sql = concat( +'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_t_tail from @capture_t_tail_sql; +execute capture_t_tail; +deallocate prepare capture_t_tail; insert into t values (6,'我家门口有三个人'); +set @wait_t_mutation_sql = concat( +'select coalesce(max(chunk_id), -1) > ', @t_tail_before_mutation, +' as t_insert_ready from `', database(), '`.`', @t_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_t_mutation from @wait_t_mutation_sql; +execute wait_t_mutation; +➤ t_insert_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_t_mutation; +set @capture_t_tail_sql = concat( +'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_t_tail from @capture_t_tail_sql; +execute capture_t_tail; +deallocate prepare capture_t_tail; update t set body = '教室里没有人' where id = 4; -select sleep(30); -➤ sleep(30)[-6,8,0] 𝄀 -0 +set @wait_t_mutation_sql = concat( +'select coalesce(max(chunk_id), -1) > ', @t_tail_before_mutation, +' as t_update_ready from `', database(), '`.`', @t_ft2, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_t_mutation from @wait_t_mutation_sql; +execute wait_t_mutation; +➤ t_update_ready[-7,1,0] 𝄀 +1 +deallocate prepare wait_t_mutation; alter table t alter reindex ft fulltext2; select algo_params from mo_catalog.mo_indexes where name = 'ft' and algo_params like '%parser%' limit 1; ➤ algo_params[12,2048,0] 𝄀 diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.sql index c64d0a31f5cd5..42107a5b8e404 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.sql +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.sql @@ -3,7 +3,7 @@ -- (the tag=1 CdcTail is built position-free — slice 2b-tail), a REINDEX MERGE that folds -- the tail into the tag=0 base (reconstructs from tf, no positions — slice 2b-compact), -- and a REINDEX REBUILD from source. IN BM25 MODE reflects the mutations at each step; --- BOOLEAN phrase stays rejected (no positions). sleep() waits for the always-async CDC. +-- BOOLEAN phrase stays rejected (no positions). Durable tail advancement gates CDC checks. set experimental_fulltext2_index = 1; drop database if exists ft2_posfree_cdc; create database ft2_posfree_cdc; @@ -12,11 +12,43 @@ use ft2_posfree_cdc; create table t (id bigint primary key, body text); insert into t values (1,'我家有三个人'),(2,'三个人都住在我家'),(3,'我家的花园很漂亮'); create fulltext2 index ft on t(body) with parser gojieba position_free = true; +set @t_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ft' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 't') limit 1); +set @capture_t_tail_sql = concat( + 'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_t_tail from @capture_t_tail_sql; +execute capture_t_tail; +deallocate prepare capture_t_tail; -- post-create INSERT/DELETE flow through the CDC tail (built position-free) insert into t values (4,'教室里有三个人'),(5,'昨天我家有三个人来吃饭'); +set @wait_t_mutation_sql = concat( + 'select coalesce(max(chunk_id), -1) > ', @t_tail_before_mutation, + ' as t_insert_ready from `', database(), '`.`', @t_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_t_mutation from @wait_t_mutation_sql; +-- @wait_expect(2, 120) +execute wait_t_mutation; +deallocate prepare wait_t_mutation; +set @capture_t_tail_sql = concat( + 'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_t_tail from @capture_t_tail_sql; +execute capture_t_tail; +deallocate prepare capture_t_tail; delete from t where id = 3; -select sleep(30); +set @wait_t_mutation_sql = concat( + 'select coalesce(max(chunk_id), -1) > ', @t_tail_before_mutation, + ' as t_delete_ready from `', database(), '`.`', @t_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_t_mutation from @wait_t_mutation_sql; +-- @wait_expect(2, 120) +execute wait_t_mutation; +deallocate prepare wait_t_mutation; -- IN BM25 MODE reflects the mutations: 我家 now in docs 1,2,5 (doc 3 deleted); 教室 in doc 4 select id from t where match(body) against('我家' in bm25 mode) order by id; @@ -30,9 +62,40 @@ select id from t where match(body) against('我家' in bm25 mode) order by id; select id from t where match(body) against('我家' in boolean mode) order by id; -- more mutations, then REINDEX REBUILD (rebuild the base from source, stays position-free) +set @capture_t_tail_sql = concat( + 'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_t_tail from @capture_t_tail_sql; +execute capture_t_tail; +deallocate prepare capture_t_tail; insert into t values (6,'我家门口有三个人'); +set @wait_t_mutation_sql = concat( + 'select coalesce(max(chunk_id), -1) > ', @t_tail_before_mutation, + ' as t_insert_ready from `', database(), '`.`', @t_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_t_mutation from @wait_t_mutation_sql; +-- @wait_expect(2, 120) +execute wait_t_mutation; +deallocate prepare wait_t_mutation; +set @capture_t_tail_sql = concat( + 'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare capture_t_tail from @capture_t_tail_sql; +execute capture_t_tail; +deallocate prepare capture_t_tail; update t set body = '教室里没有人' where id = 4; -select sleep(30); +set @wait_t_mutation_sql = concat( + 'select coalesce(max(chunk_id), -1) > ', @t_tail_before_mutation, + ' as t_update_ready from `', database(), '`.`', @t_ft2, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_t_mutation from @wait_t_mutation_sql; +-- @wait_expect(2, 120) +execute wait_t_mutation; +deallocate prepare wait_t_mutation; alter table t alter reindex ft fulltext2; select algo_params from mo_catalog.mo_indexes where name = 'ft' and algo_params like '%parser%' limit 1; select id from t where match(body) against('我家' in bm25 mode) order by id; From d1ab100b3b0f4836c1da55aafc81a99651697be9 Mon Sep 17 00:00:00 2001 From: VioletQwQ-0 <1659648118@qq.com> Date: Wed, 26 Aug 2026 19:49:20 +0800 Subject: [PATCH 09/17] docs(fulltext2): record 4.2-dev backport manifest Backport manifest authored for the 4.2-dev port --- docs/fulltext2-backport-4.2dev-manifest.md | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/fulltext2-backport-4.2dev-manifest.md diff --git a/docs/fulltext2-backport-4.2dev-manifest.md b/docs/fulltext2-backport-4.2dev-manifest.md new file mode 100644 index 0000000000000..4701dff584886 --- /dev/null +++ b/docs/fulltext2-backport-4.2dev-manifest.md @@ -0,0 +1,26 @@ +# FULLTEXT2 4.2-dev backport manifest + +This branch is a bounded backport from the refs captured on 2026-08-26. The +source and target refs are intentionally fixed for this run; later commits on +`upstream/main` are not followed. + +| source | scope | local commit | +| --- | --- | --- | +| `1d7b6311ce91df0968da435bb405f3d68137c595` (#25904) | FULLTEXT2 engine, parser/catalog contracts, plugin/CDC, planner/DDL and tests | `8330936547`, `7ce6f41954`, `cb2c363023`, `fb4eb5b499` | +| `ec591dc9d4ec6b3beb81b804dac11aceb9b66441` (#27165) | delayed phrase-position decode after document alignment | `1a8274faf5` | +| `3da66f2ca28d62f5e657c46d90b7cde9475486d0` (#27171), selective FT2 portion | wrapped FT2 score expressions, argument-aware score resolution, AND-reachable score ranges, and conservative float32 bounds; classic/view/vector changes from the source commit are excluded | `64515ad29e` | +| `6dccc954a1a32c12a2114328c752f15cf97ef2ba` (#27461) | cold-load reason/latency observation and cache-load lifecycle | `e3f25f079c` | +| `b8aa3f7f7351aacaaa37da27891d8f8d85e2e44d` (#27462) | immutable base/tail reuse across cache generations | `e3f25f079c` | +| `e07a7a792749db4228c1acf96d72827161747e60` (#27598) | FULLTEXT2 BVT readiness polling in place of fixed sleeps | `30e026c0b2` | + +## Ref closure + +- Source reference: `upstream/main` = `8e616a693458652cc26b3c4be45c6602dff298d4` +- Target reference: `upstream/4.2-dev` = `d2393868a7aaa6343518d80849fe4696aa3577e9` +- Branch: `backport/fulltext2-main-8e616a6-4.2dev-d239386` +- The target branch's existing parser/PERFORM and pipe changes were preserved; + the generated MySQL parser was regenerated from the merged 4.2 grammar. + +The diff does not use classic FULLTEXT as validation evidence. No local image, +direct `4.2-dev` push, Kubernetes mutation, or GitOps file mutation is part of +this backport. From 35f910dd543e1d3a952e4bd1a1b9852558ee2e27 Mon Sep 17 00:00:00 2001 From: VioletQwQ-0 <1659648118@qq.com> Date: Wed, 26 Aug 2026 21:43:01 +0800 Subject: [PATCH 10/17] fix(fulltext2): repair 4.2 backport validation --- pkg/sql/plan/build_ddl.go | 32 +++++++++- pkg/sql/plan/build_ddl_fulltext2_test.go | 61 +++++++++++++++++++ pkg/sql/plan/build_ddl_vector_test.go | 1 + pkg/sql/plan/fulltext2_query_shape_test.go | 9 +++ .../fulltext2_merge_forcesync.result | 2 +- .../fulltext2/fulltext2_showcreate.result | 8 +-- .../fulltext2/fulltext2_async.result | 8 +-- .../fulltext2/fulltext2_async.sql | 8 +-- .../fulltext2/fulltext2_datalink.result | 4 +- .../fulltext2/fulltext2_datalink.sql | 4 +- .../fulltext2/fulltext2_include_async.result | 12 ++-- .../fulltext2/fulltext2_include_async.sql | 12 ++-- .../fulltext2_position_free_merge.result | 16 ++--- .../fulltext2_position_free_merge.sql | 16 ++--- 14 files changed, 146 insertions(+), 47 deletions(-) create mode 100644 pkg/sql/plan/build_ddl_fulltext2_test.go diff --git a/pkg/sql/plan/build_ddl.go b/pkg/sql/plan/build_ddl.go index a354e24ed94e8..7813227db978e 100644 --- a/pkg/sql/plan/build_ddl.go +++ b/pkg/sql/plan/build_ddl.go @@ -2328,6 +2328,30 @@ func buildFullTextIndexTable(createTable *plan.CreateTable, indexInfos []*tree.F if err != nil { return err } + // Capture the plugin's build-time session vars (BuildSessionVars) into each + // index def's algo_params.session_vars so background builds (idxcron reindex, + // ISCP async, clone, and restore) reproduce the create-time configuration. + if ctx != nil { + if names := p.Catalog().BuildSessionVars(); len(names) > 0 { + sv, cerr := compileplugin.CaptureVars(ctx.ResolveVariable, names) + if cerr != nil { + return cerr + } + if len(sv) > 0 { + for _, idxDef := range idxDefs { + flat := map[string]string{} + if idxDef.IndexAlgoParams != "" { + if flat, err = catalog.IndexParamsStringToMap(idxDef.IndexAlgoParams); err != nil { + return err + } + } + if idxDef.IndexAlgoParams, err = catalog.IndexParamsMapToJsonStringWithSessionVars(flat, sv); err != nil { + return err + } + } + } + } + } createTable.IndexTables = append(createTable.IndexTables, tblDefs...) createTable.TableDef.Indexes = append(createTable.TableDef.Indexes, idxDefs...) } @@ -2939,6 +2963,10 @@ func validateIncludeColumns(ctx CompilerContext, if len(includeCols) == 0 { return nil } + supportedNames := make([]string, 0, len(supportedTypes)) + for _, typ := range supportedTypes { + supportedNames = append(supportedNames, typ.String()) + } seen := make(map[string]struct{}, len(includeCols)) for _, uc := range includeCols { name := uc.ColName() @@ -2981,8 +3009,8 @@ func validateIncludeColumns(ctx CompilerContext, } if !supported { return moerr.NewNotSupportedf(ctx.GetContext(), - "INCLUDE column '%s' has unsupported type %s (supported: int32, int64, float32, float64)", - origin, colType.String()) + "INCLUDE column '%s' has unsupported type %s (supported: %s)", + origin, colType.String(), strings.Join(supportedNames, ", ")) } } return nil diff --git a/pkg/sql/plan/build_ddl_fulltext2_test.go b/pkg/sql/plan/build_ddl_fulltext2_test.go new file mode 100644 index 0000000000000..a0213d0fe7946 --- /dev/null +++ b/pkg/sql/plan/build_ddl_fulltext2_test.go @@ -0,0 +1,61 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plan + +import ( + "testing" + + "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/container/types" + pbplan "github.com/matrixorigin/matrixone/pkg/pb/plan" + "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" + "github.com/stretchr/testify/require" +) + +type fulltext2SessionVarCompilerContext struct { + *MockCompilerContext +} + +func (c *fulltext2SessionVarCompilerContext) ResolveVariable(name string, isSystemVar, isGlobalVar bool) (interface{}, error) { + if name == "lower_case_table_names" { + return int64(1), nil + } + return c.MockCompilerContext.ResolveVariable(name, isSystemVar, isGlobalVar) +} + +func TestBuildFullTextIndexTableCapturesSessionVars(t *testing.T) { + ctx := &fulltext2SessionVarCompilerContext{MockCompilerContext: NewMockCompilerContext(true)} + createTable := &pbplan.CreateTable{TableDef: &pbplan.TableDef{}} + indexInfo := &tree.FullTextIndex{ + Name: "ft", + IsV2: true, + KeyParts: []*tree.KeyPart{{ + ColName: tree.NewUnresolvedColName("body"), + }}, + } + colMap := map[string]*ColDef{ + "id": {Name: "id", Typ: pbplan.Type{Id: int32(types.T_int64)}}, + "body": {Name: "body", Typ: pbplan.Type{Id: int32(types.T_text)}}, + } + + err := buildFullTextIndexTable(createTable, []*tree.FullTextIndex{indexInfo}, colMap, nil, "id", ctx) + require.NoError(t, err) + require.Len(t, createTable.TableDef.Indexes, 2) + for _, indexDef := range createTable.TableDef.Indexes { + sessionVars, err := catalog.IndexParamsSessionVars(indexDef.IndexAlgoParams) + require.NoError(t, err) + require.Contains(t, string(sessionVars), "lower_case_table_names") + } +} diff --git a/pkg/sql/plan/build_ddl_vector_test.go b/pkg/sql/plan/build_ddl_vector_test.go index 6363b8713211c..5862940ef38fb 100644 --- a/pkg/sql/plan/build_ddl_vector_test.go +++ b/pkg/sql/plan/build_ddl_vector_test.go @@ -103,6 +103,7 @@ func TestValidateIncludeColumns_UnsupportedType(t *testing.T) { colMap, "v", "id", includeTestSupportedTypes) require.Error(t, err) require.Contains(t, err.Error(), "unsupported type") + require.Contains(t, err.Error(), "supported: INT, BIGINT, FLOAT, DOUBLE") } func TestValidateIncludeColumns_AllSupportedNumericTypes(t *testing.T) { diff --git a/pkg/sql/plan/fulltext2_query_shape_test.go b/pkg/sql/plan/fulltext2_query_shape_test.go index aa0ec36b7cdeb..b355db8c7cc5d 100644 --- a/pkg/sql/plan/fulltext2_query_shape_test.go +++ b/pkg/sql/plan/fulltext2_query_shape_test.go @@ -2,6 +2,15 @@ // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package plan diff --git a/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.result b/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.result index 94f0e5e3c57b1..3f9f5c9bc743b 100644 --- a/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.result +++ b/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.result @@ -13,7 +13,7 @@ t ¦ CREATE TABLE `t` ( `id` bigint NOT NULL, `body` text DEFAULT NULL, PRIMARY KEY (`id`), - FULLTEXT2 `ft`(`body`) WITH PARSER ngram max_index_capacity = 2 + FULLTEXT2 `ft`(`body`) WITH PARSER ngram max_index_capacity = 2 ) select id from t where match(body) against('alpha' in bm25 mode) order by id; ➤ id[-5,64,0] 𝄀 diff --git a/test/distributed/cases/fulltext2/fulltext2_showcreate.result b/test/distributed/cases/fulltext2/fulltext2_showcreate.result index 6a675054f0dd6..86fe73387e6ee 100644 --- a/test/distributed/cases/fulltext2/fulltext2_showcreate.result +++ b/test/distributed/cases/fulltext2/fulltext2_showcreate.result @@ -10,7 +10,7 @@ t1 ¦ CREATE TABLE `t1` ( `id` bigint NOT NULL, `body` text DEFAULT NULL, PRIMARY KEY (`id`), - FULLTEXT2 `ft`(`body`) WITH PARSER ngram max_index_capacity = 2 + FULLTEXT2 `ft`(`body`) WITH PARSER ngram max_index_capacity = 2 ) create table t2 (id bigint primary key, body text); create fulltext2 index ft on t2(body) auto_update = true second 5; @@ -20,7 +20,7 @@ t2 ¦ CREATE TABLE `t2` ( `id` bigint NOT NULL, `body` text DEFAULT NULL, PRIMARY KEY (`id`), - FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true second = 5 + FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true second = 5 ) create table t3 (id bigint primary key, body text); create fulltext2 index ft on t3(body) auto_update = true day 1 hour 2; @@ -30,7 +30,7 @@ t3 ¦ CREATE TABLE `t3` ( `id` bigint NOT NULL, `body` text DEFAULT NULL, PRIMARY KEY (`id`), - FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true day = 1 hour = 2 + FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true day = 1 hour = 2 ) create table t4 ( id bigint NOT NULL, @@ -44,6 +44,6 @@ t4 ¦ CREATE TABLE `t4` ( `id` bigint NOT NULL, `body` text DEFAULT NULL, PRIMARY KEY (`id`), - FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true second = 5 + FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true second = 5 ) drop database ft2_showcreate; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.result index 81e9a48c53c4d..28fa530de8793 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.result +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.result @@ -45,8 +45,8 @@ show create table src2; Table Create Table src2 CREATE TABLE `src2` (\n `id1` varchar(65535) NOT NULL,\n `id2` bigint NOT NULL,\n `body` char(128) DEFAULT NULL,\n `title` text DEFAULT NULL,\n PRIMARY KEY (`id1`,`id2`),\n FULLTEXT2 `ftidx`(`body`,`title`) WITH PARSER ngram\n) set @capture_src_tail_sql = concat( -'select coalesce(max(chunk_id), -1) into @src_tail_before_mutation from `', database(), '`.`', @src_ft2_index, -'` where index_id = ''cdc_tail'' and tag = 1' +'set @src_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @src_ft2_index, +'` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_src_tail from @capture_src_tail_sql; execute capture_src_tail; @@ -63,8 +63,8 @@ execute wait_src_mutation; 1 deallocate prepare wait_src_mutation; set @capture_src_tail_sql = concat( -'select coalesce(max(chunk_id), -1) into @src_tail_before_mutation from `', database(), '`.`', @src_ft2_index, -'` where index_id = ''cdc_tail'' and tag = 1' +'set @src_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @src_ft2_index, +'` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_src_tail from @capture_src_tail_sql; execute capture_src_tail; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.sql index 0a99bfd519013..66b87ae5fc8a8 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.sql +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_async.sql @@ -65,8 +65,8 @@ show create table src2; -- post-create UPDATE + DELETE flow through CDC too set @capture_src_tail_sql = concat( - 'select coalesce(max(chunk_id), -1) into @src_tail_before_mutation from `', database(), '`.`', @src_ft2_index, - '` where index_id = ''cdc_tail'' and tag = 1' + 'set @src_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @src_ft2_index, + '` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_src_tail from @capture_src_tail_sql; execute capture_src_tail; @@ -82,8 +82,8 @@ prepare wait_src_mutation from @wait_src_mutation_sql; execute wait_src_mutation; deallocate prepare wait_src_mutation; set @capture_src_tail_sql = concat( - 'select coalesce(max(chunk_id), -1) into @src_tail_before_mutation from `', database(), '`.`', @src_ft2_index, - '` where index_id = ''cdc_tail'' and tag = 1' + 'set @src_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @src_ft2_index, + '` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_src_tail from @capture_src_tail_sql; execute capture_src_tail; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.result index 3b23ad2e0a416..1624871b7ca5c 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.result +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.result @@ -35,8 +35,8 @@ execute wait_dl2_initial; 1 deallocate prepare wait_dl2_initial; set @capture_dl2_tail_sql = concat( -'select coalesce(max(chunk_id), -1) into @dl2_tail_before_update from `', database(), '`.`', @dl2_ft2, -'` where index_id = ''cdc_tail'' and tag = 1' +'set @dl2_tail_before_update = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @dl2_ft2, +'` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_dl2_tail from @capture_dl2_tail_sql; execute capture_dl2_tail; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.sql index 332fce1c9ca6f..7a21f89cad31a 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.sql +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_datalink.sql @@ -50,8 +50,8 @@ prepare wait_dl2_initial from @wait_dl2_initial_sql; execute wait_dl2_initial; deallocate prepare wait_dl2_initial; set @capture_dl2_tail_sql = concat( - 'select coalesce(max(chunk_id), -1) into @dl2_tail_before_update from `', database(), '`.`', @dl2_ft2, - '` where index_id = ''cdc_tail'' and tag = 1' + 'set @dl2_tail_before_update = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @dl2_ft2, + '` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_dl2_tail from @capture_dl2_tail_sql; execute capture_dl2_tail; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.result index eee3d05be1d05..2910f3b3fb9f6 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.result +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.result @@ -38,8 +38,8 @@ select id, prio from src_ins where match(body) against('fox') and prio > 15 orde 2 ¦ 20 𝄀 3 ¦ 30 set @capture_src_upd_tail_sql = concat( -'select coalesce(max(chunk_id), -1) into @src_upd_tail_before_mutation from `', database(), '`.`', @src_upd_ft2, -'` where index_id = ''cdc_tail'' and tag = 1' +'set @src_upd_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @src_upd_ft2, +'` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_src_upd_tail from @capture_src_upd_tail_sql; execute capture_src_upd_tail; @@ -56,8 +56,8 @@ execute wait_src_upd_mutation; 1 deallocate prepare wait_src_upd_mutation; set @capture_src_upd_tail_sql = concat( -'select coalesce(max(chunk_id), -1) into @src_upd_tail_before_mutation from `', database(), '`.`', @src_upd_ft2, -'` where index_id = ''cdc_tail'' and tag = 1' +'set @src_upd_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @src_upd_ft2, +'` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_src_upd_tail from @capture_src_upd_tail_sql; execute capture_src_upd_tail; @@ -102,8 +102,8 @@ execute wait_phantom_initial; 1 deallocate prepare wait_phantom_initial; set @capture_phantom_tail_sql = concat( -'select coalesce(max(chunk_id), -1) into @phantom_tail_before_mutation from `', database(), '`.`', @src_phantom_ft2, -'` where index_id = ''cdc_tail'' and tag = 1' +'set @phantom_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @src_phantom_ft2, +'` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_phantom_tail from @capture_phantom_tail_sql; execute capture_phantom_tail; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.sql index 6c83f1c54446b..401fea06d397c 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.sql +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_include_async.sql @@ -46,8 +46,8 @@ select id, prio from src_ins where match(body) against('fox') and prio > 15 orde -- mutate src_upd: UPDATE an include value (LWW) + DELETE, BEFORE its first search. set @capture_src_upd_tail_sql = concat( - 'select coalesce(max(chunk_id), -1) into @src_upd_tail_before_mutation from `', database(), '`.`', @src_upd_ft2, - '` where index_id = ''cdc_tail'' and tag = 1' + 'set @src_upd_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @src_upd_ft2, + '` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_src_upd_tail from @capture_src_upd_tail_sql; execute capture_src_upd_tail; @@ -63,8 +63,8 @@ prepare wait_src_upd_mutation from @wait_src_upd_mutation_sql; execute wait_src_upd_mutation; deallocate prepare wait_src_upd_mutation; set @capture_src_upd_tail_sql = concat( - 'select coalesce(max(chunk_id), -1) into @src_upd_tail_before_mutation from `', database(), '`.`', @src_upd_ft2, - '` where index_id = ''cdc_tail'' and tag = 1' + 'set @src_upd_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @src_upd_ft2, + '` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_src_upd_tail from @capture_src_upd_tail_sql; execute capture_src_upd_tail; @@ -109,8 +109,8 @@ prepare wait_phantom_initial from @wait_phantom_initial_sql; execute wait_phantom_initial; deallocate prepare wait_phantom_initial; set @capture_phantom_tail_sql = concat( - 'select coalesce(max(chunk_id), -1) into @phantom_tail_before_mutation from `', database(), '`.`', @src_phantom_ft2, - '` where index_id = ''cdc_tail'' and tag = 1' + 'set @phantom_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @src_phantom_ft2, + '` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_phantom_tail from @capture_phantom_tail_sql; execute capture_phantom_tail; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.result index 853e99fd3f738..de02d2f5df9db 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.result +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.result @@ -7,8 +7,8 @@ insert into t values (1,'我家有三个人'),(2,'三个人都住在我家'),(3, create fulltext2 index ft on t(body) with parser gojieba position_free = true; set @t_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ft' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 't') limit 1); set @capture_t_tail_sql = concat( -'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, -'` where index_id = ''cdc_tail'' and tag = 1' +'set @t_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @t_ft2, +'` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_t_tail from @capture_t_tail_sql; execute capture_t_tail; @@ -25,8 +25,8 @@ execute wait_t_mutation; 1 deallocate prepare wait_t_mutation; set @capture_t_tail_sql = concat( -'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, -'` where index_id = ''cdc_tail'' and tag = 1' +'set @t_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @t_ft2, +'` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_t_tail from @capture_t_tail_sql; execute capture_t_tail; @@ -62,8 +62,8 @@ select id from t where match(body) against('我家' in bm25 mode) order by id; select id from t where match(body) against('我家' in boolean mode) order by id; invalid input: fulltext2 index "ft" is POSITION_FREE (bag-of-words only): query it with MATCH(...) AGAINST(... IN BM25 MODE); it has no positions for natural-language / boolean phrase matching set @capture_t_tail_sql = concat( -'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, -'` where index_id = ''cdc_tail'' and tag = 1' +'set @t_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @t_ft2, +'` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_t_tail from @capture_t_tail_sql; execute capture_t_tail; @@ -80,8 +80,8 @@ execute wait_t_mutation; 1 deallocate prepare wait_t_mutation; set @capture_t_tail_sql = concat( -'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, -'` where index_id = ''cdc_tail'' and tag = 1' +'set @t_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @t_ft2, +'` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_t_tail from @capture_t_tail_sql; execute capture_t_tail; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.sql index 42107a5b8e404..e390bb063031e 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.sql +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_position_free_merge.sql @@ -14,8 +14,8 @@ insert into t values (1,'我家有三个人'),(2,'三个人都住在我家'),(3, create fulltext2 index ft on t(body) with parser gojieba position_free = true; set @t_ft2 = (select index_table_name from mo_catalog.mo_indexes where name = 'ft' and algo_table_type = 'ftv2_index' and table_id in (select rel_id from mo_catalog.mo_tables where reldatabase = database() and relname = 't') limit 1); set @capture_t_tail_sql = concat( - 'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, - '` where index_id = ''cdc_tail'' and tag = 1' + 'set @t_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @t_ft2, + '` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_t_tail from @capture_t_tail_sql; execute capture_t_tail; @@ -33,8 +33,8 @@ prepare wait_t_mutation from @wait_t_mutation_sql; execute wait_t_mutation; deallocate prepare wait_t_mutation; set @capture_t_tail_sql = concat( - 'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, - '` where index_id = ''cdc_tail'' and tag = 1' + 'set @t_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @t_ft2, + '` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_t_tail from @capture_t_tail_sql; execute capture_t_tail; @@ -63,8 +63,8 @@ select id from t where match(body) against('我家' in boolean mode) order by id -- more mutations, then REINDEX REBUILD (rebuild the base from source, stays position-free) set @capture_t_tail_sql = concat( - 'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, - '` where index_id = ''cdc_tail'' and tag = 1' + 'set @t_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @t_ft2, + '` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_t_tail from @capture_t_tail_sql; execute capture_t_tail; @@ -80,8 +80,8 @@ prepare wait_t_mutation from @wait_t_mutation_sql; execute wait_t_mutation; deallocate prepare wait_t_mutation; set @capture_t_tail_sql = concat( - 'select coalesce(max(chunk_id), -1) into @t_tail_before_mutation from `', database(), '`.`', @t_ft2, - '` where index_id = ''cdc_tail'' and tag = 1' + 'set @t_tail_before_mutation = (select coalesce(max(chunk_id), -1) from `', database(), '`.`', @t_ft2, + '` where index_id = ''cdc_tail'' and tag = 1)' ); prepare capture_t_tail from @capture_t_tail_sql; execute capture_t_tail; From fbea073c3b55836317c7aeb49d090ed2167f004c Mon Sep 17 00:00:00 2001 From: VioletQwQ-0 <1659648118@qq.com> Date: Wed, 26 Aug 2026 23:55:45 +0800 Subject: [PATCH 11/17] fix(fulltext2): close covered prefilter and coverage gaps --- pkg/sql/plan/apply_indices_fulltext.go | 11 +- pkg/sql/plan/apply_indices_fulltext2_test.go | 459 +++++++++++++++++++ 2 files changed, 465 insertions(+), 5 deletions(-) diff --git a/pkg/sql/plan/apply_indices_fulltext.go b/pkg/sql/plan/apply_indices_fulltext.go index 783c1eef2e2e3..a7d9c8553cc89 100644 --- a/pkg/sql/plan/apply_indices_fulltext.go +++ b/pkg/sql/plan/apply_indices_fulltext.go @@ -996,9 +996,10 @@ func (builder *QueryBuilder) tryApplyCoveredFulltext2(nodeID int32, projNode, so // ---- All guards passed: build the covered plan. From here on we mutate. ---- ctx := builder.ctxByNode[nodeID] - // Remove the MATCH filter from the base scan (it is fully served by the TVF now). This is - // the last filter standing (residual is empty), so FilterList becomes empty. - scanNode.FilterList = append(scanNode.FilterList[:filterids[0]], scanNode.FilterList[filterids[0]+1:]...) + // The MATCH and every non-MATCH filter were consumed by the TVF prefilter. Drop the + // source filters together; the base scan is removed from the covered plan, so retaining + // the peeled predicates here would neither filter the TVF nor preserve their semantics. + scanNode.FilterList = nil cfg, cfgErr := builder.buildFulltext2SearchCfg(scanNode, idxdef, mode) if cfgErr != nil { @@ -1622,8 +1623,8 @@ func (builder *QueryBuilder) getWrappedFullText2Matches(projNode, scanNode *plan } } } - var exprs []*plan.Expr - var defs []*plan.IndexDef + exprs := make([]*plan.Expr, 0, len(candidates)) + defs := make([]*plan.IndexDef, 0, len(candidates)) for _, candidate := range candidates { fn := candidate.GetF() if fn == nil || len(fn.Args) < 2 { diff --git a/pkg/sql/plan/apply_indices_fulltext2_test.go b/pkg/sql/plan/apply_indices_fulltext2_test.go index de54dc8bf1d0f..162dff8c0a89b 100644 --- a/pkg/sql/plan/apply_indices_fulltext2_test.go +++ b/pkg/sql/plan/apply_indices_fulltext2_test.go @@ -19,6 +19,7 @@ import ( "testing" "github.com/matrixorigin/matrixone/pkg/catalog" + "github.com/matrixorigin/matrixone/pkg/container/types" "github.com/matrixorigin/matrixone/pkg/pb/plan" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" "github.com/stretchr/testify/require" @@ -105,3 +106,461 @@ func TestBuildFulltext2SearchCfg(t *testing.T) { require.NoError(t, err) require.Contains(t, cfg, "__store") } + +func fulltext2MatchFilter(fn *plan.Function) *plan.Expr { + return &plan.Expr{ + Typ: plan.Type{Id: int32(types.T_bool)}, + Expr: &plan.Expr_F{F: fn}, + } +} + +func fulltext2ColumnExpr(tag, pos int32, name string, typ plan.Type) *plan.Expr { + return &plan.Expr{ + Typ: typ, + Expr: &plan.Expr_Col{Col: &plan.ColRef{ + RelPos: tag, + ColPos: pos, + Name: name, + }}, + } +} + +func newFulltext2CoveredFixture(t *testing.T, withIncludeFilter, withSort bool) (*QueryBuilder, int32, *plan.Node, *plan.Node, *plan.IndexDef) { + t.Helper() + + builder := NewQueryBuilder(plan.Query_SELECT, NewMockCompilerContext(true), false, true) + ctx := NewBindContext(builder, nil) + tableDef := &plan.TableDef{ + Name: "docs", + Cols: []*plan.ColDef{ + {Name: "id", Typ: plan.Type{Id: int32(types.T_int64), Width: 64}}, + {Name: "body", Typ: plan.Type{Id: int32(types.T_text)}}, + {Name: "title", Typ: plan.Type{Id: int32(types.T_varchar), Width: 128}}, + }, + Name2ColIndex: map[string]int32{"id": 0, "body": 1, "title": 2}, + Pkey: &plan.PrimaryKeyDef{PkeyColName: "id", Names: []string{"id"}}, + } + idxdef := &plan.IndexDef{ + IndexName: "ft2_docs", + IndexAlgo: catalog.MoIndexFullText2Algo.ToString(), + IndexAlgoTableType: catalog.FullText2Index_TblType_Storage, + IndexTableName: "__ft2_docs_store", + IndexAlgoParams: `{"parser":"ngram"}`, + IncludedColumns: []string{"title"}, + Parts: []string{"body"}, + TableExist: true, + } + metaDef := &plan.IndexDef{ + IndexName: idxdef.IndexName, + IndexAlgo: idxdef.IndexAlgo, + IndexAlgoTableType: catalog.FullText2Index_TblType_Metadata, + IndexTableName: "__ft2_docs_meta", + IndexAlgoParams: idxdef.IndexAlgoParams, + TableExist: true, + } + tableDef.Indexes = []*plan.IndexDef{idxdef, metaDef} + + tag := builder.genNewBindTag() + match := ft2TestMatch("hello", 0, tag, "body") + filters := []*plan.Expr{fulltext2MatchFilter(match)} + if withIncludeFilter { + filters = append(filters, &plan.Expr{ + Typ: plan.Type{Id: int32(types.T_bool)}, + Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{ObjName: "="}, + Args: []*plan.Expr{ + fulltext2ColumnExpr(tag, 0, "id", tableDef.Cols[0].Typ), + makePlan2Int64ConstExprWithType(7), + }, + }}, + }) + } + scan := &plan.Node{ + NodeType: plan.Node_TABLE_SCAN, + TableDef: tableDef, + ObjRef: &plan.ObjectRef{SchemaName: "db", ObjName: "docs"}, + BindingTags: []int32{tag}, + FilterList: filters, + Stats: &plan.Stats{}, + } + scanID := builder.appendNode(scan, ctx) + + project := &plan.Node{ + NodeType: plan.Node_PROJECT, + ProjectList: []*plan.Expr{ + fulltext2ColumnExpr(tag, 0, "id", tableDef.Cols[0].Typ), + fulltext2MatchFilter(match), + fulltext2ColumnExpr(tag, 2, "title", tableDef.Cols[2].Typ), + }, + } + if withSort { + sort := &plan.Node{NodeType: plan.Node_SORT, Children: []int32{scanID}, Stats: &plan.Stats{}} + sortID := builder.appendNode(sort, ctx) + project.Children = []int32{sortID} + } else { + project.Children = []int32{scanID} + } + projectID := builder.appendNode(project, ctx) + return builder, projectID, project, scan, idxdef +} + +func TestFulltext2CoveredProjectionUsesTVFOutputs(t *testing.T) { + for _, tc := range []struct { + name string + withIncludeFilter bool + withSort bool + wantArgs int + }{ + {name: "implicit score sort", wantArgs: 3}, + {name: "include prefilter and explicit sort", withIncludeFilter: true, withSort: true, wantArgs: 4}, + } { + t.Run(tc.name, func(t *testing.T) { + builder, projectID, project, scan, idxdef := newFulltext2CoveredFixture(t, tc.withIncludeFilter, tc.withSort) + filterIDs, filterDefs := builder.getFullTextMatchFiltersFromScanNode(scan) + require.Equal(t, []int32{0}, filterIDs) + require.Len(t, filterDefs, 1) + projectIDs, projectDefs := builder.getFullTextMatchFromProject(project, scan) + require.Equal(t, []int32{1}, projectIDs) + require.Len(t, projectDefs, 1) + var sortNode *plan.Node + if tc.withSort { + sortNode = builder.qry.Nodes[project.Children[0]] + } + if tc.withIncludeFilter { + _, _, residual, err := buildFilterPredicateJSON(scan.FilterList[1:], scan, []string{"title"}, "id", true) + require.NoError(t, err) + require.Empty(t, residual) + } + + newID, err := builder.applyIndicesForProjectionUsingFullTextIndex( + projectID, project, sortNode, scan, + []int32{0}, []*plan.IndexDef{idxdef}, + []int32{1}, []*plan.IndexDef{idxdef}, + nil, nil, nil, nil, + ) + require.NoError(t, err) + require.Equal(t, projectID, newID) + require.Empty(t, scan.FilterList) + + if sortNode == nil { + sortNode = builder.qry.Nodes[project.Children[0]] + } + require.Equal(t, plan.Node_SORT, sortNode.NodeType) + search := builder.qry.Nodes[sortNode.Children[0]] + require.Equal(t, plan.Node_FUNCTION_SCAN, search.NodeType) + require.Equal(t, fulltext2_search_func_name, search.TableDef.TblFunc.Name) + require.Len(t, search.TblFuncExprList, tc.wantArgs) + require.Equal(t, scan.TableDef.Cols[0].Typ.Id, search.TableDef.Cols[0].Typ.Id) + + pk := project.ProjectList[0].GetCol() + require.NotNil(t, pk) + require.Equal(t, search.BindingTags[0], pk.RelPos) + require.Equal(t, int32(0), pk.ColPos) + score := project.ProjectList[1].GetCol() + require.NotNil(t, score) + require.Equal(t, search.BindingTags[0], score.RelPos) + require.Equal(t, int32(1), score.ColPos) + included := project.ProjectList[2].GetCol() + require.NotNil(t, included) + require.Equal(t, search.BindingTags[0], included.RelPos) + require.Equal(t, int32(2), included.ColPos) + if tc.withIncludeFilter { + require.Contains(t, search.TblFuncExprList[3].GetLit().GetSval(), `"col":-1`) + } + }) + } +} + +func TestFulltext2CoveredProjectionDeclinesUncoveredColumn(t *testing.T) { + builder, projectID, project, scan, idxdef := newFulltext2CoveredFixture(t, false, false) + project.ProjectList = append(project.ProjectList, fulltext2ColumnExpr(scan.BindingTags[0], 1, "body", scan.TableDef.Cols[1].Typ)) + + newID, err := builder.applyIndicesForProjectionUsingFullTextIndex( + projectID, project, nil, scan, + []int32{0}, []*plan.IndexDef{idxdef}, + []int32{1}, []*plan.IndexDef{idxdef}, + nil, nil, nil, nil, + ) + require.NoError(t, err) + require.Equal(t, projectID, newID) + sort := builder.qry.Nodes[project.Children[0]] + require.Equal(t, plan.Node_SORT, sort.NodeType) + join := builder.qry.Nodes[sort.Children[0]] + require.Equal(t, plan.Node_JOIN, join.NodeType) + require.Equal(t, plan.Node_TABLE_SCAN, builder.qry.Nodes[join.Children[0]].NodeType) + require.Equal(t, plan.Node_FUNCTION_SCAN, builder.qry.Nodes[join.Children[1]].NodeType) +} + +func fulltext2ComparisonExpr(op string, left, right *plan.Expr) *plan.Expr { + return &plan.Expr{ + Typ: plan.Type{Id: int32(types.T_bool)}, + Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{ObjName: op}, + Args: []*plan.Expr{left, right}, + }}, + } +} + +func TestFulltext2MatchShapeHelpers(t *testing.T) { + match := ft2TestMatch("hello", 0, 1, "body") + matchExpr := ft2TestExpr(match) + wrapped := &plan.Expr{ + Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{ObjName: "round"}, + Args: []*plan.Expr{matchExpr}, + }}, + } + list := &plan.Expr{Expr: &plan.Expr_List{List: &plan.ExprList{List: []*plan.Expr{ + wrapped, + makePlan2Int64ConstExprWithType(1), + }}}} + + require.True(t, exprCallsFunc(list, "fulltext_match")) + require.False(t, exprCallsFunc(nil, "fulltext_match")) + require.False(t, exprCallsFunc(makePlan2StringConstExprWithType("plain"), "fulltext_match")) + matches := collectNestedFullTextMatches(list, nil) + require.Len(t, matches, 1) + require.Same(t, matchExpr, matches[0]) + require.Empty(t, collectNestedFullTextMatches(nil, nil)) + + replaced := replaceScoreFnInExprBy(list, func(fn *plan.Function) *plan.Expr { + if fn != nil && fn.Func != nil && fn.Func.ObjName == "fulltext_match" { + return makePlan2Float32ConstExprWithType(0.75) + } + return nil + }) + require.NotNil(t, replaced.GetList()) + require.Equal(t, float32(0.75), replaced.GetList().List[0].GetF().Args[0].GetLit().GetFval()) + require.Nil(t, replaceScoreFnInExprBy(nil, nil)) + + for _, name := range []string{"round", "cast", "floor", "ceil"} { + expr := &plan.Expr{Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{ObjName: name}, + Args: []*plan.Expr{matchExpr}, + }}} + require.Same(t, matchExpr, monotoneWrappedFullTextMatch(expr), name) + } + require.Same(t, matchExpr, monotoneWrappedFullTextMatch(matchExpr)) + require.Nil(t, monotoneWrappedFullTextMatch(nil)) + require.Nil(t, monotoneWrappedFullTextMatch(&plan.Expr{Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{ObjName: "round"}, + }}})) + require.Nil(t, monotoneWrappedFullTextMatch(&plan.Expr{Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{ObjName: "sqrt"}, + Args: []*plan.Expr{matchExpr}, + }}})) +} + +func TestFulltext2DrivingMatchAndLiteralGuards(t *testing.T) { + match := ft2TestMatch("hello", 0, 1, "body") + matchExpr := ft2TestExpr(match) + value := makePlan2Float64ConstExprWithType(0.5) + + require.Len(t, collectDrivingFullText2Matches(fulltext2ComparisonExpr(">", matchExpr, value), nil), 1) + require.Len(t, collectDrivingFullText2Matches(fulltext2ComparisonExpr(">=", matchExpr, value), nil), 1) + require.Len(t, collectDrivingFullText2Matches(fulltext2ComparisonExpr("and", + fulltext2ComparisonExpr(">", matchExpr, value), + fulltext2ComparisonExpr(">=", matchExpr, value)), nil), 2) + for _, expr := range []*plan.Expr{ + fulltext2ComparisonExpr(">=", matchExpr, makePlan2Float64ConstExprWithType(0)), + fulltext2ComparisonExpr(">", matchExpr, makePlan2Float64ConstExprWithType(-1)), + fulltext2ComparisonExpr("<", matchExpr, value), + fulltext2ComparisonExpr(">", matchExpr, makePlan2StringConstExprWithType("0.5")), + fulltext2ComparisonExpr(">", makePlan2StringConstExprWithType("0.5"), value), + } { + require.Empty(t, collectDrivingFullText2Matches(expr, nil)) + } + + for _, tc := range []struct { + expr *plan.Expr + want float64 + }{ + {makePlan2Int64ConstExprWithType(-2), -2}, + {makePlan2Uint64ConstExprWithType(3), 3}, + {makePlan2Float32ConstExprWithType(0.25), 0.25}, + {makePlan2Float64ConstExprWithType(0.75), 0.75}, + } { + got, ok := constValueAsFloat(tc.expr) + require.True(t, ok) + require.Equal(t, tc.want, got) + } + for _, expr := range []*plan.Expr{nil, makePlan2StringConstExprWithType("not numeric"), matchExpr} { + _, ok := constValueAsFloat(expr) + require.False(t, ok) + } +} + +func TestFulltext2WrappedMatchDiscovery(t *testing.T) { + builder, _, project, scan, idxdef := newFulltext2CoveredFixture(t, false, false) + tag := scan.BindingTags[0] + second := ft2TestMatch("other", 0, tag, "body") + scan.FilterList = append(scan.FilterList, fulltext2ComparisonExpr(">", ft2TestExpr(second), makePlan2Float64ConstExprWithType(0.5))) + project.ProjectList = append(project.ProjectList, &plan.Expr{Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{ObjName: "round"}, + Args: []*plan.Expr{ft2TestExpr(second)}, + }}}) + + gotExprs, gotDefs := builder.getWrappedFullText2Matches(project, scan, []int32{0}, []int32{1}) + require.Len(t, gotExprs, 1) + require.Len(t, gotDefs, 1) + require.Same(t, second, gotExprs[0].GetF()) + require.Same(t, idxdef, gotDefs[0]) + + upperBuilder, _, upperProject, upperScan, _ := newFulltext2CoveredFixture(t, false, false) + upperMatch := ft2TestMatch("upper", 0, upperScan.BindingTags[0], "body") + upperScan.FilterList = []*plan.Expr{fulltext2ComparisonExpr("<", ft2TestExpr(upperMatch), makePlan2Float64ConstExprWithType(0.5))} + upperProject.ProjectList = nil + gotExprs, gotDefs = upperBuilder.getWrappedFullText2Matches(upperProject, upperScan, nil, nil) + require.Empty(t, gotExprs) + require.Empty(t, gotDefs) + + classicBuilder, _, classicProject, classicScan, _ := newFulltext2CoveredFixture(t, false, false) + classicScan.TableDef.Indexes[0].IndexAlgo = catalog.MOIndexFullTextAlgo.ToString() + classicMatch := ft2TestMatch("classic", 0, classicScan.BindingTags[0], "body") + classicProject.ProjectList = []*plan.Expr{&plan.Expr{Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{ObjName: "round"}, + Args: []*plan.Expr{ft2TestExpr(classicMatch)}, + }}}} + gotExprs, gotDefs = classicBuilder.getWrappedFullText2Matches(classicProject, classicScan, nil, nil) + require.Empty(t, gotExprs) + require.Empty(t, gotDefs) + + malformedBuilder, _, malformedProject, malformedScan, _ := newFulltext2CoveredFixture(t, false, false) + malformedProject.ProjectList = []*plan.Expr{{Expr: &plan.Expr_F{F: &plan.Function{ + Func: &plan.ObjectRef{ObjName: "fulltext_match"}, + Args: []*plan.Expr{makePlan2StringConstExprWithType("missing mode")}, + }}}} + gotExprs, gotDefs = malformedBuilder.getWrappedFullText2Matches(malformedProject, malformedScan, nil, nil) + require.Empty(t, gotExprs) + require.Empty(t, gotDefs) + + gotExprs, gotDefs = builder.getWrappedFullText2Matches(nil, nil, nil, nil) + require.Empty(t, gotExprs) + require.Empty(t, gotDefs) +} + +func TestFulltext2ServingAndCoverageHelpers(t *testing.T) { + match := ft2TestMatch("hello", 0, 1, "body") + builder := &QueryBuilder{} + served := []fulltextServedMatch{{fn: match, nodeID: 1, fulltext2: true}} + require.True(t, builder.isServedFullText2Match(match, served)) + require.False(t, builder.isServedFullText2Match(nil, served)) + require.False(t, builder.isServedFullText2Match(&plan.Function{Func: &plan.ObjectRef{ObjName: "other"}}, served)) + require.False(t, builder.isServedFullText2Match(match, []fulltextServedMatch{{fn: match}})) + + scoreNode := &plan.Node{ + BindingTags: []int32{7}, + TableDef: &plan.TableDef{Cols: []*plan.ColDef{{}, {Typ: plan.Type{Id: int32(types.T_float32)}}}}, + } + scoreBuilder := &QueryBuilder{qry: &plan.Query{Nodes: []*plan.Node{scoreNode}}} + scoreRef := scoreBuilder.fullText2ScoreColRef(0) + require.NotNil(t, scoreRef) + require.Equal(t, int32(7), scoreRef.GetCol().RelPos) + require.Equal(t, int32(1), scoreRef.GetCol().ColPos) + invalidBuilder := &QueryBuilder{qry: &plan.Query{Nodes: []*plan.Node{ + {}, + {TableDef: &plan.TableDef{Cols: []*plan.ColDef{{}, {}}}}, + {TableDef: &plan.TableDef{Cols: []*plan.ColDef{{}, {Typ: plan.Type{Id: int32(types.T_float32)}}}}}, + }}} + require.Nil(t, invalidBuilder.fullText2ScoreColRef(-1)) + require.Nil(t, invalidBuilder.fullText2ScoreColRef(0)) + require.Nil(t, invalidBuilder.fullText2ScoreColRef(1)) + require.Nil(t, invalidBuilder.fullText2ScoreColRef(2)) + + list := &plan.Expr{Expr: &plan.Expr_List{List: &plan.ExprList{List: []*plan.Expr{ft2TestExpr(match)}}}} + require.True(t, hasUnservedFullText2Match(list, func(*plan.Function) bool { return false })) + require.False(t, hasUnservedFullText2Match(list, func(*plan.Function) bool { return true })) + require.False(t, hasUnservedFullText2Match(nil, func(*plan.Function) bool { return false })) + require.False(t, hasUnservedFullText2Match(makePlan2Int64ConstExprWithType(1), func(*plan.Function) bool { return false })) + + coveredBuilder, _, _, scan, _ := newFulltext2CoveredFixture(t, false, false) + coveredMatch := ft2TestMatch("covered", 0, scan.BindingTags[0], "body") + require.True(t, coveredBuilder.exprHasFullText2Match(ft2TestExpr(coveredMatch), scan)) + scan.TableDef.Indexes[0].IndexAlgo = catalog.MOIndexFullTextAlgo.ToString() + require.False(t, coveredBuilder.exprHasFullText2Match(ft2TestExpr(coveredMatch), scan)) + require.False(t, coveredBuilder.exprHasFullText2Match(nil, scan)) + + require.True(t, isProjIndexPosition([]int32{1, 3}, 3)) + require.False(t, isProjIndexPosition([]int32{1, 3}, 2)) + + scanTag := int32(10) + otherTag := int32(11) + expr := &plan.Expr{Expr: &plan.Expr_List{List: &plan.ExprList{List: []*plan.Expr{ + fulltext2ColumnExpr(scanTag, 0, "", plan.Type{Id: int32(types.T_int64)}), + {Expr: &plan.Expr_F{F: &plan.Function{Func: &plan.ObjectRef{ObjName: "pair"}, Args: []*plan.Expr{ + fulltext2ColumnExpr(scanTag, 2, "", plan.Type{Id: int32(types.T_varchar)}), + fulltext2ColumnExpr(otherTag, 0, "other", plan.Type{Id: int32(types.T_int64)}), + }}}}, + }}}} + var positions []int32 + require.True(t, coveredBaseColRefs(expr, scanTag, func(pos int32, _ string) bool { + positions = append(positions, pos) + return true + })) + require.Equal(t, []int32{0, 2}, positions) + require.False(t, coveredBaseColRefs(expr, scanTag, func(int32, string) bool { return false })) + + remapExpr := &plan.Expr{Expr: &plan.Expr_List{List: &plan.ExprList{List: []*plan.Expr{ + fulltext2ColumnExpr(scanTag, 0, "", plan.Type{Id: int32(types.T_int64)}), + fulltext2ColumnExpr(scanTag, 2, "", plan.Type{Id: int32(types.T_varchar)}), + }}}} + remapCoveredBaseColRefs(remapExpr, scanTag, 20, 0, map[string]int{"title": 0}, &plan.Node{TableDef: &plan.TableDef{ + Cols: []*plan.ColDef{{Name: "id"}, {Name: "body"}, {Name: "title"}}, + }}) + require.Equal(t, int32(20), remapExpr.GetList().List[0].GetCol().RelPos) + require.Equal(t, int32(0), remapExpr.GetList().List[0].GetCol().ColPos) + require.Equal(t, int32(20), remapExpr.GetList().List[1].GetCol().RelPos) + require.Equal(t, int32(2), remapExpr.GetList().List[1].GetCol().ColPos) + require.Nil(t, remapCoveredBaseColRefs(nil, scanTag, 20, 0, nil, nil)) +} + +func TestFulltext2IndexNodeAndPkHelpers(t *testing.T) { + fixtureBuilder, _, _, scan, _ := newFulltext2CoveredFixture(t, false, false) + require.Equal(t, "id", fulltext2PeelablePkColName(scan)) + scan.TableDef.Cols[0].Typ.Id = int32(types.T_text) + require.Empty(t, fulltext2PeelablePkColName(scan)) + noPk := &plan.Node{TableDef: &plan.TableDef{Cols: []*plan.ColDef{{Name: "id", Typ: plan.Type{Id: int32(types.T_int64)}}}}} + require.Empty(t, fulltext2PeelablePkColName(noPk)) + + typ, ok := baseColTypeByName(scan, "TITLE") + require.True(t, ok) + require.Equal(t, int32(types.T_varchar), typ.Id) + _, ok = baseColTypeByName(scan, "missing") + require.False(t, ok) + _, ok = baseColTypeByName(nil, "title") + require.False(t, ok) + + ctx := NewBindContext(fixtureBuilder, nil) + nodeID, err := fixtureBuilder.buildFulltext2SearchNode(ctx, []*plan.Expr{ + makePlan2StringConstExprWithType("{}"), + makePlan2StringConstExprWithType("hello"), + makePlan2Int64ConstExprWithType(0), + }, nil) + require.NoError(t, err) + require.Equal(t, plan.Node_FUNCTION_SCAN, fixtureBuilder.qry.Nodes[nodeID].NodeType) + require.Equal(t, fulltext2_search_func_name, fixtureBuilder.qry.Nodes[nodeID].TableDef.TblFunc.Name) + require.Len(t, ft2SearchBaseColDefs(), 2) +} + +func TestFulltext2ProjectionWithoutIndexBecomesScore(t *testing.T) { + builder := NewQueryBuilder(plan.Query_SELECT, NewMockCompilerContext(true), false, true) + tag := builder.genNewBindTag() + scan := &plan.Node{ + TableDef: &plan.TableDef{ + Name: "docs", + Cols: []*plan.ColDef{ + {Name: "id", Typ: plan.Type{Id: int32(types.T_int64)}}, + {Name: "body", Typ: plan.Type{Id: int32(types.T_text)}}, + }, + Pkey: &plan.PrimaryKeyDef{PkeyColName: "id"}, + }, + BindingTags: []int32{tag}, + } + project := &plan.Node{ProjectList: []*plan.Expr{ + fulltext2MatchFilter(ft2TestMatch("hello", 0, tag, "body")), + }} + ids, defs := builder.getFullTextMatchFromProject(project, scan) + require.Empty(t, ids) + require.Empty(t, defs) + require.Equal(t, "fulltext_match_score", project.ProjectList[0].GetF().Func.ObjName) +} From 06ab54d812ffcae1b4910e74f2f42a85ae9dfe0b Mon Sep 17 00:00:00 2001 From: VioletQwQ-0 <1659648118@qq.com> Date: Thu, 27 Aug 2026 00:25:35 +0800 Subject: [PATCH 12/17] fix(fulltext2): close reverse score and consumer error paths --- pkg/iscp/fulltext2_consumer.go | 17 ++-- pkg/iscp/fulltext2_consumer_test.go | 81 ++++++++++++++++++++ pkg/sql/plan/apply_indices_fulltext.go | 24 ++++-- pkg/sql/plan/apply_indices_fulltext2_test.go | 4 + 4 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 pkg/iscp/fulltext2_consumer_test.go diff --git a/pkg/iscp/fulltext2_consumer.go b/pkg/iscp/fulltext2_consumer.go index 24907fdc8c2eb..9d176aae91825 100644 --- a/pkg/iscp/fulltext2_consumer.go +++ b/pkg/iscp/fulltext2_consumer.go @@ -39,14 +39,14 @@ import ( func RunFulltext2(c *IndexConsumer, ctx context.Context, errch chan error, r DataRetriever) { w, ok := c.sqlWriter.(*Fulltext2SqlWriter) if !ok { - errch <- moerr.NewInternalError(ctx, "fulltext2 iscp Run: unexpected writer type") + reportIndexConsumerErr(errch, moerr.NewInternalError(ctx, "fulltext2 iscp Run: unexpected writer type")) return } // Parser-aware tokenize (ngram/gojieba/json) so build and query tokens match. tokenize, err := fulltext2.CdcTokenizer(w.cfg.Parser) if err != nil { - errch <- err + reportIndexConsumerErr(errch, err) return } var bopts []fulltext2.BuildOpt @@ -64,7 +64,7 @@ func RunFulltext2(c *IndexConsumer, ctx context.Context, errch chan error, r Dat } tb, err := fulltext2.NewTailBuilder(w.pkType, w.capacity, w.postingCap, spillDir, tokenize, bopts...) if err != nil { - errch <- err + reportIndexConsumerErr(errch, err) return } defer tb.Cleanup() @@ -75,15 +75,16 @@ func RunFulltext2(c *IndexConsumer, ctx context.Context, errch chan error, r Dat for { select { case <-ctx.Done(): + reportIndexConsumerErr(errch, ctx.Err()) return case e := <-errch: - errch <- e + reportIndexConsumerErr(errch, e) return case blob, ok := <-c.sqlBufSendCh: if !ok { segs, ferr := tb.Finish() if ferr != nil { - errch <- ferr + reportIndexConsumerErr(errch, ferr) return } err = sqlexec.RunTxnWithSqlContext(ctx, c.cnEngine, c.cnTxnClient, c.cnUUID, r.GetAccountID(), time.Hour, nil, nil, @@ -117,7 +118,7 @@ func RunFulltext2(c *IndexConsumer, ctx context.Context, errch chan error, r Dat return nil }) if err != nil { - errch <- err + reportIndexConsumerErr(errch, err) return } // Evict the cached search index so the next query reloads tag=0 + the @@ -136,12 +137,12 @@ func RunFulltext2(c *IndexConsumer, ctx context.Context, errch chan error, r Dat cdc, derr := fulltext2.DecodeCdc(blob) if derr != nil { - errch <- derr + reportIndexConsumerErr(errch, derr) return } nevents += len(cdc.Events) if aerr := tb.AddBatch(cdc); aerr != nil { - errch <- aerr + reportIndexConsumerErr(errch, aerr) return } } diff --git a/pkg/iscp/fulltext2_consumer_test.go b/pkg/iscp/fulltext2_consumer_test.go new file mode 100644 index 0000000000000..3554ad474df53 --- /dev/null +++ b/pkg/iscp/fulltext2_consumer_test.go @@ -0,0 +1,81 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package iscp + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func newFulltext2ConsumerForTest() *IndexConsumer { + return &IndexConsumer{ + sqlWriter: newFT2Writer("ngram"), + sqlBufSendCh: make(chan []byte), + } +} + +func TestRunFulltext2CancellationReportsError(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + errch := make(chan error, 1) + done := make(chan struct{}) + + go func() { + RunFulltext2(newFulltext2ConsumerForTest(), ctx, errch, &MockRetriever{dtype: ISCPDataType_Snapshot}) + close(done) + }() + + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("RunFulltext2 did not stop after context cancellation") + } + select { + case err := <-errch: + require.ErrorIs(t, err, context.Canceled) + default: + t.Fatal("RunFulltext2 did not report context cancellation") + } +} + +func TestRunFulltext2FullErrorChannelDoesNotBlock(t *testing.T) { + existing := errors.New("existing consumer error") + errch := make(chan error, 1) + errch <- existing + ctx, cancel := context.WithCancel(context.Background()) + cancel() + done := make(chan struct{}) + + go func() { + RunFulltext2(newFulltext2ConsumerForTest(), ctx, errch, &MockRetriever{dtype: ISCPDataType_Snapshot}) + close(done) + }() + + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("RunFulltext2 blocked while reporting into a full error channel") + } + select { + case err := <-errch: + require.ErrorIs(t, err, existing) + default: + t.Fatal("the existing error was lost from a full error channel") + } +} diff --git a/pkg/sql/plan/apply_indices_fulltext.go b/pkg/sql/plan/apply_indices_fulltext.go index a7d9c8553cc89..ad97fe281ee48 100644 --- a/pkg/sql/plan/apply_indices_fulltext.go +++ b/pkg/sql/plan/apply_indices_fulltext.go @@ -1530,27 +1530,37 @@ func collectDrivingFullText2Matches(expr *plan.Expr, out []*plan.Expr) []*plan.E out = collectDrivingFullText2Matches(arg, out) } return out - case ">", ">=": + case ">", ">=", "<", "<=": if len(fn.Args) != 2 { return out } + op := fn.Func.ObjName matchSide, constSide := fn.Args[0], fn.Args[1] if monotoneWrappedFullTextMatch(matchSide) == nil { - // Reverse form: c < score / c <= score. + // Reverse form: c < score / c <= score. The other two + // constant-left forms are upper bounds and cannot drive an + // INNER JOIN because non-matching rows have score zero. matchSide, constSide = fn.Args[1], fn.Args[0] + switch op { + case "<": + op = ">" + case "<=": + op = ">=" + default: + return out + } } - if monotoneWrappedFullTextMatch(matchSide) == nil { + matchExpr := monotoneWrappedFullTextMatch(matchSide) + if matchExpr == nil { return out } v, ok := constValueAsFloat(constSide) if !ok || v < 0 { return out } - if fn.Func.ObjName == ">=" && v == 0 { - return out + if op == ">" || (op == ">=" && v > 0) { + return append(out, matchExpr) } - return append(out, monotoneWrappedFullTextMatch(matchSide)) - case "<", "<=": // Upper-bound-only MATCH cannot establish membership, so it is only lifted when a // separate bare MATCH already supplies the stream. return out diff --git a/pkg/sql/plan/apply_indices_fulltext2_test.go b/pkg/sql/plan/apply_indices_fulltext2_test.go index 162dff8c0a89b..37d42ac4fb936 100644 --- a/pkg/sql/plan/apply_indices_fulltext2_test.go +++ b/pkg/sql/plan/apply_indices_fulltext2_test.go @@ -358,6 +358,8 @@ func TestFulltext2DrivingMatchAndLiteralGuards(t *testing.T) { require.Len(t, collectDrivingFullText2Matches(fulltext2ComparisonExpr(">", matchExpr, value), nil), 1) require.Len(t, collectDrivingFullText2Matches(fulltext2ComparisonExpr(">=", matchExpr, value), nil), 1) + require.Len(t, collectDrivingFullText2Matches(fulltext2ComparisonExpr("<", value, matchExpr), nil), 1) + require.Len(t, collectDrivingFullText2Matches(fulltext2ComparisonExpr("<=", value, matchExpr), nil), 1) require.Len(t, collectDrivingFullText2Matches(fulltext2ComparisonExpr("and", fulltext2ComparisonExpr(">", matchExpr, value), fulltext2ComparisonExpr(">=", matchExpr, value)), nil), 2) @@ -365,6 +367,8 @@ func TestFulltext2DrivingMatchAndLiteralGuards(t *testing.T) { fulltext2ComparisonExpr(">=", matchExpr, makePlan2Float64ConstExprWithType(0)), fulltext2ComparisonExpr(">", matchExpr, makePlan2Float64ConstExprWithType(-1)), fulltext2ComparisonExpr("<", matchExpr, value), + fulltext2ComparisonExpr(">", value, matchExpr), + fulltext2ComparisonExpr(">=", value, matchExpr), fulltext2ComparisonExpr(">", matchExpr, makePlan2StringConstExprWithType("0.5")), fulltext2ComparisonExpr(">", makePlan2StringConstExprWithType("0.5"), value), } { From 4cce042c8d3ad2cbada59ddd24e287785c297c1a Mon Sep 17 00:00:00 2001 From: VioletQwQ-0 <1659648118@qq.com> Date: Thu, 27 Aug 2026 00:25:42 +0800 Subject: [PATCH 13/17] test(fulltext2): restore wrapped score and readiness BVT --- .../fulltext2_merge_forcesync.result | 2 +- .../fulltext2/fulltext2_showcreate.result | 8 +- .../fulltext2/fulltext2_wrapped_score.result | 267 ++++++++++++++++++ .../fulltext2/fulltext2_wrapped_score.sql | 205 ++++++++++++++ .../fulltext2/fulltext2_restore_clone.result | 78 ++++- .../fulltext2/fulltext2_restore_clone.sql | 87 +++++- 6 files changed, 624 insertions(+), 23 deletions(-) create mode 100644 test/distributed/cases/fulltext2/fulltext2_wrapped_score.result create mode 100644 test/distributed/cases/fulltext2/fulltext2_wrapped_score.sql diff --git a/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.result b/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.result index 3f9f5c9bc743b..94f0e5e3c57b1 100644 --- a/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.result +++ b/test/distributed/cases/fulltext2/fulltext2_merge_forcesync.result @@ -13,7 +13,7 @@ t ¦ CREATE TABLE `t` ( `id` bigint NOT NULL, `body` text DEFAULT NULL, PRIMARY KEY (`id`), - FULLTEXT2 `ft`(`body`) WITH PARSER ngram max_index_capacity = 2 + FULLTEXT2 `ft`(`body`) WITH PARSER ngram max_index_capacity = 2 ) select id from t where match(body) against('alpha' in bm25 mode) order by id; ➤ id[-5,64,0] 𝄀 diff --git a/test/distributed/cases/fulltext2/fulltext2_showcreate.result b/test/distributed/cases/fulltext2/fulltext2_showcreate.result index 86fe73387e6ee..6a675054f0dd6 100644 --- a/test/distributed/cases/fulltext2/fulltext2_showcreate.result +++ b/test/distributed/cases/fulltext2/fulltext2_showcreate.result @@ -10,7 +10,7 @@ t1 ¦ CREATE TABLE `t1` ( `id` bigint NOT NULL, `body` text DEFAULT NULL, PRIMARY KEY (`id`), - FULLTEXT2 `ft`(`body`) WITH PARSER ngram max_index_capacity = 2 + FULLTEXT2 `ft`(`body`) WITH PARSER ngram max_index_capacity = 2 ) create table t2 (id bigint primary key, body text); create fulltext2 index ft on t2(body) auto_update = true second 5; @@ -20,7 +20,7 @@ t2 ¦ CREATE TABLE `t2` ( `id` bigint NOT NULL, `body` text DEFAULT NULL, PRIMARY KEY (`id`), - FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true second = 5 + FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true second = 5 ) create table t3 (id bigint primary key, body text); create fulltext2 index ft on t3(body) auto_update = true day 1 hour 2; @@ -30,7 +30,7 @@ t3 ¦ CREATE TABLE `t3` ( `id` bigint NOT NULL, `body` text DEFAULT NULL, PRIMARY KEY (`id`), - FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true day = 1 hour = 2 + FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true day = 1 hour = 2 ) create table t4 ( id bigint NOT NULL, @@ -44,6 +44,6 @@ t4 ¦ CREATE TABLE `t4` ( `id` bigint NOT NULL, `body` text DEFAULT NULL, PRIMARY KEY (`id`), - FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true second = 5 + FULLTEXT2 `ft`(`body`) WITH PARSER ngram auto_update = true second = 5 ) drop database ft2_showcreate; diff --git a/test/distributed/cases/fulltext2/fulltext2_wrapped_score.result b/test/distributed/cases/fulltext2/fulltext2_wrapped_score.result new file mode 100644 index 0000000000000..5246821e40214 --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_wrapped_score.result @@ -0,0 +1,267 @@ +set ft2_relevancy_algorithm="BM25"; +set experimental_fulltext2_index = 1; +drop database if exists ft2_wrapped; +create database ft2_wrapped; +use ft2_wrapped; +create table docs(id int primary key, body text); +insert into docs values (1,'hello a'),(2,'other text'),(3,'hello hello b'); +create fulltext2 index ft on docs(body); +select id, match(body) against('hello') as sc from docs where match(body) against('hello') order by id; +➤ id[4,32,0] ¦ sc[7,5,0] 𝄀 +1 ¦ 0.03313846 𝄀 +3 ¦ 0.040571388 +select id, round(match(body) against('hello'), 3) as r from docs where match(body) against('hello') order by id; +➤ id[4,32,0] ¦ r[8,53,31] 𝄀 +1 ¦ 0.033 𝄀 +3 ¦ 0.041 +select id, cast(match(body) against('hello') as decimal(10,3)) as r from docs where match(body) against('hello') order by id; +➤ id[4,32,0] ¦ r[3,10,3] 𝄀 +1 ¦ 0.033 𝄀 +3 ¦ 0.041 +select id, match(body) against('hello') * 100 as r from docs where match(body) against('hello') order by id; +➤ id[4,32,0] ¦ r[8,53,31] 𝄀 +1 ¦ 3.31384614109993 𝄀 +3 ¦ 4.0571387857198715 +select id, round(match(body) against('hello') + 1, 3) as r from docs where match(body) against('hello') order by id; +➤ id[4,32,0] ¦ r[8,53,31] 𝄀 +1 ¦ 1.033 𝄀 +3 ¦ 1.041 +select id, round(match(body) against('hello'), 3) as r from docs where match(body) against('hello') order by r desc; +➤ id[4,32,0] ¦ r[8,53,31] 𝄀 +3 ¦ 0.041 𝄀 +1 ¦ 0.033 +explain select id, round(match(body) against('hello'), 3) as r from docs where match(body) against('hello'); +-- @regex("Table Function on fulltext2_search", true) +➤ TP QUERY PLAN[12,0,0] 𝄀 +Project 𝄀 + -> Sort 𝄀 + Sort Key: mo_fulltext_alias_0.__mo_ft_score DESC 𝄀 + -> Join 𝄀 + Join Type: INNER 𝄀 + Join Cond: (docs.id = mo_fulltext_alias_0.__mo_ft_doc_id) 𝄀 + Runtime Filter Build: #[-1,0] 𝄀 + -> Table Scan on ft2_wrapped.docs 𝄀 + Runtime Filter Probe: docs.id 𝄀 + -> Table Function on fulltext2_search +create view v as select id, match(body) against('hello') as sc from docs where match(body) against('hello'); +select id from v where sc > 0 order by id; -- baseline: every match +➤ id[4,32,0] 𝄀 +1 𝄀 +3 +select id from v where sc > 0.037 order by id; -- only the higher scorer +➤ id[4,32,0] 𝄀 +3 +select id from v where round(sc, 4) > 0.037 order by id; +➤ id[4,32,0] 𝄀 +3 +select id from v where cast(sc as double) > 0.037 order by id; +➤ id[4,32,0] 𝄀 +3 +select id from v where sc * 2 > 0.074 order by id; +➤ id[4,32,0] 𝄀 +3 +select id from v where sc > 0.037 and id > 1 order by id; +➤ id[4,32,0] 𝄀 +3 +select id from v where round(cast(sc as double) * 2, 3) > 0.074 order by id; +➤ id[4,32,0] 𝄀 +3 +explain select id from v where round(sc, 4) > 0; +-- @regex("Table Function on fulltext2_search", true) +➤ TP QUERY PLAN[12,0,0] 𝄀 +Project 𝄀 + -> Sort 𝄀 + Sort Key: mo_fulltext_alias_0.__mo_ft_score DESC 𝄀 + -> Filter 𝄀 + Filter Cond: (round(cast(mo_fulltext_alias_0.__mo_ft_score AS DOUBLE), 4) > 0) 𝄀 + -> Join 𝄀 + Join Type: INNER 𝄀 + Join Cond: (docs.id = mo_fulltext_alias_0.__mo_ft_doc_id) 𝄀 + Runtime Filter Build: #[-1,0] 𝄀 + -> Table Scan on ft2_wrapped.docs 𝄀 + Runtime Filter Probe: docs.id 𝄀 + -> Table Function on fulltext2_search +select id from v where sc > 0.037 order by id; -- only the higher-scoring row +➤ id[4,32,0] 𝄀 +3 +select id from v where sc > 0.5 order by id; -- none +➤ id[4,32,0] +select id from v where round(sc, 4) > 0.037 order by id; +➤ id[4,32,0] 𝄀 +3 +select id from v where cast(sc as double) > 0.5 order by id; +➤ id[4,32,0] +select id from ( +select id, match(body) against('hello') as sc from docs where match(body) against('hello') +) x where sc > 0 order by id; +➤ id[4,32,0] 𝄀 +1 𝄀 +3 +create table two(id int primary key, body text); +insert into two values (1,'hello'),(2,'hello hello'),(3,'hello hello hello'),(4,'world'),(5,'hello hello world'); +create fulltext2 index ft_two on two(body); +select id, match(body) against('hello') as h from two where match(body) against('hello') order by id; +➤ id[4,32,0] ¦ h[7,5,0] 𝄀 +1 ¦ 0.01211813 𝄀 +2 ¦ 0.013416502 𝄀 +3 ¦ 0.013913408 𝄀 +5 ¦ 0.011558833 +select id, match(body) against('world') as w from two where match(body) against('world') order by id; +➤ id[4,32,0] ¦ w[7,5,0] 𝄀 +4 ¦ 0.20433064 𝄀 +5 ¦ 0.12927042 +select id, round(match(body) against('hello'),3) as h, round(match(body) against('world'),3) as w +from two where match(body) against('hello') and match(body) against('world') order by id; +➤ id[4,32,0] ¦ h[8,53,31] ¦ w[8,53,31] 𝄀 +5 ¦ 0.012 ¦ 0.129 +select id, match(body) against('hello') as sc, round(match(body) against('hello'),3) as r +from two where match(body) against('hello') order by id; +➤ id[4,32,0] ¦ sc[7,5,0] ¦ r[8,53,31] 𝄀 +1 ¦ 0.01211813 ¦ 0.012 𝄀 +2 ¦ 0.013416502 ¦ 0.013 𝄀 +3 ¦ 0.013913408 ¦ 0.014 𝄀 +5 ¦ 0.011558833 ¦ 0.012 +select id, round(match(body) against('world'),3) as r from two where match(body) against('hello') order by id; +➤ id[4,32,0] ¦ r[8,53,31] 𝄀 +5 ¦ 0.129 +select id from two where match(body) against('hello') and match(body) against('world') > 0.1 order by id; +➤ id[4,32,0] 𝄀 +5 +select id from two where match(body) against('hello') and match(body) against('world') > 0.9 order by id; +➤ id[4,32,0] +select id, round(match(body) against('world'),3) as w from two +where match(body) against('hello') and match(body) against('world') +and match(body) against('world') > 0.1 order by id; +➤ id[4,32,0] ¦ w[8,53,31] 𝄀 +5 ¦ 0.129 +select id from two +where match(body) against('hello') and match(body) against('world') +and match(body) against('world') > 0.9 order by id; +➤ id[4,32,0] +select id from two where match(body) against('hello') order by round(match(body) against('hello'),3) desc, id; +➤ id[4,32,0] 𝄀 +3 𝄀 +2 𝄀 +1 𝄀 +5 +select id from two where match(body) against('hello') > 0.0132 order by id; -- drops the lower scorers +➤ id[4,32,0] 𝄀 +2 𝄀 +3 +select id from two where match(body) against('hello') > 0.9 order by id; -- none +➤ id[4,32,0] +select id, round(match(body) against('hello'),3) as r from two order by id; -- projection, no WHERE MATCH +➤ id[4,32,0] ¦ r[8,53,31] 𝄀 +1 ¦ 0.012 𝄀 +2 ¦ 0.013 𝄀 +3 ¦ 0.014 𝄀 +5 ¦ 0.012 +select count(*) as n from two where match(body) against('hello') > 0.0132; -- and through an aggregate +➤ n[-5,64,0] 𝄀 +2 +select count(*) as n from two where match(body) against('hello') > 0.9; +➤ n[-5,64,0] 𝄀 +0 +explain select id from two where match(body) against('hello') > 0.0132; +-- @regex("Table Function on fulltext2_search", true) +➤ TP QUERY PLAN[12,0,0] 𝄀 +Project 𝄀 + -> Sort 𝄀 + Sort Key: mo_fulltext_alias_0.__mo_ft_score DESC 𝄀 + -> Filter 𝄀 + Filter Cond: (cast(mo_fulltext_alias_0.__mo_ft_score AS DOUBLE) > 0.0132) + -- HINT: Cast expression may prevent index usage 𝄀 + -> Join 𝄀 + Join Type: INNER 𝄀 + Join Cond: (two.id = mo_fulltext_alias_0.__mo_ft_doc_id) 𝄀 + Runtime Filter Build: #[-1,0] 𝄀 + -> Table Scan on ft2_wrapped.two 𝄀 + Runtime Filter Probe: two.id 𝄀 + -> Table Function on fulltext2_search +create table nodx(id int primary key, txt text); +insert into nodx values (1,'hello'); +select id from nodx where match(txt) against('hello') > 0.1; +not supported: MATCH() AGAINST() function cannot be replaced by FULLTEXT INDEX and full table scan with fulltext search is not supported yet. +select id, match(body) against('hello') as sc from two where match(body) against('hello') order by id; +➤ id[4,32,0] ¦ sc[7,5,0] 𝄀 +1 ¦ 0.01211813 𝄀 +2 ¦ 0.013416502 𝄀 +3 ¦ 0.013913408 𝄀 +5 ¦ 0.011558833 +select id from two where match(body) against('hello') and match(body) against('hello') > 0.0125 order by id; +➤ id[4,32,0] 𝄀 +2 𝄀 +3 +select id from two where match(body) against('hello') and match(body) against('hello') < 0.0125 order by id; +➤ id[4,32,0] 𝄀 +1 𝄀 +5 +select id from two where match(body) against('hello') +and match(body) against('hello') > 0.0118 and match(body) against('hello') < 0.0138 order by id; +➤ id[4,32,0] 𝄀 +1 𝄀 +2 +select id from two where match(body) against('hello') and match(body) against('hello') >= 0.013416502 order by id; +➤ id[4,32,0] 𝄀 +3 +select id from two where match(body) against('hello') and match(body) against('hello') <= 0.013416502 order by id; +➤ id[4,32,0] 𝄀 +1 𝄀 +2 𝄀 +5 +select id from two where match(body) against('hello') and (match(body) against('hello') > 0.0138 or id = 1) order by id; +➤ id[4,32,0] 𝄀 +1 𝄀 +3 +explain select id from two where match(body) against('hello') and match(body) against('hello') > 0.0125; +-- @regex("Table Function on fulltext2_search", true) +➤ TP QUERY PLAN[12,0,0] 𝄀 +Project 𝄀 + -> Sort 𝄀 + Sort Key: mo_fulltext_alias_0.__mo_ft_score DESC 𝄀 + -> Filter 𝄀 + Filter Cond: (cast(mo_fulltext_alias_0.__mo_ft_score AS DOUBLE) > 0.0125) + -- HINT: Cast expression may prevent index usage 𝄀 + -> Join 𝄀 + Join Type: INNER 𝄀 + Join Cond: (two.id = mo_fulltext_alias_0.__mo_ft_doc_id) 𝄀 + Runtime Filter Build: #[-1,0] 𝄀 + -> Table Scan on ft2_wrapped.two 𝄀 + Runtime Filter Probe: two.id 𝄀 + -> Table Function on fulltext2_search +create table covpk(body varchar(100) primary key, tag int); +insert into covpk values ('hello',1),('hello hello',2),('hello hello hello',3),('world',4); +create fulltext2 index ci_covpk on covpk(body) include(tag); +select body, tag, match(body) against('hello') as sc from covpk where match(body) against('hello') order by body; +➤ body[12,75,0] ¦ tag[4,32,0] ¦ sc[7,5,0] 𝄀 +hello ¦ 1 ¦ 0.019339437 𝄀 +hello hello ¦ 2 ¦ 0.02132055 𝄀 +hello hello hello ¦ 3 ¦ 0.022074306 +select body, tag, round(match(body) against('hello'),3) as r from covpk where match(body) against('hello') order by body; +➤ body[12,75,0] ¦ tag[4,32,0] ¦ r[8,53,31] 𝄀 +hello ¦ 1 ¦ 0.019 𝄀 +hello hello ¦ 2 ¦ 0.021 𝄀 +hello hello hello ¦ 3 ¦ 0.022 +explain select body, tag, round(match(body) against('hello'),3) as r from covpk where match(body) against('hello'); +-- @regex("Table Function on fulltext2_search", true) +➤ TP QUERY PLAN[12,0,0] 𝄀 +Project 𝄀 + -> Sort 𝄀 + Sort Key: mo_fulltext_alias_0.__mo_ft_score DESC 𝄀 + -> Table Function on fulltext2_search +select id from two where match(body) against('hello') and match(body) against('hello') < 0.0118 limit 1; +➤ id[4,32,0] 𝄀 +5 +select id from two where match(body) against('hello') and match(body) against('hello') < 0.0118; +➤ id[4,32,0] 𝄀 +5 +select count(*) as n from ( +select id from two where match(body) against('hello') and match(body) against('hello') < 0.0132 limit 1 +) q; +➤ n[-5,64,0] 𝄀 +1 +select id from two where match(body) against('hello') and match(body) against('hello') > 0.0132 limit 1; +➤ id[4,32,0] 𝄀 +3 +drop database ft2_wrapped; +set ft2_relevancy_algorithm="BM25"; diff --git a/test/distributed/cases/fulltext2/fulltext2_wrapped_score.sql b/test/distributed/cases/fulltext2/fulltext2_wrapped_score.sql new file mode 100644 index 0000000000000..4624097d673ed --- /dev/null +++ b/test/distributed/cases/fulltext2/fulltext2_wrapped_score.sql @@ -0,0 +1,205 @@ +-- Relevance algorithm pinned FIRST: every score value and threshold below depends on it. +set ft2_relevancy_algorithm="BM25"; +-- MATCH() returns a FLOAT relevance score, so it can be wrapped like any other scalar +-- expression: round(match(...), 3), cast(match(...) as decimal), match(...) * 100, or a +-- comparison on it. The rewrite has to see THROUGH that wrapping, because the placeholder it +-- replaces has no evaluable implementation -- left inside a cast or a comparison it reaches +-- execution and raises "MATCH() AGAINST() function cannot be replaced by FULLTEXT INDEX". +-- +-- This is the fulltext twin of vector_ivf_wrapped_dist (#26961), which pins the same shapes +-- for wrapped vector distances; the expression walk is shared between the two. +-- +-- Every EXPLAIN asserts the index is still reached, so a regression that "fixes" these by +-- silently falling back to a scan fails here too. +set experimental_fulltext2_index = 1; + +drop database if exists ft2_wrapped; +create database ft2_wrapped; +use ft2_wrapped; + +create table docs(id int primary key, body text); +insert into docs values (1,'hello a'),(2,'other text'),(3,'hello hello b'); +create fulltext2 index ft on docs(body); + +-- ---------------- baseline: the bare score ------------------------------------ +select id, match(body) against('hello') as sc from docs where match(body) against('hello') order by id; + +-- ---------------- wrapped in the PROJECTION ----------------------------------- +select id, round(match(body) against('hello'), 3) as r from docs where match(body) against('hello') order by id; +select id, cast(match(body) against('hello') as decimal(10,3)) as r from docs where match(body) against('hello') order by id; +select id, match(body) against('hello') * 100 as r from docs where match(body) against('hello') order by id; +select id, round(match(body) against('hello') + 1, 3) as r from docs where match(body) against('hello') order by id; + +-- ordering by the wrapped alias must still rank by relevance +select id, round(match(body) against('hello'), 3) as r from docs where match(body) against('hello') order by r desc; + +-- @separator:table +-- @regex("Table Function on fulltext2_search", true) +explain select id, round(match(body) against('hello'), 3) as r from docs where match(body) against('hello'); + +-- ---------------- wrapped in a FILTER above a view ---------------------------- +-- Thresholds are chosen to EXCLUDE a row. `sc > 0` is satisfied by every matching row, so a +-- predicate that is silently never applied returns the same rows as a correct one -- which is +-- exactly how an earlier version of this fix passed while attaching the lifted predicate to a +-- plan field nothing evaluates. Keep one `> 0` as a baseline; the rest must change the row set. +-- Inlining substitutes the score alias with its definition and pushes it onto the base scan, +-- where the bare-match test cannot see it. It is lifted onto the join instead. +create view v as select id, match(body) against('hello') as sc from docs where match(body) against('hello'); + +select id from v where sc > 0 order by id; -- baseline: every match +select id from v where sc > 0.037 order by id; -- only the higher scorer +select id from v where round(sc, 4) > 0.037 order by id; +select id from v where cast(sc as double) > 0.037 order by id; +select id from v where sc * 2 > 0.074 order by id; +select id from v where sc > 0.037 and id > 1 order by id; +select id from v where round(cast(sc as double) * 2, 3) > 0.074 order by id; + +-- @separator:table +-- @regex("Table Function on fulltext2_search", true) +explain select id from v where round(sc, 4) > 0; + +-- ---------------- DISCRIMINATING thresholds ------------------------------------ +-- `sc > 0` is worthless as a test: every matching row satisfies it, so a predicate that is +-- silently never applied returns exactly the same rows as a correct one. That is how an +-- earlier version of this fix -- which attached the lifted predicate to a Node_JOIN +-- FilterList, a field nothing evaluates -- passed a whole suite of shapes while returning +-- rows the predicate excluded. These thresholds must CHANGE the row set. +select id from v where sc > 0.037 order by id; -- only the higher-scoring row +select id from v where sc > 0.5 order by id; -- none +select id from v where round(sc, 4) > 0.037 order by id; +select id from v where cast(sc as double) > 0.5 order by id; + +-- the same shape with no view at all +select id from ( + select id, match(body) against('hello') as sc from docs where match(body) against('hello') +) x where sc > 0 order by id; + +-- ---------------- WHICH match a wrapped score refers to ------------------------- +-- A wrapped MATCH is rewritten to the score of the index scan built for THAT match, compared +-- on pattern/mode/index parts (the same test that builds eqmap). An earlier version rewrote +-- by function NAME under a "only one served match" count guard, which handed a wrapped +-- against('world') the relevance of the accompanying where ... against('hello') -- a wrong +-- number, silently, on every row. +create table two(id int primary key, body text); +insert into two values (1,'hello'),(2,'hello hello'),(3,'hello hello hello'),(4,'world'),(5,'hello hello world'); +create fulltext2 index ft_two on two(body); + +-- per-term baselines: 'hello' and 'world' score differently, which is the whole point +select id, match(body) against('hello') as h from two where match(body) against('hello') order by id; +select id, match(body) against('world') as w from two where match(body) against('world') order by id; + +-- both matches served: each wrapped copy must report ITS OWN score, not the other's +select id, round(match(body) against('hello'),3) as h, round(match(body) against('world'),3) as w +from two where match(body) against('hello') and match(body) against('world') order by id; + +-- a bare AND a wrapped copy of the SAME match in one projection. The old count guard saw +-- "2 served matches" here (the filter and the projection copy, already collapsed onto one +-- index scan by eqmap) and skipped the sweep, so the wrapped copy threw. +select id, match(body) against('hello') as sc, round(match(body) against('hello'),3) as r +from two where match(body) against('hello') order by id; + +-- a wrapped MATCH on a DIFFERENT term is answered by a stream of its OWN, reporting its own +-- relevance. Note the row set: two streams are INNER JOINed, so only documents matching BOTH +-- survive -- the same restriction bare multi-stream MATCH already applies. +select id, round(match(body) against('world'),3) as r from two where match(body) against('hello') order by id; +select id from two where match(body) against('hello') and match(body) against('world') > 0.1 order by id; +select id from two where match(body) against('hello') and match(body) against('world') > 0.9 order by id; + +-- the same with the second match ALSO named bare: the wrapped predicate must reuse that +-- stream rather than build a second one, and still filter by the world score. +select id, round(match(body) against('world'),3) as w from two +where match(body) against('hello') and match(body) against('world') + and match(body) against('world') > 0.1 order by id; +select id from two +where match(body) against('hello') and match(body) against('world') + and match(body) against('world') > 0.9 order by id; + +-- ORDER BY a wrapped MATCH with no alias to hide behind +select id from two where match(body) against('hello') order by round(match(body) against('hello'),3) desc, id; + +-- ---------------- a wrapped MATCH drives the rewrite on its own ------------------ +-- Discovery recognises only a BARE fulltext_match at a top-level position, so a query whose +-- ONLY match is wrapped never entered the rewrite and raised 20105: a wrapped MATCH could +-- work solely as a passenger, alongside a bare MATCH with identical arguments to drive it. +-- getWrappedFullTextMatches registers it as its own stream instead. +select id from two where match(body) against('hello') > 0.0132 order by id; -- drops the lower scorers +select id from two where match(body) against('hello') > 0.9 order by id; -- none +select id, round(match(body) against('hello'),3) as r from two order by id; -- projection, no WHERE MATCH +select count(*) as n from two where match(body) against('hello') > 0.0132; -- and through an aggregate +select count(*) as n from two where match(body) against('hello') > 0.9; + +-- @separator:table +-- @regex("Table Function on fulltext2_search", true) +explain select id from two where match(body) against('hello') > 0.0132; + +-- a MATCH no INDEX can serve is still left alone and still raises 20105 +create table nodx(id int primary key, txt text); +insert into nodx values (1,'hello'); +select id from nodx where match(txt) against('hello') > 0.1; + +-- ---------------- score range pushed into the engine ----------------------------- +-- fulltext2 scores documents itself, so an AND-reachable `MATCH(...) const` is handed to +-- the search as a relevance interval and out-of-range documents never cross into the join. +-- The planner keeps its own Filter as well and widens each pushed bound outward by one float32 +-- ULP (SQL compares in double, the engine scores in float32), so the pushdown can only remove +-- work, never rows -- these must return exactly what the Filter alone would. +select id, match(body) against('hello') as sc from two where match(body) against('hello') order by id; + +select id from two where match(body) against('hello') and match(body) against('hello') > 0.0125 order by id; +select id from two where match(body) against('hello') and match(body) against('hello') < 0.0125 order by id; +select id from two where match(body) against('hello') + and match(body) against('hello') > 0.0118 and match(body) against('hello') < 0.0138 order by id; + +-- inclusive bounds +select id from two where match(body) against('hello') and match(body) against('hello') >= 0.013416502 order by id; +select id from two where match(body) against('hello') and match(body) against('hello') <= 0.013416502 order by id; + +-- an OR is NOT pushed (the predicate need not hold for a returned row); the rows still come +-- back correctly, just filtered above the join +select id from two where match(body) against('hello') and (match(body) against('hello') > 0.0138 or id = 1) order by id; + +-- the index must still be used +-- @separator:table +-- @regex("Table Function on fulltext2_search", true) +explain select id from two where match(body) against('hello') and match(body) against('hello') > 0.0125; + +-- ---------------- wrapped MATCH on the COVERED fast path ------------------------- +-- tryApplyCoveredFulltext2 drops the base-table JOIN and reads pk/score/include columns +-- straight from the TVF, remapping only the projections it recognised as bare MATCHes -- a +-- wrapped one was left behind to throw. It needs the same sweep as the JOIN path. +-- +-- Reaching the covered path with a wrapped MATCH needs the MATCH's column to count as covered, +-- and an indexed text column is rejected as an INCLUDE column, so the pk is the only way in. +create table covpk(body varchar(100) primary key, tag int); +insert into covpk values ('hello',1),('hello hello',2),('hello hello hello',3),('world',4); +create fulltext2 index ci_covpk on covpk(body) include(tag); + +-- baseline, then the wrapped form: the scores must be the same numbers +select body, tag, match(body) against('hello') as sc from covpk where match(body) against('hello') order by body; +select body, tag, round(match(body) against('hello'),3) as r from covpk where match(body) against('hello') order by body; + +-- and it must still be the covered plan, not a fallback to the JOIN +-- @separator:table +-- @regex("Table Function on fulltext2_search", true) +explain select body, tag, round(match(body) against('hello'),3) as r from covpk where match(body) against('hello'); + +-- ---------------- lifted predicate vs the candidate LIMIT ------------------------ +-- Lifting a wrapped predicate off the scan empties FilterList, which used to re-enable the +-- LIMIT pushdown into the fulltext TVF. The predicate runs ABOVE the join, so a capped stream +-- hands it only the top-relevance rows: the threshold below capped the stream to the single +-- highest-scoring document and then rejected it, returning nothing while id 5 qualified. +-- Under BM25 length normalisation id 5 is the UNIQUE LOWEST scorer -- with a surviving cap +-- these return 0 rows. +select id from two where match(body) against('hello') and match(body) against('hello') < 0.0118 limit 1; +select id from two where match(body) against('hello') and match(body) against('hello') < 0.0118; +select count(*) as n from ( + select id from two where match(body) against('hello') and match(body) against('hello') < 0.0132 limit 1 +) q; +-- positive control: LIMIT still works at the top of the range, where the cap agreed anyway +select id from two where match(body) against('hello') and match(body) against('hello') > 0.0132 limit 1; + +drop database ft2_wrapped; + +-- Restore the default so this case does not leak its setting to whatever runs next -- +-- the failure mode that made an earlier version of this file order-dependent. +set ft2_relevancy_algorithm="BM25"; diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.result b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.result index c430fc40c72ad..4ad09e96358c4 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.result +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.result @@ -6,9 +6,28 @@ create table src(id int primary key, body text, FULLTEXT2 ftidx(body)); insert into src values (1,'alpha keyword'),(2,'beta keyword'), (3,'gamma topic'),(4,'delta topic'); -select sleep(30); -sleep(30) -0 +set @src_ft2_index = ( +select index_table_name +from mo_catalog.mo_indexes +where name = 'ftidx' +and algo = 'fulltext2' +and algo_table_type = 'ftv2_index' +and table_id in ( +select rel_id +from mo_catalog.mo_tables +where reldatabase = database() and relname = 'src' +) +limit 1 +); +set @src_ft2_ready_sql = concat( +'select coalesce(max(chunk_id), -1) >= 0 as ready from `', database(), '`.`', @src_ft2_index, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_src_ft2 from @src_ft2_ready_sql; +execute wait_src_ft2; +ready +1 +deallocate prepare wait_src_ft2; select id from src where match(body) against('alpha' in boolean mode) order by id; id 1 @@ -20,10 +39,32 @@ count(*) show create table dst; Table Create Table dst CREATE TABLE `dst` (\n `id` int NOT NULL,\n `body` text DEFAULT NULL,\n PRIMARY KEY (`id`),\n FULLTEXT2 `ftidx`(`body`) WITH PARSER ngram\n) +set @dst_ft2_index = ( +select index_table_name +from mo_catalog.mo_indexes +where name = 'ftidx' +and algo = 'fulltext2' +and algo_table_type = 'ftv2_index' +and table_id in ( +select rel_id +from mo_catalog.mo_tables +where reldatabase = database() and relname = 'dst' +) +limit 1 +); insert into dst values (5,'epsilon fresh'); -select sleep(45); -sleep(45) -0 +set @dst_ft2_ready_sql = concat( +'select coalesce(max(d.chunk_id), -1) > coalesce((select max(s.chunk_id) from `', +database(), '`.`', @src_ft2_index, +'` s where s.index_id = ''cdc_tail'' and s.tag = 1), -1) as ready from `', +database(), '`.`', @dst_ft2_index, +'` d where d.index_id = ''cdc_tail'' and d.tag = 1' +); +prepare wait_dst_ft2 from @dst_ft2_ready_sql; +execute wait_dst_ft2; +ready +1 +deallocate prepare wait_dst_ft2; select id from dst where match(body) against('beta' in boolean mode) order by id; id 2 @@ -40,9 +81,28 @@ id create snapshot ft2_sp for account; drop table src; restore database sys.ft2_restore {snapshot="ft2_sp"}; -select sleep(30); -sleep(30) -0 +set @restored_ft2_index = ( +select index_table_name +from mo_catalog.mo_indexes +where name = 'ftidx' +and algo = 'fulltext2' +and algo_table_type = 'ftv2_index' +and table_id in ( +select rel_id +from mo_catalog.mo_tables +where reldatabase = database() and relname = 'src' +) +limit 1 +); +set @restored_ft2_ready_sql = concat( +'select coalesce(max(chunk_id), -1) >= 0 as ready from `', database(), '`.`', @restored_ft2_index, +'` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_restored_ft2 from @restored_ft2_ready_sql; +execute wait_restored_ft2; +ready +1 +deallocate prepare wait_restored_ft2; select id from src where match(body) against('alpha' in boolean mode) order by id; id 1 diff --git a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.sql b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.sql index 209272f7d8f98..3d3eb12ec2033 100644 --- a/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.sql +++ b/test/distributed/cases/pessimistic_transaction/fulltext2/fulltext2_restore_clone.sql @@ -27,8 +27,32 @@ insert into src values (1,'alpha keyword'),(2,'beta keyword'), (3,'gamma topic'),(4,'delta topic'); --- let the async CDC build finish, then confirm the source index answers MATCH -select sleep(30); +-- Wait for the initial CDC sync before the first MATCH. Do not poll MATCH here: +-- fulltext2's per-CN search cache is not cross-invalidated by the CDC consumer, +-- so an early empty MATCH would pin a stale snapshot for later queries. Poll the +-- hidden storage table instead; its cdc_tail row is the CDC build's durable +-- readiness signal. +set @src_ft2_index = ( + select index_table_name + from mo_catalog.mo_indexes + where name = 'ftidx' + and algo = 'fulltext2' + and algo_table_type = 'ftv2_index' + and table_id in ( + select rel_id + from mo_catalog.mo_tables + where reldatabase = database() and relname = 'src' + ) + limit 1 +); +set @src_ft2_ready_sql = concat( + 'select coalesce(max(chunk_id), -1) >= 0 as ready from `', database(), '`.`', @src_ft2_index, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_src_ft2 from @src_ft2_ready_sql; +-- @wait_expect(2, 30) +execute wait_src_ft2; +deallocate prepare wait_src_ft2; select id from src where match(body) against('alpha' in boolean mode) order by id; -- ================= CLONE (gate auto-skipped: flag toggled OFF) ================= @@ -39,6 +63,19 @@ create table dst clone src; select count(*) from dst; show create table dst; +set @dst_ft2_index = ( + select index_table_name + from mo_catalog.mo_indexes + where name = 'ftidx' + and algo = 'fulltext2' + and algo_table_type = 'ftv2_index' + and table_id in ( + select rel_id + from mo_catalog.mo_tables + where reldatabase = database() and relname = 'dst' + ) + limit 1 +); -- (c) maintenance re-armed: insert a row AFTER the clone. Done BEFORE any dst MATCH -- so the clone's per-CN index cache is first loaded fresh (with the new row present). -- fulltext2's index cache is not cross-CN invalidated by the CDC consumer, so a dst @@ -46,9 +83,20 @@ show create table dst; -- 'epsilon' query would read it stale on multi-CN. insert into dst values (5,'epsilon fresh'); --- let RestoreInitSQL's re-registered CDC + the post-clone insert settle on the clone -select sleep(45); - +-- The clone copies the source tail. Wait until the destination tail advances past +-- the source tail, which proves the post-clone insert was persisted without relying +-- on a hard-coded chunk number. +set @dst_ft2_ready_sql = concat( + 'select coalesce(max(d.chunk_id), -1) > coalesce((select max(s.chunk_id) from `', + database(), '`.`', @src_ft2_index, + '` s where s.index_id = ''cdc_tail'' and s.tag = 1), -1) as ready from `', + database(), '`.`', @dst_ft2_index, + '` d where d.index_id = ''cdc_tail'' and d.tag = 1' +); +prepare wait_dst_ft2 from @dst_ft2_ready_sql; +-- @wait_expect(2, 45) +execute wait_dst_ft2; +deallocate prepare wait_dst_ft2; -- (b) the cloned index answers MATCH on the copied rows ... select id from dst where match(body) against('beta' in boolean mode) order by id; select id from dst where match(body) against('topic' in boolean mode) order by id; @@ -63,12 +111,33 @@ create snapshot ft2_sp for account; -- mutate after the snapshot: drop the indexed source table drop table src; --- restore the database from the snapshot -> rebuilds the fulltext2 index from the --- restored rows (flag is OFF, but restore replays the existing index) +-- restore the database from the snapshot -> restores the rows and the existing +-- fulltext2 index (flag is OFF, but restore replays the existing index). restore database sys.ft2_restore {snapshot="ft2_sp"}; --- the restored index answers MATCH -select sleep(30); +-- Wait before the first MATCH after restore until the copied CDC tail is visible, +-- so the cache is loaded from the complete restored index. +set @restored_ft2_index = ( + select index_table_name + from mo_catalog.mo_indexes + where name = 'ftidx' + and algo = 'fulltext2' + and algo_table_type = 'ftv2_index' + and table_id in ( + select rel_id + from mo_catalog.mo_tables + where reldatabase = database() and relname = 'src' + ) + limit 1 +); +set @restored_ft2_ready_sql = concat( + 'select coalesce(max(chunk_id), -1) >= 0 as ready from `', database(), '`.`', @restored_ft2_index, + '` where index_id = ''cdc_tail'' and tag = 1' +); +prepare wait_restored_ft2 from @restored_ft2_ready_sql; +-- @wait_expect(2, 30) +execute wait_restored_ft2; +deallocate prepare wait_restored_ft2; select id from src where match(body) against('alpha' in boolean mode) order by id; select id from src where match(body) against('keyword' in boolean mode) order by id; From 0bbe3caa0948e77b8a5b159ebe34c7e885433cce Mon Sep 17 00:00:00 2001 From: VioletQwQ-0 <1659648118@qq.com> Date: Thu, 27 Aug 2026 00:26:39 +0800 Subject: [PATCH 14/17] docs(fulltext2): refresh 4.2-dev backport manifest --- docs/fulltext2-backport-4.2dev-manifest.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/fulltext2-backport-4.2dev-manifest.md b/docs/fulltext2-backport-4.2dev-manifest.md index 4701dff584886..8cc6df7f811ad 100644 --- a/docs/fulltext2-backport-4.2dev-manifest.md +++ b/docs/fulltext2-backport-4.2dev-manifest.md @@ -6,18 +6,21 @@ source and target refs are intentionally fixed for this run; later commits on | source | scope | local commit | | --- | --- | --- | -| `1d7b6311ce91df0968da435bb405f3d68137c595` (#25904) | FULLTEXT2 engine, parser/catalog contracts, plugin/CDC, planner/DDL and tests | `8330936547`, `7ce6f41954`, `cb2c363023`, `fb4eb5b499` | -| `ec591dc9d4ec6b3beb81b804dac11aceb9b66441` (#27165) | delayed phrase-position decode after document alignment | `1a8274faf5` | -| `3da66f2ca28d62f5e657c46d90b7cde9475486d0` (#27171), selective FT2 portion | wrapped FT2 score expressions, argument-aware score resolution, AND-reachable score ranges, and conservative float32 bounds; classic/view/vector changes from the source commit are excluded | `64515ad29e` | -| `6dccc954a1a32c12a2114328c752f15cf97ef2ba` (#27461) | cold-load reason/latency observation and cache-load lifecycle | `e3f25f079c` | -| `b8aa3f7f7351aacaaa37da27891d8f8d85e2e44d` (#27462) | immutable base/tail reuse across cache generations | `e3f25f079c` | -| `e07a7a792749db4228c1acf96d72827161747e60` (#27598) | FULLTEXT2 BVT readiness polling in place of fixed sleeps | `30e026c0b2` | +| `1d7b6311ce91df0968da435bb405f3d68137c595` (#25904) | FULLTEXT2 engine, parser/catalog contracts, plugin/CDC, planner/DDL and tests | `966acb2a92bb740e57b0b7a181df2d21956e1d3`, `0eb9a921ece274c956447640b41ad2a255079e8d`, `403a99fb65304e75b82e75793448f0690a871d0e`, `bfa83f42737ca6304c0af3f6913e70ccc961464` | +| `ec591dc9d4ec6b3beb81b804dac11aceb9b66441` (#27165) | delayed phrase-position decode after document alignment | `2b142144c3c24a2100d7d313e952717025d56aeb` | +| `3da66f2ca28d62f5e657c46d90b7cde9475486d0` (#27171), selective FT2 portion | wrapped FT2 score expressions, argument-aware score resolution, AND-reachable score ranges, and conservative float32 bounds; classic/view/vector changes from the source commit are excluded | `835e046faa9242dbe288bd72364a475e2132bbed` | +| `6dccc954a1a32c12a2114328c752f15cf97ef2ba` (#27461) | cold-load reason/latency observation and cache-load lifecycle | `e4abf5e90d0b5e09590571ca78447e50806527c` | +| `b8aa3f7f7351aacaaa37da27891d8f8d85e2e44d` (#27462) | immutable base/tail reuse across cache generations | `e4abf5e90d0b5e09590571ca78447e50806527c` | +| `e07a7a792749db4228c1acf96d72827161747e60` (#27598) | FULLTEXT2 BVT readiness polling in place of fixed sleeps | `e3a7440e2ac32bac780669f33c656960e22cff22` | +| current validation and review repairs | restore execution-level wrapped-score coverage, repair reverse score-bound membership and ISCP error propagation, and remove fixed-sleep/whitespace gate failures | `35f910dd543e1d3a952e4bd1a1b9852558ee2e27`, `fbea073c3b55836317c7aeb49d090ed2167f004c`, `06ab54d812ffcae1b4910e74f2f42a85ae9dfe0b`, `4cce042c8d3ad2cbada59ddd24e287785c297c1a` | ## Ref closure - Source reference: `upstream/main` = `8e616a693458652cc26b3c4be45c6602dff298d4` - Target reference: `upstream/4.2-dev` = `d2393868a7aaa6343518d80849fe4696aa3577e9` - Branch: `backport/fulltext2-main-8e616a6-4.2dev-d239386` +- The repair/BVT commits above are ancestors of the manifest update; the exact + post-update branch head is recorded in the PR and the final remote reread. - The target branch's existing parser/PERFORM and pipe changes were preserved; the generated MySQL parser was regenerated from the merged 4.2 grammar. From a8476d6dc80234b7cb269e076ac1b38595bce887 Mon Sep 17 00:00:00 2001 From: VioletQwQ-0 <1659648118@qq.com> Date: Thu, 27 Aug 2026 02:24:36 +0800 Subject: [PATCH 15/17] fix(fulltext2): trim show create option padding --- pkg/sql/plan/apply_indices_fulltext2_test.go | 2 +- pkg/sql/plan/build_show_util.go | 2 +- pkg/sql/plan/build_show_util_test.go | 22 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/pkg/sql/plan/apply_indices_fulltext2_test.go b/pkg/sql/plan/apply_indices_fulltext2_test.go index 37d42ac4fb936..d23487ce60409 100644 --- a/pkg/sql/plan/apply_indices_fulltext2_test.go +++ b/pkg/sql/plan/apply_indices_fulltext2_test.go @@ -421,7 +421,7 @@ func TestFulltext2WrappedMatchDiscovery(t *testing.T) { classicBuilder, _, classicProject, classicScan, _ := newFulltext2CoveredFixture(t, false, false) classicScan.TableDef.Indexes[0].IndexAlgo = catalog.MOIndexFullTextAlgo.ToString() classicMatch := ft2TestMatch("classic", 0, classicScan.BindingTags[0], "body") - classicProject.ProjectList = []*plan.Expr{&plan.Expr{Expr: &plan.Expr_F{F: &plan.Function{ + classicProject.ProjectList = []*plan.Expr{{Expr: &plan.Expr_F{F: &plan.Function{ Func: &plan.ObjectRef{ObjName: "round"}, Args: []*plan.Expr{ft2TestExpr(classicMatch)}, }}}} diff --git a/pkg/sql/plan/build_show_util.go b/pkg/sql/plan/build_show_util.go index 9873226c7788c..585a1edb0b398 100644 --- a/pkg/sql/plan/build_show_util.go +++ b/pkg/sql/plan/build_show_util.go @@ -302,7 +302,7 @@ func constructCreateTableSQL( if err != nil { return "", nil, err } - indexStr += paramStr + indexStr += strings.TrimRight(paramStr, " ") } else { val, err = sonic.Get([]byte(indexdef.IndexAlgoParams), catalog.Async) // ignore err != nil --> value not found diff --git a/pkg/sql/plan/build_show_util_test.go b/pkg/sql/plan/build_show_util_test.go index 122dc2d8370c4..7df4e9eac12f9 100644 --- a/pkg/sql/plan/build_show_util_test.go +++ b/pkg/sql/plan/build_show_util_test.go @@ -258,6 +258,28 @@ func Test_ShowCreateTableUsesIncludedColumnsFromIndexDef(t *testing.T) { require.Contains(t, got, "KEY `idx_vec` USING ivfflat (`embedding`) lists = 2 op_type 'vector_l2_ops' INCLUDE (`title`, `category`)") } +func Test_ShowCreateTableTrimsFulltext2OptionPadding(t *testing.T) { + mock := NewMockOptimizer(false) + tableDef, err := buildTestCreateTableStmt(mock, `CREATE TABLE fulltext2_padding ( + id BIGINT NOT NULL, + body TEXT, + PRIMARY KEY (id) + )`) + require.NoError(t, err) + + tableDef.Indexes = append(tableDef.Indexes, &plan.IndexDef{ + IndexName: "ft", + Parts: []string{"body"}, + IndexAlgo: catalog.MoIndexFullText2Algo.ToString(), + IndexAlgoParams: `{"parser":"ngram","max_index_capacity":"2"}`, + }) + + got, _, err := ConstructCreateTableSQL(&mock.ctxt, tableDef, nil, false, nil) + require.NoError(t, err) + require.Contains(t, got, " FULLTEXT2 `ft`(`body`) WITH PARSER ngram max_index_capacity = 2\n)") + require.NotContains(t, got, "max_index_capacity = 2 \n)") +} + func Test_ShowCreateTableQuotesIncludedColumns(t *testing.T) { mock := NewMockOptimizer(false) tableDef, err := buildTestCreateTableStmt(mock, `CREATE TABLE vector_src_reserved ( From 772417a0fe3e53420ba46adaeb1c7ffc5c3eba3a Mon Sep 17 00:00:00 2001 From: VioletQwQ-0 <1659648118@qq.com> Date: Thu, 27 Aug 2026 09:23:51 +0800 Subject: [PATCH 16/17] fix(plan): rewrite wrapped fulltext2 join child filters --- pkg/sql/plan/apply_indices_fulltext.go | 7 +- pkg/sql/plan/apply_indices_fulltext2_test.go | 127 ++++++++++++++++++ .../fulltext2/fulltext2_wrapped_score.result | 15 +++ .../fulltext2/fulltext2_wrapped_score.sql | 11 ++ 4 files changed, 158 insertions(+), 2 deletions(-) diff --git a/pkg/sql/plan/apply_indices_fulltext.go b/pkg/sql/plan/apply_indices_fulltext.go index ad97fe281ee48..ba2b1f53c7816 100644 --- a/pkg/sql/plan/apply_indices_fulltext.go +++ b/pkg/sql/plan/apply_indices_fulltext.go @@ -1212,12 +1212,13 @@ func (builder *QueryBuilder) applyFullTextFiltersForJoinChildren(nodeID int32, j func (builder *QueryBuilder) applyFullTextFiltersForScanInJoin(nodeID int32, scanNode *plan.Node, colRefCnt map[[2]int32]int, idxColMap map[[2]int32]*plan.Expr) (int32, bool, error) { filterids, filterIndexDefs := builder.getFullTextMatchFiltersFromScanNode(scanNode) - if len(filterids) == 0 { + wrappedExprs, wrappedIndexDefs := builder.getWrappedFullText2Matches(nil, scanNode, filterids, nil) + if len(filterids) == 0 && len(wrappedExprs) == 0 { return scanNode.NodeId, false, nil } ctxNodeID := builder.fullTextRewriteContextNodeID(nodeID, scanNode) - newNodeID, _, _, err := builder.applyJoinFullTextIndices( + newNodeID, _, _, _, err := builder.applyJoinFullTextIndicesWithWrapped( ctxNodeID, nil, scanNode, @@ -1227,6 +1228,8 @@ func (builder *QueryBuilder) applyFullTextFiltersForScanInJoin(nodeID int32, sca filterIndexDefs, nil, nil, + wrappedExprs, + wrappedIndexDefs, map[int32]int32{}, colRefCnt, idxColMap, diff --git a/pkg/sql/plan/apply_indices_fulltext2_test.go b/pkg/sql/plan/apply_indices_fulltext2_test.go index d23487ce60409..b52df78bc5481 100644 --- a/pkg/sql/plan/apply_indices_fulltext2_test.go +++ b/pkg/sql/plan/apply_indices_fulltext2_test.go @@ -15,6 +15,7 @@ package plan import ( + "context" "encoding/json" "testing" @@ -271,6 +272,132 @@ func TestFulltext2CoveredProjectionUsesTVFOutputs(t *testing.T) { } } +func newFulltext2JoinChildFixture(t *testing.T, matchOnRight bool) (*QueryBuilder, int32) { + t.Helper() + + builder := NewQueryBuilder(plan.Query_SELECT, NewMockCompilerContext(true), false, true) + ctx := NewBindContext(builder, nil) + newTableDef := func(name string) *plan.TableDef { + indexName := "ft2_" + name + storage := &plan.IndexDef{ + IndexName: indexName, + IndexAlgo: catalog.MoIndexFullText2Algo.ToString(), + IndexAlgoTableType: catalog.FullText2Index_TblType_Storage, + IndexTableName: "__" + indexName + "_store", + IndexAlgoParams: `{"parser":"ngram"}`, + Parts: []string{"body"}, + TableExist: true, + } + metadata := &plan.IndexDef{ + IndexName: indexName, + IndexAlgo: storage.IndexAlgo, + IndexAlgoTableType: catalog.FullText2Index_TblType_Metadata, + IndexTableName: "__" + indexName + "_meta", + IndexAlgoParams: storage.IndexAlgoParams, + Parts: []string{"body"}, + TableExist: true, + } + return &plan.TableDef{ + Name: name, + Cols: []*plan.ColDef{ + {Name: "id", Typ: plan.Type{Id: int32(types.T_int64), Width: 64}}, + {Name: "body", Typ: plan.Type{Id: int32(types.T_text)}}, + }, + Name2ColIndex: map[string]int32{"id": 0, "body": 1}, + Pkey: &plan.PrimaryKeyDef{PkeyColName: "id", Names: []string{"id"}}, + Indexes: []*plan.IndexDef{storage, metadata}, + } + } + + leftDef := newTableDef("left_docs") + rightDef := newTableDef("right_docs") + leftTag := builder.genNewBindTag() + rightTag := builder.genNewBindTag() + matchTag := leftTag + if matchOnRight { + matchTag = rightTag + } + match := ft2TestMatch("hello", 0, matchTag, "body") + match.Args[2].GetCol().ColPos = 1 + wrapped := fulltext2ComparisonExpr(">", ft2TestExpr(match), makePlan2Float64ConstExprWithType(0.01)) + newScan := func(tableDef *plan.TableDef, tag int32, filters []*plan.Expr) *plan.Node { + return &plan.Node{ + NodeType: plan.Node_TABLE_SCAN, + TableDef: tableDef, + ObjRef: &plan.ObjectRef{SchemaName: "db", ObjName: tableDef.Name}, + BindingTags: []int32{tag}, + FilterList: filters, + Stats: &plan.Stats{TableCnt: 1000, Outcnt: 100, Selectivity: 0.1, Cost: 1000}, + } + } + var leftFilters, rightFilters []*plan.Expr + if matchOnRight { + rightFilters = []*plan.Expr{wrapped} + } else { + leftFilters = []*plan.Expr{wrapped} + } + leftScanID := builder.appendNode(newScan(leftDef, leftTag, leftFilters), ctx) + rightScanID := builder.appendNode(newScan(rightDef, rightTag, rightFilters), ctx) + joinCond, err := BindFuncExprImplByPlanExpr(context.Background(), "=", []*plan.Expr{ + fulltext2ColumnExpr(leftTag, 0, "id", leftDef.Cols[0].Typ), + fulltext2ColumnExpr(rightTag, 0, "id", rightDef.Cols[0].Typ), + }) + require.NoError(t, err) + joinID := builder.appendNode(&plan.Node{ + NodeType: plan.Node_JOIN, + Children: []int32{leftScanID, rightScanID}, + JoinType: plan.Node_INNER, + OnList: []*plan.Expr{joinCond}, + }, ctx) + return builder, joinID +} + +func countFulltext2SearchNodes(builder *QueryBuilder, nodeID int32) int { + if nodeID < 0 || int(nodeID) >= len(builder.qry.Nodes) || builder.qry.Nodes[nodeID] == nil { + return 0 + } + node := builder.qry.Nodes[nodeID] + count := 0 + if node.NodeType == plan.Node_FUNCTION_SCAN && node.TableDef != nil && node.TableDef.TblFunc != nil && + node.TableDef.TblFunc.Name == fulltext2_search_func_name { + count++ + } + for _, childID := range node.Children { + count += countFulltext2SearchNodes(builder, childID) + } + return count +} + +func TestFulltext2WrappedMatchJoinChild(t *testing.T) { + for _, matchOnRight := range []bool{false, true} { + t.Run(map[bool]string{false: "left", true: "right"}[matchOnRight], func(t *testing.T) { + builder, joinID := newFulltext2JoinChildFixture(t, matchOnRight) + joinNode := builder.qry.Nodes[joinID] + originalMatchedScanID := joinNode.Children[0] + if matchOnRight { + originalMatchedScanID = joinNode.Children[1] + } + + newID, err := builder.applyIndicesForJoins(joinID, joinNode, map[[2]int32]int{}, map[[2]int32]*plan.Expr{}) + require.NoError(t, err) + require.Equal(t, joinID, newID) + + matchedChildID := builder.qry.Nodes[joinID].Children[0] + if matchOnRight { + matchedChildID = builder.qry.Nodes[joinID].Children[1] + } + require.NotEqual(t, originalMatchedScanID, matchedChildID) + matchedChild := builder.qry.Nodes[matchedChildID] + require.Equal(t, plan.Node_FILTER, matchedChild.NodeType) + require.Len(t, matchedChild.FilterList, 1) + require.False(t, exprCallsFunc(matchedChild.FilterList[0], "fulltext_match")) + require.Len(t, matchedChild.Children, 1) + require.Equal(t, plan.Node_JOIN, builder.qry.Nodes[matchedChild.Children[0]].NodeType) + require.Equal(t, 1, countFulltext2SearchNodes(builder, matchedChildID)) + }) + } +} + func TestFulltext2CoveredProjectionDeclinesUncoveredColumn(t *testing.T) { builder, projectID, project, scan, idxdef := newFulltext2CoveredFixture(t, false, false) project.ProjectList = append(project.ProjectList, fulltext2ColumnExpr(scan.BindingTags[0], 1, "body", scan.TableDef.Cols[1].Typ)) diff --git a/test/distributed/cases/fulltext2/fulltext2_wrapped_score.result b/test/distributed/cases/fulltext2/fulltext2_wrapped_score.result index 5246821e40214..4eba1a985fc09 100644 --- a/test/distributed/cases/fulltext2/fulltext2_wrapped_score.result +++ b/test/distributed/cases/fulltext2/fulltext2_wrapped_score.result @@ -162,6 +162,21 @@ select count(*) as n from two where match(body) against('hello') > 0.0132; -- select count(*) as n from two where match(body) against('hello') > 0.9; ➤ n[-5,64,0] 𝄀 0 +create table join_peer(id int primary key); +insert into join_peer values (1),(2),(3),(4),(5); +select t.id from two t join join_peer p on t.id = p.id where match(t.body) against('hello') > 0.0132 order by t.id; +➤ id[4,32,0] 𝄀 +2 𝄀 +3 +select t.id from two t join join_peer p on t.id = p.id where match(t.body) against('hello') > 0.9 order by t.id; +➤ id[4,32,0] +select t.id from join_peer p join two t on p.id = t.id where match(t.body) against('hello') > 0.0132 order by t.id; +➤ id[4,32,0] 𝄀 +2 𝄀 +3 +select t.id from join_peer p join two t on p.id = t.id where match(t.body) against('hello') > 0.9 order by t.id; +➤ id[4,32,0] +drop table join_peer; explain select id from two where match(body) against('hello') > 0.0132; -- @regex("Table Function on fulltext2_search", true) ➤ TP QUERY PLAN[12,0,0] 𝄀 diff --git a/test/distributed/cases/fulltext2/fulltext2_wrapped_score.sql b/test/distributed/cases/fulltext2/fulltext2_wrapped_score.sql index 4624097d673ed..c801903a52078 100644 --- a/test/distributed/cases/fulltext2/fulltext2_wrapped_score.sql +++ b/test/distributed/cases/fulltext2/fulltext2_wrapped_score.sql @@ -128,6 +128,17 @@ select id, round(match(body) against('hello'),3) as r from two order by id; -- select count(*) as n from two where match(body) against('hello') > 0.0132; -- and through an aggregate select count(*) as n from two where match(body) against('hello') > 0.9; +-- ---------------- wrapped-only MATCH on either INNER JOIN child ---------------- +-- A wrapped-only MATCH must drive the fulltext2 stream even when its table is one child of a +-- join. Check both child positions and both a qualifying and a rejecting score threshold. +create table join_peer(id int primary key); +insert into join_peer values (1),(2),(3),(4),(5); +select t.id from two t join join_peer p on t.id = p.id where match(t.body) against('hello') > 0.0132 order by t.id; +select t.id from two t join join_peer p on t.id = p.id where match(t.body) against('hello') > 0.9 order by t.id; +select t.id from join_peer p join two t on p.id = t.id where match(t.body) against('hello') > 0.0132 order by t.id; +select t.id from join_peer p join two t on p.id = t.id where match(t.body) against('hello') > 0.9 order by t.id; +drop table join_peer; + -- @separator:table -- @regex("Table Function on fulltext2_search", true) explain select id from two where match(body) against('hello') > 0.0132; From 325686d29c17b3729f32b837b5bc2144d33f0a9b Mon Sep 17 00:00:00 2001 From: VioletQwQ-0 <1659648118@qq.com> Date: Thu, 27 Aug 2026 10:13:16 +0800 Subject: [PATCH 17/17] fix(plan): remove unused fulltext2 join wrapper --- pkg/sql/plan/apply_indices_fulltext.go | 11 ----------- pkg/sql/plan/apply_indices_fulltext2.go | 4 ++-- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/pkg/sql/plan/apply_indices_fulltext.go b/pkg/sql/plan/apply_indices_fulltext.go index ba2b1f53c7816..3140ee223feb0 100644 --- a/pkg/sql/plan/apply_indices_fulltext.go +++ b/pkg/sql/plan/apply_indices_fulltext.go @@ -298,17 +298,6 @@ func (builder *QueryBuilder) applyIndicesForAggUsingFullTextIndex(nodeID int32, return nodeID, nil } -func (builder *QueryBuilder) applyJoinFullTextIndices(nodeID int32, projNode *plan.Node, scanNode *plan.Node, - paginationLimit, paginationOffset *plan.Expr, - filterids []int32, filter_indexDefs []*plan.IndexDef, - projids []int32, proj_indexDefs []*plan.IndexDef, eqmap map[int32]int32, - colRefCnt map[[2]int32]int, idxColMap map[[2]int32]*plan.Expr) (int32, []int32, []int32, error) { - joinID, filterIDs, projIDs, _, err := builder.applyJoinFullTextIndicesWithWrapped(nodeID, projNode, scanNode, - paginationLimit, paginationOffset, filterids, filter_indexDefs, projids, proj_indexDefs, - nil, nil, eqmap, colRefCnt, idxColMap) - return joinID, filterIDs, projIDs, err -} - func (builder *QueryBuilder) applyJoinFullTextIndicesWithWrapped(nodeID int32, projNode *plan.Node, scanNode *plan.Node, paginationLimit, paginationOffset *plan.Expr, filterids []int32, filter_indexDefs []*plan.IndexDef, diff --git a/pkg/sql/plan/apply_indices_fulltext2.go b/pkg/sql/plan/apply_indices_fulltext2.go index 3066ec9de36b9..7550099c36f1b 100644 --- a/pkg/sql/plan/apply_indices_fulltext2.go +++ b/pkg/sql/plan/apply_indices_fulltext2.go @@ -14,8 +14,8 @@ // fulltext2-specific pieces of the MATCH query rewrite. fulltext2 reuses the // classic MATCH(col) AGAINST(...) surface (fulltext_match), so it is resolved by -// findMatchFullTextIndex and chained by applyJoinFullTextIndices exactly like a -// classic fulltext index — the only difference is the per-match TVF: this file's +// findMatchFullTextIndex and routed through the shared join rewrite exactly like +// a classic fulltext index — the only difference is the per-match TVF: this file's // buildFulltext2SearchTableFunc emits a fulltext2_search TVF (WAND positional // engine) instead of fulltext_index_scan. Both emit (doc_id, score), so the // downstream join/sort/limit is unchanged.