diff --git a/contrib/cube/Makefile b/contrib/cube/Makefile new file mode 100644 index 0000000000..3bf289f30c --- /dev/null +++ b/contrib/cube/Makefile @@ -0,0 +1,83 @@ +# +# $Header: /cvsroot/pgsql/contrib/cube/Makefile,v 1.1 2000/12/11 20:39:14 tgl Exp $ +# + +subdir = contrib/cube +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global + +# override libdir to install shlib in contrib not main directory +libdir := $(libdir)/contrib + +# shared library parameters +NAME= cube +SO_MAJOR_VERSION= 1 +SO_MINOR_VERSION= 0 + +override CPPFLAGS += -I$(srcdir) + +OBJS= cube.o cubeparse.o cubescan.o buffer.o + +all: all-lib $(NAME).sql + +# Shared library stuff +include $(top_srcdir)/src/Makefile.shlib + + +cubeparse.c cubeparse.h: cubeparse.y + $(YACC) -d $(YFLAGS) -p cube_yy $< + mv -f y.tab.c cubeparse.c + mv -f y.tab.h cubeparse.h + +cubescan.c: cubescan.l +ifdef FLEX + $(FLEX) $(FLEXFLAGS) -Pcube_yy -o'$@' $< +else + @$(missing) flex $< $@ +endif + +$(NAME).sql: $(NAME).sql.in + sed -e 's:MODULE_PATHNAME:$(libdir)/$(shlib):g' < $< > $@ + +.PHONY: submake +submake: + $(MAKE) -C $(top_builddir)/src/test/regress pg_regress + +# against installed postmaster +installcheck: submake + $(top_builddir)/src/test/regress/pg_regress cube + +# in-tree test doesn't work yet (no way to install my shared library) +#check: all submake +# $(top_builddir)/src/test/regress/pg_regress --temp-install \ +# --top-builddir=$(top_builddir) seg +check: + @echo "'make check' is not supported." + @echo "Do 'make install', then 'make installcheck' instead." + +install: all installdirs install-lib + $(INSTALL_DATA) $(srcdir)/README.$(NAME) $(docdir)/contrib + $(INSTALL_DATA) $(NAME).sql $(datadir)/contrib + +installdirs: + $(mkinstalldirs) $(docdir)/contrib $(datadir)/contrib $(libdir) + +uninstall: uninstall-lib + rm -f $(docdir)/contrib/README.$(NAME) $(datadir)/contrib/$(NAME).sql + +clean distclean maintainer-clean: clean-lib + rm -f cubeparse.c cubeparse.h cubescan.c + rm -f y.tab.c y.tab.h $(OBJS) $(NAME).sql +# things created by various check targets + rm -rf results tmp_check log + rm -f regression.diffs regression.out regress.out run_check.out +ifeq ($(PORTNAME), win) + rm -f regress.def +endif + +depend dep: + $(CC) -MM $(CFLAGS) *.c >depend + +ifeq (depend,$(wildcard depend)) +include depend +endif diff --git a/contrib/cube/README.cube b/contrib/cube/README.cube new file mode 100644 index 0000000000..cba90f6744 --- /dev/null +++ b/contrib/cube/README.cube @@ -0,0 +1,289 @@ +This directory contains the code for the user-defined type, +CUBE, representing multidimensional cubes. + + +FILES +----- + +Makefile building instructions for the shared library + +README.cube the file you are now reading + +buffer.c globals and buffer access utilities shared between + the parser (cubeparse.y) and the scanner (cubescan.l) + +buffer.h function prototypes for buffer.c + +cube.c the implementation of this data type in c + +cube.sql.in SQL code needed to register this type with postgres + (transformed to cube.sql by make) + +cubedata.h the data structure used to store the cubes + +cubeparse.y the grammar file for the parser (used by cube_in() in cube.c) + +cubescan.l scanner rules (used by cube_yyparse() in cubeparse.y) + + +INSTALLATION +============ + +To install the type, run + + make + make install + +For this to work, make sure that: + +. the cube source directory is in the postgres contrib directory +. the user running "make install" has postgres administrative authority +. this user's environment defines the PGLIB and PGDATA variables and has + postgres binaries in the PATH. + +This only installs the type implementation and documentation. To make the +type available in any particular database, do + + psql -d databasename < cube.sql + +If you install the type in the template1 database, all subsequently created +databases will inherit it. + +To test the new type, after "make install" do + + make installcheck + +If it fails, examine the file regression.diffs to find out the reason (the +test code is a direct adaptation of the regression tests from the main +source tree). + + +SYNTAX +====== + +The following are valid external representations for the CUBE type: + +'x' A floating point value representing + a one-dimensional point or one-dimensional + zero length cubement + +'(x)' Same as above + +'x1,x2,x3,...,xn' A point in n-dimensional space, + represented internally as a zero volume box + +'(x1,x2,x3,...,xn)' Same as above + +'(x),(y)' 1-D cubement starting at x and ending at y + or vice versa; the order does not matter + +'(x1,...,xn),(y1,...,yn)' n-dimensional box represented by + a pair of its opposite corners, no matter which. + Functions take care of swapping to achieve + "lower left -- upper right" representation + before computing any values + +Grammar +------- + +rule 1 box -> O_BRACKET paren_list COMMA paren_list C_BRACKET +rule 2 box -> paren_list COMMA paren_list +rule 3 box -> paren_list +rule 4 box -> list +rule 5 paren_list -> O_PAREN list C_PAREN +rule 6 list -> FLOAT +rule 7 list -> list COMMA FLOAT + +Tokens +------ + +n [0-9]+ +integer [+-]?{n} +real [+-]?({n}\.{n}?)|(\.{n}) +FLOAT ({integer}|{real})([eE]{integer})? +O_BRACKET \[ +C_BRACKET \] +O_PAREN \( +C_PAREN \) +COMMA \, + + +Examples of valid CUBE representations: +-------------------------------------- + +'x' A floating point value representing + a one-dimensional point (or, zero-length + one-dimensional interval) + +'(x)' Same as above + +'x1,x2,x3,...,xn' A point in n-dimensional space, + represented internally as a zero volume cube + +'(x1,x2,x3,...,xn)' Same as above + +'(x),(y)' A 1-D interval starting at x and ending at y + or vice versa; the order does not matter + +'[(x),(y)]' Same as above + +'(x1,...,xn),(y1,...,yn)' An n-dimensional box represented by + a pair of its diagonally opposite corners, + regardless of order. Swapping is provided + by all comarison routines to ensure the + "lower left -- upper right" representation + before actaul comparison takes place. + +'[(x1,...,xn),(y1,...,yn)]' Same as above + + +White space is ignored, so '[(x),(y)]' can be: '[ ( x ), ( y ) ]' + + +DEFAULTS +======== + +I believe this union: + +select cube_union('(0,5,2),(2,3,1)','0'); +cube_union +------------------- +(0, 0, 0),(2, 5, 2) +(1 row) + +does not contradict to the common sense, neither does the intersection + +select cube_inter('(0,-1),(1,1)','(-2),(2)'); +cube_inter +------------- +(0, 0),(1, 0) +(1 row) + +In all binary operations on differently sized boxes, I assume the smaller +one to be a cartesian projection, i. e., having zeroes in place of coordinates +omitted in the string representation. The above examples are equivalent to: + +cube_union('(0,5,2),(2,3,1)','(0,0,0),(0,0,0)'); +cube_inter('(0,-1),(1,1)','(-2,0),(2,0)'); + + +The following containment predicate uses the point syntax, +while in fact the second argument is internally represented by a box. +This syntax makes it unnecessary to define the special Point type +and functions for (box,point) predicates. + +select cube_contains('(0,0),(1,1)', '0.5,0.5'); +cube_contains +-------------- +t +(1 row) + + +PRECISION +========= + +Values are stored internally as 32-bit floating point numbers. This means that +numbers with more than 7 significant digits will be truncated. + + +USAGE +===== + +The access method for CUBE is a GiST (gist_cube_ops), which is a +generalization of R-tree. GiSTs allow the postgres implementation of +R-tree, originally encoded to support 2-D geometric types such as +boxes and polygons, to be used with any data type whose data domain +can be partitioned using the concepts of containment, intersection and +equality. In other words, everything that can intersect or contain +its own kind can be indexed with a GiST. That includes, among other +things, all geometric data types, regardless of their dimensionality +(see also contrib/seg). + +The operators supported by the GiST access method include: + + +[a, b] << [c, d] Is left of + + The left operand, [a, b], occurs entirely to the left of the + right operand, [c, d], on the axis (-inf, inf). It means, + [a, b] << [c, d] is true if b < c and false otherwise + +[a, b] >> [c, d] Is right of + + [a, b] is occurs entirely to the right of [c, d]. + [a, b] >> [c, d] is true if b > c and false otherwise + +[a, b] &< [c, d] Over left + + The cubement [a, b] overlaps the cubement [c, d] in such a way + that a <= c <= b and b <= d + +[a, b] &> [c, d] Over right + + The cubement [a, b] overlaps the cubement [c, d] in such a way + that a > c and b <= c <= d + +[a, b] = [c, d] Same as + + The cubements [a, b] and [c, d] are identical, that is, a == b + and c == d + +[a, b] @ [c, d] Contains + + The cubement [a, b] contains the cubement [c, d], that is, + a <= c and b >= d + +[a, b] @ [c, d] Contained in + + The cubement [a, b] is contained in [c, d], that is, + a >= c and b <= d + +Although the mnemonics of the following operators is questionable, I +preserved them to maintain visual consistency with other geometric +data types defined in Postgres. + +Other operators: + +[a, b] < [c, d] Less than +[a, b] > [c, d] Greater than + + These operators do not make a lot of sense for any practical + purpose but sorting. These operators first compare (a) to (c), + and if these are equal, compare (b) to (d). That accounts for + reasonably good sorting in most cases, which is useful if + you want to use ORDER BY with this type + +There are a few other potentially useful functions defined in cube.c +that vanished from the schema because I stopped using them. Some of +these were meant to support type casting. Let me know if I was wrong: +I will then add them back to the schema. I would also appreciate +other ideas that would enhance the type and make it more useful. + +For examples of usage, see sql/cube.sql + + +CREDITS +======= + +This code is essentially based on the example written for +Illustra, http://garcia.me.berkeley.edu/~adong/rtree + +My thanks are primarily to Prof. Joe Hellerstein +(http://db.cs.berkeley.edu/~jmh/) for elucidating the gist of the GiST +(http://gist.cs.berkeley.edu/), and to his former student, Andy Dong +(http://best.me.berkeley.edu/~adong/), for his exemplar. +I am also grateful to all postgres developers, present and past, for enabling +myself to create my own world and live undisturbed in it. And I would like to +acknowledge my gratitude to Argonne Lab and to the U.S. Department of Energy +for the years of faithful support of my database research. + +------------------------------------------------------------------------ +Gene Selkov, Jr. +Computational Scientist +Mathematics and Computer Science Division +Argonne National Laboratory +9700 S Cass Ave. +Building 221 +Argonne, IL 60439-4844 + +selkovjr@mcs.anl.gov diff --git a/contrib/cube/buffer.c b/contrib/cube/buffer.c new file mode 100644 index 0000000000..0bcc2d19b5 --- /dev/null +++ b/contrib/cube/buffer.c @@ -0,0 +1,79 @@ +/* This module defines the parse buffer and routines for setting/reading it */ + +#include "postgres.h" + +#include "utils/elog.h" + +static char * PARSE_BUFFER; +static char * PARSE_BUFFER_PTR; +static unsigned int PARSE_BUFFER_SIZE; +static unsigned int SCANNER_POS; + +void set_parse_buffer( char* s ); +void reset_parse_buffer( void ); +int read_parse_buffer( void ); +char * parse_buffer( void ); +char * parse_buffer_ptr( void ); +unsigned int parse_buffer_curr_char( void ); +unsigned int parse_buffer_size( void ); +unsigned int parse_buffer_pos( void ); + +extern void cube_flush_scanner_buffer(void); /* defined in cubescan.l */ + +void set_parse_buffer( char* s ) +{ + PARSE_BUFFER = s; + PARSE_BUFFER_SIZE = strlen(s); + if ( PARSE_BUFFER_SIZE == 0 ) { + elog(ERROR, "cube_in: can't parse an empty string"); + } + PARSE_BUFFER_PTR = PARSE_BUFFER; + SCANNER_POS = 0; +} + +void reset_parse_buffer( void ) +{ + PARSE_BUFFER_PTR = PARSE_BUFFER; + SCANNER_POS = 0; + cube_flush_scanner_buffer(); +} + +int read_parse_buffer( void ) +{ + int c; + /* + c = *PARSE_BUFFER_PTR++; + SCANNER_POS++; + */ + c = PARSE_BUFFER[SCANNER_POS]; + if(SCANNER_POS < PARSE_BUFFER_SIZE) + SCANNER_POS++; + return c; +} + +char * parse_buffer( void ) +{ + return PARSE_BUFFER; +} + +unsigned int parse_buffer_curr_char( void ) +{ + return PARSE_BUFFER[SCANNER_POS]; +} + +char * parse_buffer_ptr( void ) +{ + return PARSE_BUFFER_PTR; +} + +unsigned int parse_buffer_pos( void ) +{ + return SCANNER_POS; +} + +unsigned int parse_buffer_size( void ) +{ + return PARSE_BUFFER_SIZE; +} + + diff --git a/contrib/cube/buffer.h b/contrib/cube/buffer.h new file mode 100644 index 0000000000..fd41a7b69b --- /dev/null +++ b/contrib/cube/buffer.h @@ -0,0 +1,8 @@ +extern void set_parse_buffer( char* s ); +extern void reset_parse_buffer( void ); +extern int read_parse_buffer( void ); +extern char * parse_buffer( void ); +extern char * parse_buffer_ptr( void ); +extern unsigned int parse_buffer_curr_char( void ); +extern unsigned int parse_buffer_pos( void ); +extern unsigned int parse_buffer_size( void ); diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c new file mode 100644 index 0000000000..35ac34f0b0 --- /dev/null +++ b/contrib/cube/cube.c @@ -0,0 +1,1058 @@ +/****************************************************************************** + This file contains routines that can be bound to a Postgres backend and + called by the backend in the process of processing queries. The calling + format for these routines is dictated by Postgres architecture. +******************************************************************************/ + +#include "postgres.h" + +#include + +#include "access/gist.h" +#include "access/rtree.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/builtins.h" + +#include "cubedata.h" + +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#define abs(a) ((a) < (0) ? (-a) : (a)) + +extern void set_parse_buffer(char *str); +extern int cube_yyparse(); + +/* +** Input/Output routines +*/ +NDBOX * cube_in(char *str); +char * cube_out(NDBOX *cube); + + +/* +** GiST support methods +*/ +bool g_cube_consistent(GISTENTRY *entry, NDBOX *query, StrategyNumber strategy); +GISTENTRY * g_cube_compress(GISTENTRY *entry); +GISTENTRY * g_cube_decompress(GISTENTRY *entry); +float * g_cube_penalty(GISTENTRY *origentry, GISTENTRY *newentry, float *result); +GIST_SPLITVEC * g_cube_picksplit(bytea *entryvec, GIST_SPLITVEC *v); +bool g_cube_leaf_consistent(NDBOX *key, NDBOX *query, StrategyNumber strategy); +bool g_cube_internal_consistent(NDBOX *key, NDBOX *query, StrategyNumber strategy); +NDBOX * g_cube_union(bytea *entryvec, int *sizep); +NDBOX * g_cube_binary_union(NDBOX *r1, NDBOX *r2, int *sizep); +bool * g_cube_same(NDBOX *b1, NDBOX *b2, bool *result); + +/* +** R-tree suport functions +*/ +bool cube_same(NDBOX *a, NDBOX *b); +bool cube_different(NDBOX *a, NDBOX *b); +bool cube_contains(NDBOX *a, NDBOX *b); +bool cube_contained (NDBOX *a, NDBOX *b); +bool cube_overlap(NDBOX *a, NDBOX *b); +NDBOX * cube_union(NDBOX *a, NDBOX *b); +NDBOX * cube_inter(NDBOX *a, NDBOX *b); +float * cube_size(NDBOX *a); +void rt_cube_size(NDBOX *a, float *sz); + +/* +** These make no sense for this type, but R-tree wants them +*/ +bool cube_over_left(NDBOX *a, NDBOX *b); +bool cube_over_right(NDBOX *a, NDBOX *b); +bool cube_left(NDBOX *a, NDBOX *b); +bool cube_right(NDBOX *a, NDBOX *b); + +/* +** miscellaneous +*/ +bool cube_lt(NDBOX *a, NDBOX *b); +bool cube_gt(NDBOX *a, NDBOX *b); +float * cube_distance(NDBOX *a, NDBOX *b); + +/* +** Auxiliary funxtions +*/ +static float distance_1D(float a1, float a2, float b1, float b2); +static NDBOX *swap_corners (NDBOX *a); + + +/***************************************************************************** + * Input/Output functions + *****************************************************************************/ + +/* NdBox = [(lowerleft),(upperright)] */ +/* [(xLL(1)...xLL(N)),(xUR(1)...xUR(n))] */ +NDBOX * +cube_in(char *str) +{ + void * result; + + set_parse_buffer( str ); + + if ( cube_yyparse(&result) != 0 ) { + return NULL; + } + + return ( (NDBOX *)result ); +} + +/* + * You might have noticed a slight inconsistency between the following + * declaration and the SQL definition: + * CREATE FUNCTION cube_out(opaque) RETURNS opaque ... + * The reason is that the argument pass into cube_out is really just a + * pointer. POSTGRES thinks all output functions are: + * char *out_func(char *); + */ +char * +cube_out(NDBOX *cube) +{ + char *result; + char *p; + int equal = 1; + int dim = cube->dim; + int i; + + if (cube == NULL) + return(NULL); + + p = result = (char *) palloc(100); + + /* while printing the first (LL) corner, check if it is equal + to the scond one */ + p += sprintf(p, "("); + for ( i=0; i < dim; i++ ) { + p += sprintf(p, "%g", cube->x[i]); + p += sprintf(p, ", "); + if ( cube->x[i] != cube->x[i+dim] ) { + equal = 0; + } + } + p -= 2; /* get rid of the last ", " */ + p += sprintf(p, ")"); + + if ( !equal ) { + p += sprintf(p, ",("); + for ( i=dim; i < dim * 2; i++ ) { + p += sprintf(p, "%g", cube->x[i]); + p += sprintf(p, ", "); + } + p -= 2; + p += sprintf(p, ")"); + } + + return(result); +} + + +/***************************************************************************** + * GiST functions + *****************************************************************************/ + +/* +** The GiST Consistent method for boxes +** Should return false if for all data items x below entry, +** the predicate x op query == FALSE, where op is the oper +** corresponding to strategy in the pg_amop table. +*/ +bool +g_cube_consistent(GISTENTRY *entry, + NDBOX *query, + StrategyNumber strategy) +{ + /* + ** if entry is not leaf, use g_cube_internal_consistent, + ** else use g_cube_leaf_consistent + */ + if (GIST_LEAF(entry)) + return(g_cube_leaf_consistent((NDBOX *)(entry->pred), query, strategy)); + else + return(g_cube_internal_consistent((NDBOX *)(entry->pred), query, strategy)); +} + + +/* +** The GiST Union method for boxes +** returns the minimal bounding box that encloses all the entries in entryvec +*/ +NDBOX * +g_cube_union(bytea *entryvec, int *sizep) +{ + int numranges, i; + NDBOX *out = (NDBOX *)NULL; + NDBOX *tmp; + + /* + fprintf(stderr, "union\n"); + */ + numranges = (VARSIZE(entryvec) - VARHDRSZ)/sizeof(GISTENTRY); + tmp = (NDBOX *)(((GISTENTRY *)(VARDATA(entryvec)))[0]).pred; + /* + *sizep = sizeof(NDBOX); -- NDBOX has variable size + */ + *sizep = tmp->size; + + for (i = 1; i < numranges; i++) { + out = g_cube_binary_union(tmp, (NDBOX *) + (((GISTENTRY *)(VARDATA(entryvec)))[i]).pred, + sizep); + /* + fprintf(stderr, "\t%s ^ %s -> %s\n", cube_out(tmp), cube_out((NDBOX *)(((GISTENTRY *)(VARDATA(entryvec)))[i]).pred), cube_out(out)); + */ + if (i > 1) pfree(tmp); + tmp = out; + } + + return(out); +} + +/* +** GiST Compress and Decompress methods for boxes +** do not do anything. +*/ +GISTENTRY * +g_cube_compress(GISTENTRY *entry) +{ + return(entry); +} + +GISTENTRY * +g_cube_decompress(GISTENTRY *entry) +{ + return(entry); +} + +/* +** The GiST Penalty method for boxes +** As in the R-tree paper, we use change in area as our penalty metric +*/ +float * +g_cube_penalty(GISTENTRY *origentry, GISTENTRY *newentry, float *result) +{ + Datum ud; + float tmp1, tmp2; + + ud = (Datum)cube_union((NDBOX *)(origentry->pred), (NDBOX *)(newentry->pred)); + rt_cube_size((NDBOX *)ud, &tmp1); + rt_cube_size((NDBOX *)(origentry->pred), &tmp2); + *result = tmp1 - tmp2; + pfree((char *)ud); + /* + fprintf(stderr, "penalty\n"); + fprintf(stderr, "\t%g\n", *result); + */ + return(result); +} + + + +/* +** The GiST PickSplit method for boxes +** We use Guttman's poly time split algorithm +*/ +GIST_SPLITVEC * +g_cube_picksplit(bytea *entryvec, + GIST_SPLITVEC *v) +{ + OffsetNumber i, j; + NDBOX *datum_alpha, *datum_beta; + NDBOX *datum_l, *datum_r; + NDBOX *union_d, *union_dl, *union_dr; + NDBOX *inter_d; + bool firsttime; + float size_alpha, size_beta, size_union, size_inter; + float size_waste, waste; + float size_l, size_r; + int nbytes; + OffsetNumber seed_1 = 0, seed_2 = 0; + OffsetNumber *left, *right; + OffsetNumber maxoff; + + /* + fprintf(stderr, "picksplit\n"); + */ + maxoff = ((VARSIZE(entryvec) - VARHDRSZ)/sizeof(GISTENTRY)) - 2; + nbytes = (maxoff + 2) * sizeof(OffsetNumber); + v->spl_left = (OffsetNumber *) palloc(nbytes); + v->spl_right = (OffsetNumber *) palloc(nbytes); + + firsttime = true; + waste = 0.0; + + for (i = FirstOffsetNumber; i < maxoff; i = OffsetNumberNext(i)) { + datum_alpha = (NDBOX *)(((GISTENTRY *)(VARDATA(entryvec)))[i].pred); + for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j)) { + datum_beta = (NDBOX *)(((GISTENTRY *)(VARDATA(entryvec)))[j].pred); + + /* compute the wasted space by unioning these guys */ + /* size_waste = size_union - size_inter; */ + union_d = (NDBOX *)cube_union(datum_alpha, datum_beta); + rt_cube_size(union_d, &size_union); + inter_d = (NDBOX *)cube_inter(datum_alpha, datum_beta); + rt_cube_size(inter_d, &size_inter); + size_waste = size_union - size_inter; + + pfree(union_d); + + if (inter_d != (NDBOX *) NULL) + pfree(inter_d); + + /* + * are these a more promising split than what we've + * already seen? + */ + + if (size_waste > waste || firsttime) { + waste = size_waste; + seed_1 = i; + seed_2 = j; + firsttime = false; + } + } + } + + left = v->spl_left; + v->spl_nleft = 0; + right = v->spl_right; + v->spl_nright = 0; + + datum_alpha = (NDBOX *)(((GISTENTRY *)(VARDATA(entryvec)))[seed_1].pred); + datum_l = (NDBOX *)cube_union(datum_alpha, datum_alpha); + rt_cube_size((NDBOX *)datum_l, &size_l); + datum_beta = (NDBOX *)(((GISTENTRY *)(VARDATA(entryvec)))[seed_2].pred);; + datum_r = (NDBOX *)cube_union(datum_beta, datum_beta); + rt_cube_size((NDBOX *)datum_r, &size_r); + + /* + * Now split up the regions between the two seeds. An important + * property of this split algorithm is that the split vector v + * has the indices of items to be split in order in its left and + * right vectors. We exploit this property by doing a merge in + * the code that actually splits the page. + * + * For efficiency, we also place the new index tuple in this loop. + * This is handled at the very end, when we have placed all the + * existing tuples and i == maxoff + 1. + */ + + maxoff = OffsetNumberNext(maxoff); + for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { + + /* + * If we've already decided where to place this item, just + * put it on the right list. Otherwise, we need to figure + * out which page needs the least enlargement in order to + * store the item. + */ + + if (i == seed_1) { + *left++ = i; + v->spl_nleft++; + continue; + } else if (i == seed_2) { + *right++ = i; + v->spl_nright++; + continue; + } + + /* okay, which page needs least enlargement? */ + datum_alpha = (NDBOX *)(((GISTENTRY *)(VARDATA(entryvec)))[i].pred); + union_dl = (NDBOX *)cube_union(datum_l, datum_alpha); + union_dr = (NDBOX *)cube_union(datum_r, datum_alpha); + rt_cube_size((NDBOX *)union_dl, &size_alpha); + rt_cube_size((NDBOX *)union_dr, &size_beta); + + /* pick which page to add it to */ + if (size_alpha - size_l < size_beta - size_r) { + pfree(datum_l); + pfree(union_dr); + datum_l = union_dl; + size_l = size_alpha; + *left++ = i; + v->spl_nleft++; + } else { + pfree(datum_r); + pfree(union_dl); + datum_r = union_dr; + size_r = size_alpha; + *right++ = i; + v->spl_nright++; + } + } + *left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */ + + v->spl_ldatum = (char *)datum_l; + v->spl_rdatum = (char *)datum_r; + + return v; +} + +/* +** Equality method +*/ +bool * +g_cube_same(NDBOX *b1, NDBOX *b2, bool *result) +{ + if (cube_same(b1, b2)) + *result = TRUE; + else *result = FALSE; + /* + fprintf(stderr, "same: %s\n", (*result ? "TRUE" : "FALSE" )); + */ + return(result); +} + +/* +** SUPPORT ROUTINES +*/ +bool +g_cube_leaf_consistent(NDBOX *key, + NDBOX *query, + StrategyNumber strategy) +{ + bool retval; + + /* + fprintf(stderr, "leaf_consistent, %d\n", strategy); + */ + switch(strategy) { + case RTLeftStrategyNumber: + retval = (bool)cube_left(key, query); + break; + case RTOverLeftStrategyNumber: + retval = (bool)cube_over_left(key,query); + break; + case RTOverlapStrategyNumber: + retval = (bool)cube_overlap(key, query); + break; + case RTOverRightStrategyNumber: + retval = (bool)cube_over_right(key, query); + break; + case RTRightStrategyNumber: + retval = (bool)cube_right(key, query); + break; + case RTSameStrategyNumber: + retval = (bool)cube_same(key, query); + break; + case RTContainsStrategyNumber: + retval = (bool)cube_contains(key, query); + break; + case RTContainedByStrategyNumber: + retval = (bool)cube_contained(key,query); + break; + default: + retval = FALSE; + } + return(retval); +} + +bool +g_cube_internal_consistent(NDBOX *key, + NDBOX *query, + StrategyNumber strategy) +{ + bool retval; + + /* + fprintf(stderr, "internal_consistent, %d\n", strategy); + */ + switch(strategy) { + case RTLeftStrategyNumber: + case RTOverLeftStrategyNumber: + retval = (bool)cube_over_left(key,query); + break; + case RTOverlapStrategyNumber: + retval = (bool)cube_overlap(key, query); + break; + case RTOverRightStrategyNumber: + case RTRightStrategyNumber: + retval = (bool)cube_right(key, query); + break; + case RTSameStrategyNumber: + case RTContainsStrategyNumber: + retval = (bool)cube_contains(key, query); + break; + case RTContainedByStrategyNumber: + retval = (bool)cube_overlap(key, query); + break; + default: + retval = FALSE; + } + return(retval); +} + +NDBOX * +g_cube_binary_union(NDBOX *r1, NDBOX *r2, int *sizep) +{ + NDBOX *retval; + + retval = cube_union(r1, r2); + *sizep = retval->size; + + return (retval); +} + + +/* cube_union */ +NDBOX *cube_union(NDBOX *box_a, NDBOX *box_b) +{ + int i; + NDBOX *result; + NDBOX *a = swap_corners(box_a); + NDBOX *b = swap_corners(box_b); + + if ( a->dim >= b->dim ) { + result = palloc(a->size); + result->size = a->size; + result->dim = a->dim; + } + else { + result = palloc(b->size); + result->size = b->size; + result->dim = b->dim; + } + + /* swap the box pointers if needed */ + if ( a->dim < b->dim ) { + NDBOX * tmp = b; b = a; a = tmp; + } + + /* use the potentially smaller of the two boxes (b) to fill in + the result, padding absent dimensions with zeroes*/ + for ( i = 0; i < b->dim; i++ ) { + result->x[i] = b->x[i]; + result->x[i + a->dim] = b->x[i + b->dim]; + } + for ( i = b->dim; i < a->dim; i++ ) { + result->x[i] = 0; + result->x[i + a->dim] = 0; + } + + /* compute the union */ + for ( i = 0; i < a->dim; i++ ) { + result->x[i] = min(a->x[i], result->x[i]); + } + for ( i = a->dim; i < a->dim * 2; i++ ) { + result->x[i] = max(a->x[i], result->x[i]); + } + + pfree(a); + pfree(b); + + return(result); +} + +/* cube_inter */ +NDBOX *cube_inter(NDBOX *box_a, NDBOX *box_b) +{ + int i; + NDBOX * result; + NDBOX *a = swap_corners(box_a); + NDBOX *b = swap_corners(box_b); + + if ( a->dim >= b->dim ) { + result = palloc(a->size); + result->size = a->size; + result->dim = a->dim; + } + else { + result = palloc(b->size); + result->size = b->size; + result->dim = b->dim; + } + + /* swap the box pointers if needed */ + if ( a->dim < b->dim ) { + NDBOX * tmp = b; b = a; a = tmp; + } + + /* use the potentially smaller of the two boxes (b) to fill in + the result, padding absent dimensions with zeroes*/ + for ( i = 0; i < b->dim; i++ ) { + result->x[i] = b->x[i]; + result->x[i + a->dim] = b->x[i + b->dim]; + } + for ( i = b->dim; i < a->dim; i++ ) { + result->x[i] = 0; + result->x[i + a->dim] = 0; + } + + /* compute the intersection */ + for ( i = 0; i < a->dim; i++ ) { + result->x[i] = max(a->x[i], result->x[i]); + } + for ( i = a->dim; i < a->dim * 2; i++ ) { + result->x[i] = min(a->x[i], result->x[i]); + } + + pfree(a); + pfree(b); + + /* Is it OK to return a non-null intersection for non-overlapping boxes? */ + return(result); +} + +/* cube_size */ +float *cube_size(NDBOX *a) +{ + int i,j; + float *result; + + result = (float *) palloc(sizeof(float)); + + *result = 1.0; + for ( i = 0, j = a->dim; i < a->dim; i++,j++ ) { + *result=(*result)*abs((a->x[j] - a->x[i])); + } + + return(result); +} + +void +rt_cube_size(NDBOX *a, float *size) +{ + int i,j; + if (a == (NDBOX *) NULL) + *size = 0.0; + else { + *size = 1.0; + for ( i = 0, j = a->dim; i < a->dim; i++,j++ ) { + *size=(*size)*abs((a->x[j] - a->x[i])); + } + } + return; +} + +/* The following four methods compare the projections of the boxes + onto the 0-th coordinate axis. These methods are useless for dimensions + larger than 2, but it seems that R-tree requires all its strategies + map to real functions that return something */ + +/* is the right edge of (a) located to the left of + the right edge of (b)? */ +bool cube_over_left(NDBOX *box_a, NDBOX *box_b) +{ + NDBOX *a; + NDBOX *b; + + if ( (box_a==NULL) || (box_b==NULL) ) + return(FALSE); + + a = swap_corners(box_a); + b = swap_corners(box_b); + + return( a->x[a->dim - 1] <= b->x[b->dim - 1] && !cube_left(a, b) && !cube_right(a, b) ); +} + +/* is the left edge of (a) located to the right of + the left edge of (b)? */ +bool cube_over_right(NDBOX *box_a, NDBOX *box_b) +{ + NDBOX *a; + NDBOX *b; + + if ( (box_a==NULL) || (box_b==NULL) ) + return(FALSE); + + a = swap_corners(box_a); + b = swap_corners(box_b); + + return( a->x[a->dim - 1] >= b->x[b->dim - 1] && !cube_left(a, b) && !cube_right(a, b) ); +} + + +/* return 'true' if the projection of 'a' is + entirely on the left of the projection of 'b' */ +bool cube_left(NDBOX *box_a, NDBOX *box_b) +{ + NDBOX *a; + NDBOX *b; + + if ( (box_a==NULL) || (box_b==NULL) ) + return(FALSE); + + a = swap_corners(box_a); + b = swap_corners(box_b); + + return( a->x[a->dim - 1] < b->x[0]); +} + +/* return 'true' if the projection of 'a' is + entirely on the right of the projection of 'b' */ +bool cube_right(NDBOX *box_a, NDBOX *box_b) +{ + NDBOX *a; + NDBOX *b; + + if ( (box_a==NULL) || (box_b==NULL) ) + return(FALSE); + + a = swap_corners(box_a); + b = swap_corners(box_b); + + return( a->x[0] > b->x[b->dim - 1]); +} + +/* make up a metric in which one box will be 'lower' than the other + -- this can be useful for srting and to determine uniqueness */ +bool cube_lt(NDBOX *box_a, NDBOX *box_b) +{ + int i; + int dim; + NDBOX *a; + NDBOX *b; + + if ( (box_a==NULL) || (box_b==NULL) ) + return(FALSE); + + a = swap_corners(box_a); + b = swap_corners(box_b); + dim = min(a->dim, b->dim); + + /* if all common dimensions are equal, the cube with more dimensions wins */ + if ( cube_same(a, b) ) { + if (a->dim < b->dim) { + return(TRUE); + } + else { + return(FALSE); + } + } + + /* compare the common dimensions */ + for ( i = 0; i < dim; i++ ) { + if ( a->x[i] > b->x[i] ) + return(FALSE); + if ( a->x[i] < b->x[i] ) + return(TRUE); + } + for ( i = 0; i < dim; i++ ) { + if ( a->x[i + a->dim] > b->x[i + b->dim] ) + return(FALSE); + if ( a->x[i + a->dim] < b->x[i + b->dim] ) + return(TRUE); + } + + /* compare extra dimensions to zero */ + if ( a->dim > b->dim ) { + for ( i = dim; i < a->dim; i++ ) { + if ( a->x[i] > 0 ) + return(FALSE); + if ( a->x[i] < 0 ) + return(TRUE); + } + for ( i = 0; i < dim; i++ ) { + if ( a->x[i + a->dim] > 0 ) + return(FALSE); + if ( a->x[i + a->dim] < 0 ) + return(TRUE); + } + } + if ( a->dim < b->dim ) { + for ( i = dim; i < b->dim; i++ ) { + if ( b->x[i] > 0 ) + return(TRUE); + if ( b->x[i] < 0 ) + return(FALSE); + } + for ( i = 0; i < dim; i++ ) { + if ( b->x[i + b->dim] > 0 ) + return(TRUE); + if ( b->x[i + b->dim] < 0 ) + return(FALSE); + } + } + + return(FALSE); +} + + +bool cube_gt(NDBOX *box_a, NDBOX *box_b) +{ + int i; + int dim; + NDBOX *a; + NDBOX *b; + + if ( (box_a==NULL) || (box_b==NULL) ) + return(FALSE); + + a = swap_corners(box_a); + b = swap_corners(box_b); + dim = min(a->dim, b->dim); + + /* if all common dimensions are equal, the cube with more dimensions wins */ + if ( cube_same(a, b) ) { + if (a->dim > b->dim) { + return(TRUE); + } + else { + return(FALSE); + } + } + + /* compare the common dimensions */ + for ( i = 0; i < dim; i++ ) { + if ( a->x[i] < b->x[i] ) + return(FALSE); + if ( a->x[i] > b->x[i] ) + return(TRUE); + } + for ( i = 0; i < dim; i++ ) { + if ( a->x[i + a->dim] < b->x[i + b->dim] ) + return(FALSE); + if ( a->x[i + a->dim] > b->x[i + b->dim] ) + return(TRUE); + } + + + /* compare extra dimensions to zero */ + if ( a->dim > b->dim ) { + for ( i = dim; i < a->dim; i++ ) { + if ( a->x[i] < 0 ) + return(FALSE); + if ( a->x[i] > 0 ) + return(TRUE); + } + for ( i = 0; i < dim; i++ ) { + if ( a->x[i + a->dim] < 0 ) + return(FALSE); + if ( a->x[i + a->dim] > 0 ) + return(TRUE); + } + } + if ( a->dim < b->dim ) { + for ( i = dim; i < b->dim; i++ ) { + if ( b->x[i] < 0 ) + return(TRUE); + if ( b->x[i] > 0 ) + return(FALSE); + } + for ( i = 0; i < dim; i++ ) { + if ( b->x[i + b->dim] < 0 ) + return(TRUE); + if ( b->x[i + b->dim] > 0 ) + return(FALSE); + } + } + + return(FALSE); +} + + +/* Equal */ +bool cube_same(NDBOX *box_a, NDBOX *box_b) +{ + int i; + NDBOX *a; + NDBOX *b; + + if ( (box_a==NULL) || (box_b==NULL) ) + return(FALSE); + + a = swap_corners(box_a); + b = swap_corners(box_b); + + /* swap the box pointers if necessary */ + if ( a->dim < b->dim ) { + NDBOX * tmp = b; b = a; a = tmp; + } + + for ( i = 0; i < b->dim; i++ ) { + if ( a->x[i] != b->x[i] ) + return(FALSE); + if ( a->x[i + a->dim] != b->x[i + b->dim] ) + return(FALSE); + } + + /* all dimensions of (b) are compared to those of (a); + instead of those in (a) absent in (b), compare (a) to zero */ + for ( i = b->dim; i < a->dim; i++ ) { + if ( a->x[i] != 0 ) + return(FALSE); + if ( a->x[i + a->dim] != 0 ) + return(FALSE); + } + + pfree(a); + pfree(b); + + return(TRUE); +} + +/* Different */ +bool cube_different(NDBOX *box_a, NDBOX *box_b) +{ + return(!cube_same(box_a, box_b)); +} + + +/* Contains */ +/* Box(A) CONTAINS Box(B) IFF pt(A) < pt(B) */ +bool cube_contains(NDBOX *box_a, NDBOX *box_b) +{ + int i; + NDBOX *a; + NDBOX *b; + + if ( (box_a==NULL) || (box_b==NULL) ) + return(FALSE); + + a = swap_corners(box_a); + b = swap_corners(box_b); + + if ( a->dim < b->dim ) { + /* the further comparisons will make sense if the + excess dimensions of (b) were zeroes */ + for ( i = a->dim; i < b->dim; i++ ) { + if ( b->x[i] != 0 ) + return(FALSE); + if ( b->x[i + b->dim] != 0 ) + return(FALSE); + } + } + + /* Can't care less about the excess dimensions of (a), if any */ + for ( i = 0; i < min(a->dim, b->dim); i++ ) { + if ( a->x[i] > b->x[i] ) + return(FALSE); + if ( a->x[i + a->dim] < b->x[i + b->dim] ) + return(FALSE); + } + + pfree(a); + pfree(b); + + return(TRUE); +} + +/* Contained */ +/* Box(A) Contained by Box(B) IFF Box(B) Contains Box(A) */ +bool cube_contained (NDBOX *a, NDBOX *b) +{ + if (cube_contains(b,a) == TRUE) + return(TRUE); + else + return(FALSE); +} + +/* Overlap */ +/* Box(A) Overlap Box(B) IFF (pt(a)LL < pt(B)UR) && (pt(b)LL < pt(a)UR) */ +bool cube_overlap(NDBOX *box_a, NDBOX *box_b) +{ + int i; + NDBOX *a; + NDBOX *b; + + /* This *very bad* error was found in the source: + if ( (a==NULL) || (b=NULL) ) + return(FALSE); + */ + if ( (box_a==NULL) || (box_b==NULL) ) + return(FALSE); + + a = swap_corners(box_a); + b = swap_corners(box_b); + + /* swap the box pointers if needed */ + if ( a->dim < b->dim ) { + NDBOX * tmp = b; b = a; a = tmp; + } + + /* compare within the dimensions of (b) */ + for ( i = 0; i < b->dim; i++ ) { + if ( a->x[i] > b->x[i + b->dim] ) + return(FALSE); + if ( a->x[i + a->dim] < b->x[i] ) + return(FALSE); + } + + /* compare to zero those dimensions in (a) absent in (b) */ + for ( i = b->dim; i < a->dim; i++ ) { + if ( a->x[i] > 0 ) + return(FALSE); + if ( a->x[i + a->dim] < 0 ) + return(FALSE); + } + + pfree(a); + pfree(b); + + return(TRUE); +} + + +/* Distance */ +/* The distance is computed as a per axis sum of the squared distances + between 1D projections of the boxes onto Cartesian axes. Assuming zero + distance between overlapping projections, this metric coincides with the + "common sense" geometric distance */ +float *cube_distance(NDBOX *a, NDBOX *b) +{ + int i; + double d, distance; + float *result; + + result = (float *) palloc(sizeof(float)); + + /* swap the box pointers if needed */ + if ( a->dim < b->dim ) { + NDBOX * tmp = b; b = a; a = tmp; + } + + distance = 0.0; + /* compute within the dimensions of (b) */ + for ( i = 0; i < b->dim; i++ ) { + d = distance_1D(a->x[i], a->x[i + a->dim], b->x[i], b->x[i + b->dim]); + distance += d*d; + } + + /* compute distance to zero for those dimensions in (a) absent in (b) */ + for ( i = b->dim; i < a->dim; i++ ) { + d = distance_1D(a->x[i], a->x[i + a->dim], 0.0, 0.0); + distance += d*d; + } + + *result = (float)sqrt(distance); + + return(result); +} + +static float distance_1D(float a1, float a2, float b1, float b2) +{ + /* interval (a) is entirely on the left of (b) */ + if( (a1 <= b1) && (a2 <= b1) && (a1 <= b2) && (a2 <= b2) ) { + return ( min( b1, b2 ) - max( a1, a2 ) ); + } + + /* interval (a) is entirely on the right of (b) */ + if( (a1 > b1) && (a2 > b1) && (a1 > b2) && (a2 > b2) ) { + return ( min( a1, a2 ) - max( b1, b2 ) ); + } + + /* the rest are all sorts of intersections */ + return(0.0); +} + +/* normalize the box's co-ordinates by placing min(xLL,xUR) to LL + and max(xLL,xUR) to UR +*/ +static NDBOX *swap_corners ( NDBOX *a ) +{ + int i, j; + NDBOX * result; + + result = palloc(a->size); + result->size = a->size; + result->dim = a->dim; + + for ( i = 0, j = a->dim; i < a->dim; i++, j++ ) { + result->x[i] = min(a->x[i],a->x[j]); + result->x[j] = max(a->x[i],a->x[j]); + } + + return(result); +} diff --git a/contrib/cube/cube.sql.in b/contrib/cube/cube.sql.in new file mode 100644 index 0000000000..b67ee62260 --- /dev/null +++ b/contrib/cube/cube.sql.in @@ -0,0 +1,337 @@ +-- Create the user-defined type for N-dimensional boxes +-- +BEGIN TRANSACTION; + +CREATE FUNCTION cube_in(opaque) +RETURNS opaque +AS 'MODULE_PATHNAME' +LANGUAGE 'c'; + +CREATE FUNCTION cube_out(opaque) +RETURNS opaque +AS 'MODULE_PATHNAME' +LANGUAGE 'c'; + +CREATE TYPE cube ( +internallength = variable, +input = cube_in, +output = cube_out +); + +COMMENT ON TYPE cube IS +'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)'''; + +-- +-- External C-functions for R-tree methods +-- + +-- Left/Right methods + +CREATE FUNCTION cube_over_left(cube, cube) RETURNS bool + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +COMMENT ON FUNCTION cube_over_left(cube, cube) IS +'is over and left of (NOT IMPLEMENTED)'; + +CREATE FUNCTION cube_over_right(cube, cube) RETURNS bool + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +COMMENT ON FUNCTION cube_over_right(cube, cube) IS +'is over and right of (NOT IMPLEMENTED)'; + +CREATE FUNCTION cube_left(cube, cube) RETURNS bool + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +COMMENT ON FUNCTION cube_left(cube, cube) IS +'is left of (NOT IMPLEMENTED)'; + +CREATE FUNCTION cube_right(cube, cube) RETURNS bool + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +COMMENT ON FUNCTION cube_right(cube, cube) IS +'is right of (NOT IMPLEMENTED)'; + + +-- Comparison methods + +CREATE FUNCTION cube_lt(cube, cube) RETURNS bool + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +COMMENT ON FUNCTION cube_lt(cube, cube) IS +'lower than'; + +CREATE FUNCTION cube_gt(cube, cube) RETURNS bool + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +COMMENT ON FUNCTION cube_gt(cube, cube) IS +'greater than'; + +CREATE FUNCTION cube_contains(cube, cube) RETURNS bool + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +COMMENT ON FUNCTION cube_contains(cube, cube) IS +'contains'; + +CREATE FUNCTION cube_contained(cube, cube) RETURNS bool + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +COMMENT ON FUNCTION cube_contained(cube, cube) IS +'contained in'; + +CREATE FUNCTION cube_overlap(cube, cube) RETURNS bool + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +COMMENT ON FUNCTION cube_overlap(cube, cube) IS +'overlaps'; + +CREATE FUNCTION cube_same(cube, cube) RETURNS bool + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +COMMENT ON FUNCTION cube_same(cube, cube) IS +'same as'; + +CREATE FUNCTION cube_different(cube, cube) RETURNS bool + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +COMMENT ON FUNCTION cube_different(cube, cube) IS +'different'; + +-- support routines for indexing + +CREATE FUNCTION cube_union(cube, cube) RETURNS cube + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +CREATE FUNCTION cube_inter(cube, cube) RETURNS cube + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +CREATE FUNCTION cube_size(cube) RETURNS float4 + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + + +-- Misc N-dimensional functions + +-- proximity routines + +CREATE FUNCTION cube_distance(cube, cube) RETURNS float4 + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + + +-- +-- OPERATORS +-- + +CREATE OPERATOR < ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt, + COMMUTATOR = '>', + RESTRICT = scalarltsel, JOIN = scalarltjoinsel +); + +CREATE OPERATOR > ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt, + COMMUTATOR = '<', + RESTRICT = scalargtsel, JOIN = scalargtjoinsel +); + +CREATE OPERATOR << ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_left, + COMMUTATOR = '>>', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +CREATE OPERATOR &< ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_over_left, + COMMUTATOR = '&>', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +CREATE OPERATOR && ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap, + COMMUTATOR = '&&', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +CREATE OPERATOR &> ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_over_right, + COMMUTATOR = '&<', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +CREATE OPERATOR >> ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_right, + COMMUTATOR = '<<', + RESTRICT = positionsel, JOIN = positionjoinsel +); + +CREATE OPERATOR = ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_same, + COMMUTATOR = '=', NEGATOR = '<>', + RESTRICT = eqsel, JOIN = eqjoinsel, + SORT1 = '<', SORT2 = '<' +); + +CREATE OPERATOR <> ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_different, + COMMUTATOR = '<>', NEGATOR = '=', + RESTRICT = neqsel, JOIN = neqjoinsel +); + +CREATE OPERATOR @ ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains, + COMMUTATOR = '~', + RESTRICT = contsel, JOIN = contjoinsel +); + +CREATE OPERATOR ~ ( + LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained, + COMMUTATOR = '@', + RESTRICT = contsel, JOIN = contjoinsel +); + + +-- define the GiST support methods +CREATE FUNCTION g_cube_consistent(opaque,cube,int4) RETURNS bool + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +CREATE FUNCTION g_cube_compress(opaque) RETURNS opaque + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +CREATE FUNCTION g_cube_decompress(opaque) RETURNS opaque + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +CREATE FUNCTION g_cube_penalty(opaque,opaque,opaque) RETURNS opaque + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +CREATE FUNCTION g_cube_picksplit(opaque, opaque) RETURNS opaque + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +CREATE FUNCTION g_cube_union(bytea, opaque) RETURNS cube + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + +CREATE FUNCTION g_cube_same(cube, cube, opaque) RETURNS opaque + AS 'MODULE_PATHNAME' LANGUAGE 'c'; + + +-- register the default opclass for indexing +INSERT INTO pg_opclass (opcname, opcdeftype) + SELECT 'gist_cube_ops', oid + FROM pg_type + WHERE typname = 'cube'; + + +-- get the comparators for boxes and store them in a tmp table +SELECT o.oid AS opoid, o.oprname +INTO TABLE gist_cube_ops_tmp +FROM pg_operator o, pg_type t +WHERE o.oprleft = t.oid and o.oprright = t.oid + and t.typname = 'cube'; + +-- make sure we have the right operators +-- SELECT * from gist_cube_ops_tmp; + +-- using the tmp table, generate the amop entries + +-- cube_left +INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) + SELECT am.oid, opcl.oid, c.opoid, 1 + FROM pg_am am, pg_opclass opcl, gist_cube_ops_tmp c + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and c.oprname = '<<'; + +-- cube_over_left +INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) + SELECT am.oid, opcl.oid, c.opoid, 2 + FROM pg_am am, pg_opclass opcl, gist_cube_ops_tmp c + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and c.oprname = '&<'; + +-- cube_overlap +INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) + SELECT am.oid, opcl.oid, c.opoid, 3 + FROM pg_am am, pg_opclass opcl, gist_cube_ops_tmp c + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and c.oprname = '&&'; + +-- cube_over_right +INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) + SELECT am.oid, opcl.oid, c.opoid, 4 + FROM pg_am am, pg_opclass opcl, gist_cube_ops_tmp c + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and c.oprname = '&>'; + +-- cube_right +INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) + SELECT am.oid, opcl.oid, c.opoid, 5 + FROM pg_am am, pg_opclass opcl, gist_cube_ops_tmp c + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and c.oprname = '>>'; + +-- cube_same +INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) + SELECT am.oid, opcl.oid, c.opoid, 6 + FROM pg_am am, pg_opclass opcl, gist_cube_ops_tmp c + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and c.oprname = '='; + +-- cube_contains +INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) + SELECT am.oid, opcl.oid, c.opoid, 7 + FROM pg_am am, pg_opclass opcl, gist_cube_ops_tmp c + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and c.oprname = '@'; + +-- cube_contained +INSERT INTO pg_amop (amopid, amopclaid, amopopr, amopstrategy) + SELECT am.oid, opcl.oid, c.opoid, 8 + FROM pg_am am, pg_opclass opcl, gist_cube_ops_tmp c + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and c.oprname = '~'; + +DROP TABLE gist_cube_ops_tmp; + + +-- add the entries to amproc for the support methods +-- note the amprocnum numbers associated with each are specific! + +INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum) + SELECT am.oid, opcl.oid, pro.oid, 1 + FROM pg_am am, pg_opclass opcl, pg_proc pro + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and proname = 'g_cube_consistent'; + +INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum) + SELECT am.oid, opcl.oid, pro.oid, 2 + FROM pg_am am, pg_opclass opcl, pg_proc pro + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and proname = 'g_cube_union'; + +INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum) + SELECT am.oid, opcl.oid, pro.oid, 3 + FROM pg_am am, pg_opclass opcl, pg_proc pro + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and proname = 'g_cube_compress'; + +INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum) + SELECT am.oid, opcl.oid, pro.oid, 4 + FROM pg_am am, pg_opclass opcl, pg_proc pro + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and proname = 'g_cube_decompress'; + +INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum) + SELECT am.oid, opcl.oid, pro.oid, 5 + FROM pg_am am, pg_opclass opcl, pg_proc pro + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and proname = 'g_cube_penalty'; + +INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum) + SELECT am.oid, opcl.oid, pro.oid, 6 + FROM pg_am am, pg_opclass opcl, pg_proc pro + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and proname = 'g_cube_picksplit'; + +INSERT INTO pg_amproc (amid, amopclaid, amproc, amprocnum) + SELECT am.oid, opcl.oid, pro.oid, 7 + FROM pg_am am, pg_opclass opcl, pg_proc pro + WHERE amname = 'gist' and opcname = 'gist_cube_ops' + and proname = 'g_cube_same'; + +END TRANSACTION; diff --git a/contrib/cube/cubedata.h b/contrib/cube/cubedata.h new file mode 100644 index 0000000000..16495e74a5 --- /dev/null +++ b/contrib/cube/cubedata.h @@ -0,0 +1,7 @@ +/*#include "postgres.h"*/ + +typedef struct NDBOX { + unsigned int size; /* required to be a Postgres varlena type */ + unsigned int dim; + float x[1]; +} NDBOX; diff --git a/contrib/cube/cubeparse.y b/contrib/cube/cubeparse.y new file mode 100644 index 0000000000..33b7c1ef6b --- /dev/null +++ b/contrib/cube/cubeparse.y @@ -0,0 +1,252 @@ +%{ +/* NdBox = [(lowerleft),(upperright)] */ +/* [(xLL(1)...xLL(N)),(xUR(1)...xUR(n))] */ + +#define YYERROR_VERBOSE +#define YYPARSE_PARAM result /* need this to pass a pointer (void *) to yyparse */ +#define YYSTYPE char * +#define YYDEBUG 1 + +#include +#include "cubedata.h" +#include "buffer.h" + +#include "postgres.h" +#include "utils/palloc.h" +#include "utils/elog.h" + +#undef yylex /* falure to redefine yylex will result in a call to the */ +#define yylex cube_yylex /* wrong scanner when running inside the postgres backend */ + +extern int yylex(); /* defined as cube_yylex in cubescan.c */ +extern int errno; + +int cube_yyerror( char *msg ); +int cube_yyparse(void *result); + +static int delim_count(char *s, char delim); +static NDBOX * write_box(unsigned int dim, char *str1, char *str2); +static NDBOX * write_point_as_box(char *s); + +%} + +/* BISON Declarations */ +%token FLOAT O_PAREN C_PAREN O_BRACKET C_BRACKET COMMA +%start box + +/* Grammar follows */ +%% + +box: + O_BRACKET paren_list COMMA paren_list C_BRACKET { + + int dim; + int c = parse_buffer_curr_char(); + int pos = parse_buffer_pos(); + + /* We can't let the parser recognize more than one valid expression: + the job is done and memory is allocated. */ + if ( c != '\0' ) { + /* Not at EOF */ + reset_parse_buffer(); + elog(ERROR, "(0) bad cube representation; garbage at or before char %d, ('%c', \\%03o)\n", pos, c, c ); + YYERROR; + } + + dim = delim_count($2, ',') + 1; + if ( (delim_count($4, ',') + 1) != dim ) { + reset_parse_buffer(); + elog(ERROR, "(1) bad cube representation; different point dimensions in (%s) and (%s)\n", $2, $4); + YYABORT; + } + + *((void **)result) = write_box( dim, $2, $4 ); + + } + | + paren_list COMMA paren_list { + int dim; + int c = parse_buffer_curr_char(); + int pos = parse_buffer_pos(); + + if ( c != '\0' ) { /* Not at EOF */ + reset_parse_buffer(); + elog(ERROR, "(2) bad cube representation; garbage at or before char %d, ('%c', \\%03o)\n", pos, c, c ); + YYABORT; + } + + dim = delim_count($1, ',') + 1; + + if ( (delim_count($3, ',') + 1) != dim ) { + reset_parse_buffer(); + elog(ERROR, "(3) bad cube representation; different point dimensions in (%s) and (%s)\n", $1, $3); + YYABORT; + } + + *((void **)result) = write_box( dim, $1, $3 ); + } + | + + paren_list { + int c = parse_buffer_curr_char(); + int pos = parse_buffer_pos(); + + if ( c != '\0') { /* Not at EOF */ + reset_parse_buffer(); + elog(ERROR, "(4) bad cube representation; garbage at or before char %d, ('%c', \\%03o)\n", pos, c, c ); + YYABORT; + } + + if ( yychar != YYEOF) { + /* There's still a lookahead token to be parsed */ + reset_parse_buffer(); + elog(ERROR, "(5) bad cube representation; garbage at or before char %d, ('end of input', \\%03o)\n", pos, c); + YYABORT; + } + + *((void **)result) = write_point_as_box($1); + } + + | + + list { + int c = parse_buffer_curr_char(); + int pos = parse_buffer_pos(); + + if ( c != '\0') { /* Not at EOF */ + reset_parse_buffer(); + elog(ERROR, "(6) bad cube representation; garbage at or before char %d, ('%c', \\%03o)\n", pos, c, c); + YYABORT; + } + + if ( yychar != YYEOF) { + /* There's still a lookahead token to be parsed */ + reset_parse_buffer(); + elog(ERROR, "(7) bad cube representation; garbage at or before char %d, ('end of input', \\%03o)\n", pos, c); + YYABORT; + } + + *((void **)result) = write_point_as_box($1); + } + ; + +paren_list: + O_PAREN list C_PAREN { + $$ = $2; + } + ; + +list: + FLOAT { + $$ = palloc(strlen(parse_buffer()) + 1); + strcpy($$, $1); + } + | + list COMMA FLOAT { + $$ = $1; + strcat($$, ","); + strcat($$, $3); + } + ; + +%% + + +int cube_yyerror ( char *msg ) { + char *buf = (char *) palloc(256); + int position; + + yyclearin; + + if ( !strcmp(msg, "parse error, expecting `$'") ) { + msg = "expecting end of input"; + } + + position = parse_buffer_pos() > parse_buffer_size() ? parse_buffer_pos() - 1 : parse_buffer_pos(); + + sprintf( + buf, + "%s at or before position %d, character ('%c', \\%03o), input: '%s'\n", + msg, + position, + parse_buffer()[position - 1], + parse_buffer()[position - 1], + parse_buffer() + ); + + reset_parse_buffer(); + elog(ERROR, buf); + return 0; +} + +static int +delim_count(char *s, char delim) +{ + int ndelim = 0; + + while ((s = strchr(s, delim)) != NULL) + { + ndelim++; + s++; + } + return (ndelim); +} + +static NDBOX * +write_box(unsigned int dim, char *str1, char *str2) +{ + NDBOX * bp; + char * s; + int i; + int size = offsetof(NDBOX, x[0]) + sizeof(float) * dim * 2; + + bp = palloc(size); + bp->size = size; + bp->dim = dim; + + s = str1; + bp->x[i=0] = strtod(s, NULL); + while ((s = strchr(s, ',')) != NULL) { + s++; i++; + bp->x[i] = strtod(s, NULL); + } + + s = str2; + bp->x[i=dim] = strtod(s, NULL); + while ((s = strchr(s, ',')) != NULL) { + s++; i++; + bp->x[i] = strtod(s, NULL); + } + + return(bp); +} + + +static NDBOX * write_point_as_box(char *str) +{ + NDBOX * bp; + int i, size; + double x; + int dim = delim_count(str, ',') + 1; + char * s = str; + + size = offsetof(NDBOX, x[0]) + sizeof(float) * dim * 2; + + bp = palloc(size); + bp->size = size; + bp->dim = dim; + + i = 0; + x = strtod(s, NULL); + bp->x[0] = x; + bp->x[dim] = x; + while ((s = strchr(s, ',')) != NULL) { + s++; i++; + x = strtod(s, NULL); + bp->x[i] = x; + bp->x[i+dim] = x; + } + + return(bp); +} + diff --git a/contrib/cube/cubescan.l b/contrib/cube/cubescan.l new file mode 100644 index 0000000000..e10e7faad8 --- /dev/null +++ b/contrib/cube/cubescan.l @@ -0,0 +1,54 @@ +%{ +/* +** A scanner for EMP-style numeric ranges +*/ + +#define YYSTYPE char * +#define yylval cube_yylval + +#include +#include "cubeparse.h" +#include "buffer.h" + +#define YY_NO_UNPUT 1 +#undef yywrap + +/* flex screws a couple symbols when used with the -P otion; fix those */ +#define YY_DECL int cube_yylex YY_PROTO(( void )); \ +int cube_yylex YY_PROTO(( void )) + +/* redefined YY_INPUT reads byte-wise from the memory area defined in buffer.c */ +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ +{ \ + int c = read_parse_buffer(); \ + result = (c == '\0') ? YY_NULL : (buf[0] = c, 1); \ +} + +void cube_flush_scanner_buffer(void); +%} + +n [0-9]+ +integer [+-]?{n} +real [+-]?({n}\.{n}?)|(\.{n}) +float ({integer}|{real})([eE]{integer})? + +%% + +{float} yylval = yytext; return FLOAT; +\[ yylval = "("; return O_BRACKET; +\] yylval = ")"; return C_BRACKET; +\( yylval = "("; return O_PAREN; +\) yylval = ")"; return C_PAREN; +\, yylval = ")"; return COMMA; +[ ]+ /* discard spaces */ +. return yytext[0]; /* alert parser of the garbage */ + +%% + +int cube_yylex(); + +void cube_flush_scanner_buffer(void) { + fprintf(stderr, "cube_flush_scanner_buffer called\n"); + YY_FLUSH_BUFFER; +} diff --git a/contrib/cube/data/test_cube.data b/contrib/cube/data/test_cube.data new file mode 100644 index 0000000000..d67cd122cf --- /dev/null +++ b/contrib/cube/data/test_cube.data @@ -0,0 +1,3100 @@ +(12699,9028),(12654,8987) +(22689,4680),(22614,4626) +(43263,47296),(43217,47217) +(6184,8397),(6182,8379) +(863,28537),(788,28456) +(33783,4733),(33746,4693) +(40456,47134),(40426,47087) +(45950,8153),(45887,8060) +(33433,36474),(33399,36460) +(41106,22017),(41086,21962) +(19214,36781),(19179,36767) +(11582,40823),(11498,40737) +(35565,5404),(35546,5360) +(26489,17387),(26405,17356) +(30874,13849),(30796,13814) +(38255,1619),(38227,1593) +(4445,32006),(4405,31914) +(3923,32921),(3876,32913) +(36054,39464),(36032,39434) +(46540,6780),(46524,6758) +(12184,45811),(12118,45787) +(13198,17090),(13143,17051) +(30939,44578),(30865,44486) +(12502,4939),(12431,4902) +(3250,1108),(3169,1063) +(34029,41240),(33976,41180) +(47057,44018),(46967,43927) +(699,10114),(686,10058) +(5925,26020),(5845,25979) +(9462,39388),(9382,39388) +(270,32616),(226,32607) +(3959,49145),(3861,49115) +(207,40886),(179,40879) +(48480,43312),(48412,43233) +(37183,37209),(37161,37110) +(13576,13505),(13521,13487) +(5877,1037),(5818,1036) +(6777,16694),(6776,16692) +(49362,13905),(49299,13845) +(29356,14606),(29313,14562) +(5492,6976),(5441,6971) +(288,49588),(204,49571) +(36698,37213),(36682,37158) +(718,41336),(645,41272) +(8725,23369),(8660,23333) +(40115,9894),(40025,9818) +(40051,41181),(40015,41153) +(5739,1740),(5715,1731) +(25120,27935),(25054,27876) +(27475,46084),(27447,46003) +(33197,3252),(33161,3245) +(10892,15691),(10869,15662) +(39012,44712),(38995,44640) +(4506,6484),(4458,6459) +(13970,26316),(13964,26236) +(28009,28104),(27968,28030) +(5991,27613),(5906,27607) +(23649,6338),(23610,6314) +(25942,10008),(25911,9928) +(25651,29943),(25590,29906) +(24555,40334),(24546,40330) +(46870,43762),(46789,43709) +(20030,2752),(19945,2687) +(30758,26754),(30718,26678) +(4320,44673),(4286,44625) +(1011,15576),(939,15574) +(41936,40699),(41854,40655) +(20594,19002),(20561,18995) +(9388,41056),(9325,41042) +(34771,46693),(34751,46645) +(49398,46359),(49332,46357) +(23115,35380),(23036,35306) +(46305,34840),(46283,34765) +(16768,21692),(16691,21647) +(28695,3128),(28654,3112) +(22182,7107),(22107,7074) +(14567,1210),(14468,1139) +(14156,37139),(14136,37119) +(33500,38351),(33477,38286) +(39983,41981),(39944,41954) +(26773,20824),(26719,20813) +(42516,22947),(42460,22932) +(26127,10701),(26044,10650) +(17808,13803),(17724,13710) +(14913,49873),(14849,49836) +(37013,820),(36955,736) +(39071,1399),(39022,1381) +(9785,42546),(9687,42540) +(13423,14066),(13354,14052) +(3417,14558),(3336,14478) +(25212,46368),(25128,46316) +(10124,39848),(10027,39820) +(39722,39226),(39656,39162) +(6298,28101),(6250,28076) +(45852,5846),(45809,5750) +(48292,4885),(48290,4841) +(18905,4454),(18894,4424) +(18965,43474),(18902,43444) +(39843,28239),(39761,28199) +(18087,44660),(18019,44632) +(33886,10382),(33794,10286) +(38383,13163),(38362,13092) +(18861,25050),(18842,24965) +(29887,14326),(29806,14274) +(18733,11644),(18698,11644) +(5119,37952),(5089,37950) +(16191,34884),(16149,34864) +(29544,1104),(29496,1062) +(27740,41555),(27701,41540) +(4672,4087),(4633,4060) +(45441,38994),(45377,38958) +(3272,1176),(3232,1146) +(12820,26606),(12790,26575) +(30910,7590),(30877,7512) +(42476,39152),(42377,39127) +(6562,38490),(6542,38447) +(30046,20332),(29988,20259) +(40723,15950),(40671,15949) +(4945,46857),(4908,46817) +(47986,16882),(47963,16877) +(9842,22339),(9805,22305) +(29831,23169),(29818,23122) +(12322,34404),(12250,34312) +(22846,11091),(22759,10992) +(47627,2424),(47603,2397) +(18375,43632),(18347,43577) +(40441,974),(40394,965) +(34260,10573),(34194,10522) +(32914,9549),(32828,9503) +(49023,37827),(48978,37799) +(22183,10691),(22111,10669) +(38036,15828),(38014,15759) +(34604,16801),(34508,16746) +(26737,29997),(26675,29976) +(47375,40298),(47293,40210) +(771,2661),(732,2649) +(28514,25659),(28504,25577) +(13438,46494),(13376,46455) +(7187,17877),(7125,17786) +(49957,43390),(49897,43384) +(26543,20067),(26482,20057) +(16416,29803),(16385,29724) +(36353,7484),(36286,7414) +(26498,3377),(26415,3358) +(28990,32205),(28936,32193) +(45005,3842),(45001,3816) +(21672,23566),(21603,23566) +(33360,43465),(33302,43429) +(29884,9544),(29838,9520) +(5599,15012),(5596,14930) +(22396,21481),(22344,21422) +(24810,14955),(24780,14887) +(47114,18866),(47081,18784) +(39013,39245),(38953,39237) +(12863,40534),(12803,40529) +(351,37068),(310,37019) +(12916,34327),(12891,34240) +(49191,2694),(49170,2628) +(24127,38407),(24050,38325) +(3264,23053),(3213,23007) +(8172,30385),(8144,30336) +(19630,35716),(19573,35640) +(42554,5148),(42521,5117) +(42168,33453),(42136,33426) +(17732,32093),(17666,32057) +(1039,16626),(1037,16587) +(21287,7757),(21265,7679) +(47063,8260),(47039,8225) +(38645,16238),(38561,16204) +(18258,25358),(18196,25341) +(30458,1742),(30458,1695) +(35147,9273),(35121,9233) +(7670,16625),(7642,16545) +(49503,23432),(49484,23383) +(31089,23146),(31062,23093) +(47758,2734),(47670,2703) +(35276,1027),(35259,972) +(26337,17603),(26313,17579) +(35649,16777),(35626,16777) +(42454,5105),(42362,5101) +(21682,24951),(21646,24920) +(48383,25174),(48303,25156) +(14672,3532),(14601,3460) +(22570,22587),(22515,22512) +(23566,25623),(23484,25573) +(9530,24542),(9504,24459) +(41271,451),(41236,401) +(5556,37528),(5502,37527) +(12479,25042),(12447,24991) +(16568,22916),(16499,22864) +(42700,13084),(42676,12992) +(35523,40973),(35504,40932) +(32948,16962),(32857,16901) +(7808,13469),(7712,13469) +(13920,35203),(13870,35131) +(22731,31563),(22658,31557) +(22909,43956),(22900,43857) +(33077,35080),(33074,35030) +(48064,29307),(48022,29280) +(20232,46682),(20212,46613) +(29949,16790),(29867,16711) +(30260,32029),(30180,31979) +(17184,34503),(17110,34482) +(16066,42687),(16039,42648) +(2947,19819),(2857,19788) +(4900,47934),(4818,47894) +(27193,19014),(27174,18976) +(15597,27948),(15590,27939) +(11090,28623),(11002,28589) +(26956,18651),(26920,18620) +(3107,47753),(3103,47711) +(6745,24151),(6711,24083) +(43923,19213),(43871,19124) +(33451,23578),(33370,23534) +(8944,20605),(8862,20601) +(14905,7536),(14892,7441) +(2412,18357),(2383,18354) +(37060,1443),(36974,1366) +(15501,6230),(15429,6190) +(30333,50),(30273,6) +(35567,9965),(35482,9912) +(49847,7128),(49798,7067) +(27685,36396),(27668,36384) +(43832,18491),(43825,18431) +(36849,34600),(36785,34589) +(2348,47938),(2307,47902) +(20473,22131),(20445,22113) +(38486,4293),(38471,4288) +(30611,30451),(30553,30400) +(3883,21299),(3819,21260) +(7696,37555),(7644,37534) +(22399,7913),(22317,7911) +(42565,38605),(42500,38598) +(36595,12151),(36500,12106) +(587,35217),(571,35123) +(5764,15300),(5764,15231) +(12003,21265),(11983,21210) +(42564,4803),(42470,4737) +(42359,36834),(42271,36746) +(44700,14680),(44658,14670) +(19690,5627),(19620,5607) +(17780,43602),(17714,43565) +(45073,3491),(45041,3434) +(35043,2136),(35017,2084) +(39653,19215),(39646,19198) +(23970,25560),(23935,25502) +(28698,49233),(28600,49223) +(30266,3605),(30245,3540) +(25538,7857),(25500,7791) +(17711,1757),(17708,1756) +(5248,594),(5190,587) +(2730,32454),(2671,32436) +(1722,49089),(1635,49067) +(40954,5743),(40921,5722) +(21382,4426),(21298,4331) +(7885,18629),(7872,18605) +(42838,6459),(42748,6451) +(8217,19894),(8207,19845) +(20489,18524),(20433,18520) +(17383,23559),(17309,23515) +(38952,38968),(38934,38913) +(44665,18137),(44636,18051) +(22416,41220),(22383,41213) +(9901,664),(9818,646) +(23475,21981),(23449,21973) +(41875,17991),(41818,17988) +(36517,47731),(36509,47713) +(37595,49849),(37581,49834) +(38771,32720),(38748,32684) +(810,38523),(736,38452) +(29695,14942),(29665,14907) +(31911,15168),(31906,15113) +(3454,36839),(3438,36831) +(4832,47554),(4820,47473) +(11590,8292),(11539,8272) +(8193,33323),(8106,33317) +(16043,14799),(16001,14710) +(19574,11395),(19514,11316) +(26290,41424),(26224,41342) +(22844,12516),(22807,12471) +(15709,49580),(15655,49553) +(13387,28084),(13379,28066) +(2780,38807),(2690,38711) +(22031,32458),(22028,32377) +(13511,3351),(13440,3297) +(14648,26473),(14614,26383) +(17798,19885),(17726,19852) +(32355,27940),(32324,27861) +(43773,21031),(43767,20985) +(15419,45759),(15403,45666) +(770,38863),(729,38806) +(21221,35619),(21183,35596) +(38924,31021),(38894,30961) +(7395,32439),(7345,32416) +(2324,25118),(2268,25074) +(2958,15089),(2935,15087) +(2424,160),(2424,81) +(12123,18644),(12099,18616) +(7459,30276),(7422,30218) +(15847,45488),(15814,45428) +(26409,29897),(26389,29863) +(12336,34322),(12279,34322) +(9440,23550),(9396,23466) +(4991,30850),(4905,30768) +(47262,11940),(47201,11939) +(30584,42868),(30555,42838) +(23144,24089),(23056,24067) +(35930,11609),(35847,11573) +(7812,17271),(7789,17203) +(17946,37554),(17878,37480) +(27356,32869),(27298,32813) +(29971,47783),(29933,47697) +(26075,46494),(25988,46451) +(39314,41366),(39289,41269) +(31708,42900),(31688,42865) +(4510,10231),(4439,10203) +(43806,8482),(43758,8446) +(45990,49694),(45927,49617) +(48815,27640),(48782,27573) +(41675,26733),(41622,26723) +(23229,7709),(23175,7693) +(48976,17733),(48962,17731) +(10686,41470),(10597,41434) +(18053,27059),(17989,27012) +(35495,25950),(35459,25912) +(41896,45014),(41881,44999) +(22654,41896),(22572,41801) +(18581,7087),(18524,6988) +(14697,22406),(14681,22311) +(40092,28122),(40043,28030) +(35844,24243),(35816,24238) +(1254,25653),(1250,25644) +(1603,21730),(1556,21640) +(33048,21779),(32991,21763) +(29979,1632),(29916,1592) +(8620,633),(8580,620) +(22992,27035),(22932,27008) +(21409,29315),(21390,29309) +(3610,44748),(3547,44699) +(20402,9318),(20343,9267) +(31001,8709),(30908,8658) +(46840,47640),(46773,47551) +(49173,4705),(49143,4630) +(5339,31657),(5251,31622) +(8644,49668),(8630,49648) +(45387,2893),(45309,2885) +(47641,31020),(47584,30941) +(40238,10636),(40208,10568) +(19247,36924),(19227,36924) +(917,19957),(827,19887) +(40967,17841),(40870,17820) +(15850,4109),(15794,4085) +(20181,30916),(20085,30870) +(161,24465),(107,24374) +(21737,49690),(21667,49663) +(10328,20911),(10232,20852) +(24187,49823),(24128,49768) +(36084,4578),(36007,4501) +(38771,31741),(38673,31674) +(2202,30102),(2111,30006) +(27322,16074),(27228,16039) +(6843,17280),(6765,17248) +(16972,39744),(16912,39700) +(10608,38741),(10553,38708) +(4917,34801),(4828,34766) +(39281,33659),(39268,33618) +(31706,7119),(31645,7063) +(3427,44006),(3422,44004) +(10134,42608),(10044,42599) +(26294,32080),(26200,32068) +(21777,34680),(21769,34606) +(23373,25957),(23314,25915) +(10710,8401),(10681,8400) +(42062,19458),(42019,19394) +(26530,43036),(26458,43004) +(3394,46081),(3360,46077) +(38743,33953),(38677,33924) +(32438,8226),(32345,8160) +(9210,27333),(9118,27301) +(19594,1600),(19568,1551) +(10003,12278),(9952,12255) +(31737,7206),(31650,7146) +(16594,15821),(16502,15759) +(28208,30296),(28189,30278) +(30602,46237),(30555,46185) +(20715,5155),(20697,5140) +(48892,35271),(48793,35210) +(3175,5590),(3113,5525) +(34220,27947),(34132,27865) +(35105,39792),(35011,39727) +(21919,27314),(21839,27286) +(23963,3723),(23917,3699) +(16312,14078),(16236,14045) +(19233,49824),(19185,49794) +(1447,11768),(1356,11699) +(17311,17709),(17224,17653) +(11962,31709),(11871,31627) +(21355,40131),(21355,40085) +(33750,35273),(33724,35180) +(38896,25539),(38879,25524) +(39569,44899),(39569,44893) +(11075,41547),(11039,41500) +(3215,12202),(3199,12127) +(46215,33458),(46132,33455) +(15121,38012),(15083,37974) +(44448,18726),(44412,18690) +(3899,38263),(3870,38262) +(13854,13353),(13786,13298) +(8252,5402),(8191,5320) +(46849,37968),(46820,37897) +(16422,13957),(16376,13897) +(47369,7665),(47353,7629) +(11982,40874),(11956,40806) +(9552,27580),(9496,27562) +(32247,19399),(32176,19337) +(32704,2169),(32635,2091) +(7471,44213),(7411,44130) +(48433,7096),(48379,7089) +(37357,6543),(37338,6452) +(30460,29624),(30433,29535) +(20350,28794),(20341,28705) +(6326,32360),(6267,32317) +(1711,47519),(1654,47430) +(49540,16510),(49521,16426) +(26975,618),(26908,579) +(24118,30880),(24020,30821) +(3675,15477),(3625,15418) +(44953,9577),(44953,9530) +(38323,7965),(38235,7910) +(6629,36482),(6579,36448) +(33953,16460),(33878,16408) +(49222,16790),(49186,16695) +(17308,16951),(17274,16904) +(14135,6888),(14077,6833) +(38617,47768),(38603,47760) +(7345,10992),(7290,10914) +(35261,42152),(35176,42096) +(28586,4809),(28544,4735) +(37521,25299),(37495,25217) +(41941,17954),(41912,17915) +(1209,46863),(1171,46863) +(20103,34947),(20048,34896) +(32716,33816),(32656,33769) +(11113,6531),(11036,6467) +(48635,7321),(48563,7262) +(28435,37059),(28349,37014) +(12311,17208),(12232,17112) +(1466,48010),(1379,48008) +(11226,11997),(11223,11925) +(46896,32540),(46821,32510) +(32661,31255),(32632,31187) +(37739,20376),(37655,20306) +(44002,43326),(43920,43257) +(30337,1023),(30271,968) +(34436,23357),(34432,23345) +(21367,8168),(21353,8091) +(36370,21611),(36369,21569) +(4152,36488),(4080,36476) +(17696,13924),(17664,13853) +(34252,19395),(34159,19316) +(12574,3072),(12573,2975) +(3995,21243),(3943,21167) +(44553,30126),(44513,30108) +(4599,45275),(4552,45254) +(33191,11404),(33176,11348) +(14245,18633),(14177,18540) +(32457,20705),(32393,20700) +(40052,10499),(40016,10457) +(29824,44065),(29785,44037) +(31613,12565),(31557,12543) +(42692,29000),(42652,28996) +(40680,22219),(40603,22140) +(33575,27661),(33488,27644) +(46194,1385),(46184,1355) +(38442,48501),(38407,48426) +(25305,21544),(25236,21523) +(15562,8226),(15561,8208) +(20844,43614),(20752,43558) +(22566,30541),(22554,30532) +(2760,47802),(2672,47789) +(25515,30745),(25433,30675) +(48382,45134),(48382,45093) +(9940,27094),(9871,27087) +(48690,44361),(48610,44338) +(18992,11585),(18899,11582) +(21551,49983),(21492,49885) +(46778,29113),(46770,29071) +(43219,9593),(43212,9548) +(40291,1248),(40224,1190) +(12687,22225),(12635,22219) +(49372,38790),(49306,38721) +(49503,46808),(49411,46798) +(24745,5162),(24732,5138) +(5046,26517),(5023,26424) +(5583,46538),(5495,46531) +(6084,35950),(6079,35895) +(3503,23096),(3437,23024) +(45275,8420),(45244,8418) +(13514,45251),(13491,45249) +(42112,2748),(42047,2668) +(7810,21907),(7806,21878) +(48378,36029),(48303,35979) +(32568,48605),(32510,48563) +(859,18915),(810,18915) +(41963,17950),(41939,17915) +(42723,8031),(42685,7955) +(19587,5965),(19556,5961) +(8713,33083),(8629,32996) +(21243,7769),(21226,7740) +(43752,43026),(43720,42944) +(7883,41311),(7859,41242) +(10178,47874),(10157,47826) +(32177,48725),(32093,48646) +(22960,2784),(22953,2774) +(25101,49159),(25087,49090) +(32142,48915),(32086,48850) +(6636,44887),(6590,44825) +(37814,11606),(37769,11578) +(2870,23198),(2820,23121) +(21025,16364),(20947,16271) +(31341,36137),(31269,36114) +(38921,7906),(38888,7831) +(6966,17259),(6922,17199) +(32426,13344),(32401,13253) +(8084,30572),(8078,30572) +(42230,47674),(42150,47603) +(20724,44854),(20724,44830) +(27471,38453),(27454,38430) +(24590,37973),(24544,37941) +(45832,26077),(45772,26031) +(9589,24239),(9582,24156) +(37484,49472),(37409,49432) +(30044,19340),(30004,19333) +(16966,14632),(16936,14572) +(9439,40491),(9403,40482) +(28945,5814),(28913,5805) +(43788,41302),(43746,41231) +(33631,43451),(33614,43354) +(17590,49396),(17510,49324) +(15173,32572),(15109,32507) +(1912,23580),(1840,23504) +(38165,16185),(38076,16154) +(6729,1179),(6637,1177) +(6994,45406),(6983,45325) +(2912,21327),(2908,21305) +(14678,14244),(14659,14222) +(29944,14959),(29898,14900) +(47432,35658),(47407,35610) +(25542,39243),(25466,39149) +(5330,7206),(5304,7165) +(24790,27196),(24695,27118) +(38806,1961),(38795,1906) +(23290,4487),(23212,4416) +(35035,24337),(34990,24297) +(5549,38948),(5549,38891) +(24558,15492),(24501,15425) +(4636,3011),(4574,2933) +(26522,39986),(26451,39940) +(33486,18424),(33410,18366) +(36638,14324),(36625,14287) +(35115,41236),(35055,41191) +(31927,16896),(31841,16806) +(5796,43937),(5697,43886) +(25681,41645),(25663,41608) +(10962,42777),(10894,42732) +(32715,11026),(32672,10991) +(45803,20406),(45710,20371) +(34730,17672),(34658,17606) +(8809,6323),(8798,6232) +(39471,23837),(39390,23749) +(34078,17435),(33987,17433) +(9133,4544),(9041,4509) +(47274,29126),(47242,29060) +(6404,28488),(6403,28475) +(48894,49751),(48846,49694) +(17324,43023),(17301,42972) +(15599,8433),(15557,8386) +(48575,10202),(48488,10175) +(27638,24428),(27608,24378) +(45277,47456),(45240,47422) +(26482,46607),(26482,46570) +(41400,33898),(41397,33802) +(49853,18504),(49848,18503) +(11528,25165),(11476,25080) +(49902,41752),(49818,41746) +(1956,47506),(1922,47424) +(21834,22058),(21802,21964) +(19414,21842),(19386,21822) +(34801,13722),(34744,13681) +(13924,29243),(13835,29160) +(47749,21986),(47664,21894) +(47051,39582),(46974,39489) +(31287,49923),(31236,49913) +(47429,8625),(47337,8585) +(46987,44364),(46901,44277) +(16158,27510),(16099,27467) +(41184,6400),(41148,6317) +(1847,42471),(1829,42426) +(14409,48602),(14320,48555) +(38137,42951),(38045,42918) +(42875,2312),(42832,2243) +(27242,30617),(27181,30535) +(24882,44559),(24812,44548) +(22021,1596),(22015,1581) +(24300,1523),(24250,1443) +(43946,35909),(43869,35868) +(816,15988),(776,15967) +(25243,9401),(25237,9332) +(27967,25958),(27928,25949) +(6575,33949),(6484,33900) +(44812,35980),(44800,35913) +(37577,13064),(37495,13019) +(30891,29967),(30814,29884) +(15829,28836),(15753,28807) +(11128,34180),(11126,34117) +(9834,12537),(9801,12508) +(4899,29069),(4809,29024) +(29370,38459),(29276,38382) +(40743,46653),(40647,46559) +(9618,2723),(9578,2631) +(32542,26837),(32515,26769) +(5625,13409),(5576,13355) +(47490,19229),(47472,19203) +(48118,40275),(48063,40203) +(19245,20549),(19227,20546) +(25312,22243),(25280,22164) +(18797,28934),(18723,28881) +(31609,49393),(31512,49366) +(26183,32888),(26135,32824) +(46198,26153),(46180,26149) +(45383,16904),(45353,16888) +(7132,11408),(7091,11338) +(48262,43227),(48236,43159) +(31722,12861),(31675,12810) +(41695,48924),(41691,48921) +(48318,12877),(48287,12802) +(12069,32241),(11978,32231) +(8395,2694),(8380,2661) +(19552,34590),(19550,34497) +(12203,26166),(12187,26143) +(35745,9571),(35654,9542) +(22384,22535),(22352,22439) +(21459,28189),(21360,28189) +(7418,7203),(7343,7182) +(39497,48412),(39413,48318) +(1058,11132),(979,11051) +(45623,31417),(45548,31381) +(23887,31921),(23876,31891) +(7797,1244),(7785,1155) +(23679,43650),(23594,43644) +(21891,30561),(21833,30485) +(4069,6870),(4019,6785) +(5134,25117),(5103,25034) +(36101,41895),(36085,41810) +(39617,39211),(39544,39191) +(37437,6604),(37434,6585) +(7749,32601),(7740,32515) +(26203,34991),(26159,34946) +(31856,39006),(31783,39003) +(45828,24767),(45788,24723) +(49836,35965),(49757,35871) +(44113,49024),(44033,48995) +(38237,22326),(38187,22253) +(45235,19087),(45190,19005) +(1588,45285),(1520,45254) +(46628,8701),(46552,8665) +(47707,18258),(47668,18250) +(9377,26162),(9325,26079) +(28331,16766),(28302,16731) +(15792,27875),(15727,27809) +(16454,1972),(16415,1967) +(21012,15828),(20972,15784) +(27465,30603),(27390,30560) +(39256,7697),(39225,7604) +(25908,32801),(25854,32770) +(25215,40109),(25201,40106) +(23280,4613),(23190,4596) +(32440,30879),(32405,30807) +(49156,4224),(49126,4126) +(20005,40423),(19911,40370) +(20978,8226),(20930,8170) +(32127,22611),(32126,22579) +(21764,26509),(21701,26455) +(32923,2834),(32914,2830) +(7499,25331),(7426,25300) +(6163,36942),(6107,36908) +(41118,14583),(41034,14486) +(21211,33369),(21208,33331) +(7899,27682),(7853,27603) +(16546,48436),(16535,48400) +(24898,40195),(24855,40174) +(43029,982),(43004,952) +(26266,7962),(26252,7950) +(11308,44367),(11210,44322) +(8902,28402),(8808,28334) +(11671,19619),(11665,19549) +(47202,23593),(47153,23505) +(21981,40220),(21905,40160) +(46721,2514),(46687,2471) +(3450,33839),(3424,33811) +(41854,45864),(41762,45792) +(40183,47816),(40114,47742) +(26119,33910),(26077,33816) +(3430,16518),(3365,16500) +(40063,32176),(40005,32166) +(38702,15253),(38679,15187) +(17719,12291),(17658,12257) +(46131,30669),(46068,30587) +(42738,10952),(42731,10907) +(8721,45155),(8650,45076) +(45317,26123),(45244,26113) +(42694,11561),(42614,11490) +(10043,12479),(10009,12391) +(27584,2345),(27578,2257) +(30889,8253),(30866,8167) +(5176,48928),(5107,48838) +(9781,21023),(9745,20976) +(32430,27908),(32404,27859) +(3984,7391),(3973,7352) +(18904,8094),(18842,8091) +(20573,5508),(20482,5496) +(7806,44368),(7753,44297) +(18875,41452),(18817,41376) +(6632,12142),(6566,12079) +(33066,17865),(33055,17854) +(45726,19628),(45714,19589) +(26971,18459),(26941,18423) +(26554,23641),(26515,23592) +(45503,1325),(45441,1231) +(11898,20164),(11880,20115) +(27868,22837),(27843,22776) +(34931,8206),(34855,8144) +(42375,33603),(42350,33539) +(3184,8308),(3129,8238) +(26667,15813),(26661,15785) +(5760,49617),(5730,49546) +(794,27001),(777,26992) +(13518,45289),(13459,45235) +(34430,29754),(34363,29736) +(37912,24574),(37880,24543) +(8130,2270),(8083,2258) +(26930,21516),(26848,21455) +(3634,33511),(3592,33489) +(33080,5036),(33035,4972) +(48389,13942),(48316,13915) +(9231,5298),(9150,5232) +(1357,10601),(1321,10548) +(35175,15295),(35091,15269) +(33917,36863),(33879,36784) +(8279,12052),(8239,12021) +(11868,19083),(11862,19034) +(24019,30777),(24006,30703) +(44619,6959),(44618,6938) +(28610,2626),(28523,2582) +(29579,41801),(29482,41775) +(23448,37609),(23396,37534) +(40676,11252),(40670,11191) +(39656,14077),(39564,13999) +(33060,31042),(33033,30950) +(11720,6816),(11654,6792) +(13775,28873),(13730,28868) +(47851,39121),(47802,39084) +(30923,40255),(30860,40199) +(44169,15070),(44085,15015) +(42574,28664),(42558,28590) +(8993,43487),(8941,43460) +(40782,11648),(40763,11631) +(18516,10143),(18423,10137) +(39068,551),(39005,491) +(39672,12000),(39575,11913) +(18508,37761),(18464,37712) +(19083,35318),(19079,35280) +(30286,13736),(30222,13672) +(7223,9164),(7132,9069) +(20764,29286),(20700,29210) +(5733,8063),(5699,8058) +(8566,43873),(8549,43797) +(22126,27444),(22062,27366) +(15105,8717),(15078,8660) +(43987,33145),(43940,33083) +(46833,38652),(46755,38612) +(47768,27202),(47681,27169) +(22792,1183),(22731,1152) +(25650,43310),(25562,43247) +(37084,20116),(37045,20057) +(47461,32556),(47423,32555) +(41225,18124),(41215,18117) +(17623,25218),(17553,25158) +(13770,21703),(13770,21700) +(48958,35441),(48870,35388) +(2976,1808),(2892,1802) +(45118,22318),(45049,22224) +(42287,26616),(42281,26560) +(25525,6327),(25468,6244) +(40756,31634),(40713,31568) +(23105,26565),(23078,26565) +(48268,39862),(48265,39827) +(41656,26254),(41567,26243) +(28062,17920),(28045,17825) +(6443,17321),(6402,17238) +(10191,45466),(10151,45447) +(18097,39706),(18043,39649) +(37592,3244),(37569,3197) +(29809,5978),(29762,5950) +(12145,11251),(12130,11202) +(37507,42999),(37446,42956) +(10820,2866),(10782,2830) +(36440,42904),(36421,42832) +(38370,3386),(38279,3311) +(9345,17279),(9313,17197) +(20477,14864),(20395,14807) +(37147,37769),(37110,37729) +(15325,36135),(15284,36053) +(29034,32897),(29009,32854) +(2116,22274),(2037,22216) +(15078,38330),(15048,38251) +(7968,33600),(7914,33573) +(832,23851),(770,23786) +(38669,4348),(38594,4344) +(8521,48573),(8425,48564) +(1060,43320),(969,43289) +(26170,10150),(26144,10069) +(32324,8539),(32285,8506) +(13121,18044),(13109,18021) +(1597,9383),(1594,9367) +(49539,35164),(49505,35065) +(39464,10295),(39409,10261) +(8921,37898),(8825,37803) +(31171,47076),(31093,47039) +(7178,41397),(7108,41304) +(16240,34832),(16162,34761) +(2829,20119),(2782,20091) +(45854,21265),(45810,21250) +(6382,12106),(6315,12030) +(22301,46291),(22291,46274) +(34142,14181),(34078,14158) +(11258,29748),(11198,29742) +(37450,6943),(37398,6882) +(41675,27207),(41643,27130) +(13578,49562),(13573,49479) +(37132,37397),(37081,37301) +(49404,37193),(49332,37170) +(33536,31809),(33444,31735) +(45990,42751),(45893,42708) +(38852,20510),(38802,20509) +(27453,15836),(27391,15802) +(9347,29004),(9284,28946) +(44871,27727),(44778,27668) +(14978,19646),(14970,19644) +(23243,47091),(23166,47080) +(45204,21431),(45167,21370) +(14082,22316),(14078,22235) +(42778,22694),(42744,22606) +(4834,25241),(4760,25196) +(20497,18110),(20494,18038) +(45738,35524),(45706,35496) +(21575,5151),(21493,5092) +(2194,10052),(2172,9960) +(47735,24472),(47682,24460) +(46740,35700),(46695,35609) +(24647,42807),(24568,42779) +(18000,30576),(17975,30506) +(48638,46630),(48544,46628) +(48508,33600),(48477,33578) +(38703,45408),(38670,45313) +(21712,15015),(21625,14956) +(5840,42007),(5768,41992) +(44011,11138),(43953,11117) +(3899,33262),(3897,33238) +(30142,23967),(30096,23927) +(36950,13226),(36908,13141) +(13130,26915),(13071,26873) +(38576,35408),(38539,35392) +(16776,46244),(16700,46176) +(38251,25969),(38168,25948) +(3512,32256),(3417,32242) +(31923,31225),(31832,31197) +(5144,4969),(5124,4937) +(34499,46164),(34430,46162) +(39432,31907),(39388,31828) +(17316,24606),(17221,24533) +(20751,49352),(20709,49323) +(41673,30418),(41623,30377) +(29026,24400),(28971,24345) +(21929,30617),(21894,30598) +(35539,12421),(35536,12355) +(24938,45583),(24870,45525) +(27442,33090),(27353,33064) +(23949,12046),(23949,12036) +(11399,377),(11360,294) +(47099,9989),(47023,9942) +(641,33118),(639,33084) +(13687,41308),(13682,41290) +(3682,17727),(3645,17660) +(13262,19396),(13185,19357) +(18791,389),(18774,366) +(12489,45384),(12403,45369) +(12065,6364),(12015,6325) +(32705,23886),(32619,23827) +(7004,37333),(6911,37240) +(28594,38078),(28530,38050) +(5805,21797),(5710,21701) +(41145,18905),(41058,18873) +(35599,10002),(35591,9956) +(5387,39087),(5326,38994) +(11703,14003),(11671,13912) +(4093,10472),(4091,10470) +(14110,49740),(14063,49695) +(4170,470),(4097,463) +(22219,17296),(22164,17221) +(2505,20879),(2446,20842) +(47235,24744),(47151,24667) +(30035,23234),(30013,23197) +(3489,11659),(3461,11607) +(38435,46322),(38429,46230) +(12315,32880),(12277,32854) +(33350,35297),(33317,35263) +(18845,37671),(18836,37589) +(24855,23554),(24783,23520) +(48251,44461),(48188,44408) +(17695,43353),(17605,43286) +(4964,21292),(4893,21270) +(33919,29907),(33852,29878) +(29139,40010),(29084,39957) +(41611,37750),(41572,37741) +(41773,34717),(41682,34700) +(8225,7424),(8221,7363) +(1785,28248),(1771,28219) +(21553,36307),(21505,36257) +(7552,18199),(7527,18119) +(14410,30977),(14349,30944) +(20940,49142),(20901,49069) +(36892,5522),(36810,5478) +(40192,20926),(40179,20926) +(44702,15182),(44641,15117) +(43431,4921),(43337,4827) +(41129,21654),(41084,21642) +(6205,42785),(6113,42722) +(23714,10224),(23666,10205) +(9318,35175),(9274,35139) +(40698,12676),(40618,12627) +(49954,1340),(49905,1294) +(32774,33062),(32763,33062) +(4336,22183),(4241,22157) +(10241,47657),(10151,47592) +(6746,16718),(6666,16634) +(26842,49694),(26839,49680) +(34870,47437),(34820,47347) +(26365,22266),(26326,22183) +(39859,932),(39829,840) +(33995,10888),(33902,10793) +(32972,22342),(32951,22340) +(19951,10161),(19932,10111) +(26779,45188),(26745,45151) +(11235,13593),(11184,13589) +(27334,20968),(27288,20953) +(9586,43102),(9488,43085) +(43935,49759),(43925,49680) +(10548,37032),(10474,36955) +(9326,14927),(9295,14848) +(41340,11312),(41311,11303) +(6500,44553),(6454,44515) +(8198,26841),(8104,26749) +(47761,34183),(47702,34140) +(43637,17912),(43577,17910) +(17623,11138),(17590,11122) +(48122,13132),(48077,13060) +(27911,39796),(27908,39777) +(1108,7918),(1080,7832) +(18776,24329),(18699,24326) +(1171,37901),(1075,37871) +(38437,33948),(38364,33907) +(1913,11593),(1817,11533) +(22684,266),(22656,181) +(13299,17075),(13241,17074) +(6924,30196),(6851,30113) +(4367,13150),(4298,13053) +(37381,6101),(37380,6046) +(10307,28383),(10270,28349) +(12283,8636),(12256,8610) +(20230,32775),(20144,32723) +(32942,12812),(32905,12714) +(46140,7138),(46140,7047) +(37235,29436),(37161,29425) +(42486,25454),(42478,25444) +(47860,46973),(47842,46961) +(41760,21026),(41662,20955) +(29663,20088),(29566,20026) +(19167,33241),(19101,33235) +(12306,37845),(12301,37803) +(11288,873),(11203,857) +(30309,5120),(30282,5060) +(46927,19737),(46856,19687) +(16664,20052),(16649,19989) +(7330,8675),(7296,8613) +(45067,45724),(44991,45631) +(45317,10862),(45218,10842) +(15012,47009),(14998,46956) +(47882,10146),(47813,10099) +(31571,46215),(31511,46148) +(32257,2619),(32187,2531) +(38924,41305),(38872,41285) +(49981,34876),(49898,34786) +(30501,35099),(30418,35011) +(45862,41438),(45854,41434) +(38448,31878),(38391,31822) +(8278,43463),(8274,43378) +(5883,30629),(5878,30564) +(49501,40346),(49447,40275) +(31651,43116),(31560,43106) +(44244,32940),(44244,32926) +(17941,18079),(17938,18035) +(9518,32524),(9470,32511) +(30707,43469),(30686,43457) +(3284,46542),(3187,46477) +(43423,29642),(43393,29602) +(19940,16825),(19877,16736) +(26194,47446),(26194,47407) +(30386,24675),(30333,24652) +(42707,44466),(42688,44456) +(43395,18525),(43320,18467) +(28346,32259),(28276,32196) +(45106,40786),(45026,40767) +(36734,20414),(36722,20363) +(37140,11569),(37099,11475) +(8967,6409),(8882,6341) +(31036,27923),(30993,27890) +(22442,47682),(22347,47663) +(32511,24029),(32482,23970) +(22593,34444),(22519,34399) +(41534,15495),(41518,15455) +(35862,19997),(35818,19928) +(31419,8323),(31404,8285) +(31036,19023),(30978,19000) +(46900,15192),(46891,15102) +(12774,9651),(12765,9604) +(49985,6436),(49927,6338) +(7184,47344),(7089,47285) +(12792,45021),(12740,45011) +(15019,27192),(14940,27096) +(35415,23106),(35381,23095) +(42129,14283),(42095,14245) +(29375,45807),(29347,45743) +(21763,24916),(21700,24889) +(47656,8794),(47579,8774) +(6139,49571),(6059,49472) +(44492,45607),(44483,45532) +(22699,4301),(22628,4240) +(27407,24241),(27335,24158) +(38424,34460),(38403,34458) +(46572,48456),(46554,48402) +(39676,29056),(39643,28981) +(4202,33076),(4107,33010) +(32499,10592),(32482,10575) +(22504,45417),(22459,45378) +(49619,40322),(49619,40268) +(14463,9305),(14426,9224) +(10070,20300),(10035,20211) +(35060,28561),(34965,28553) +(23970,47522),(23887,47428) +(46803,19155),(46790,19131) +(46151,49848),(46058,49830) +(45266,40766),(45209,40738) +(31041,32195),(31007,32110) +(41401,17245),(41334,17224) +(37445,654),(37435,602) +(45568,31904),(45508,31857) +(29326,7923),(29285,7896) +(27078,34643),(27027,34606) +(34492,43443),(34437,43345) +(34109,4307),(34083,4265) +(2755,45325),(2727,45312) +(12571,24218),(12536,24195) +(41224,2454),(41149,2445) +(711,34828),(655,34788) +(9104,18865),(9036,18850) +(3508,26816),(3456,26771) +(20159,16212),(20116,16160) +(36871,7425),(36777,7421) +(2751,45244),(2734,45222) +(35867,28071),(35769,28052) +(46878,35730),(46850,35725) +(20610,35086),(20513,35037) +(3903,32612),(3887,32517) +(9330,40226),(9289,40169) +(6338,28242),(6329,28184) +(35668,18344),(35606,18304) +(29892,48927),(29878,48879) +(26999,646),(26932,612) +(36377,38898),(36338,38847) +(40289,31459),(40236,31436) +(30377,1164),(30306,1069) +(7642,12183),(7590,12112) +(40325,1716),(40296,1662) +(36412,38787),(36318,38691) +(3967,33268),(3923,33261) +(33914,40774),(33873,40763) +(45978,41431),(45963,41332) +(39195,12546),(39120,12520) +(29962,30878),(29941,30846) +(9365,10732),(9310,10726) +(28801,23943),(28740,23885) +(28934,38858),(28928,38807) +(22126,45897),(22068,45803) +(2923,33832),(2918,33751) +(25116,2276),(25083,2272) +(31174,14546),(31144,14460) +(11728,9072),(11658,9004) +(19804,49195),(19730,49125) +(23090,28826),(23010,28787) +(33989,27553),(33947,27486) +(39702,47613),(39641,47553) +(31397,3607),(31304,3519) +(5835,9262),(5791,9226) +(40112,37022),(40038,36926) +(12346,29356),(12282,29344) +(28503,9623),(28469,9591) +(38449,43143),(38378,43066) +(36950,37311),(36905,37265) +(34824,5729),(34818,5706) +(9288,26969),(9225,26900) +(2535,42176),(2478,42159) +(29098,49051),(29085,49031) +(44759,33326),(44727,33230) +(42849,2970),(42821,2919) +(46014,27193),(45985,27151) +(14506,13713),(14417,13626) +(19342,44905),(19332,44895) +(38178,37003),(38147,36925) +(29179,27310),(29084,27288) +(42713,10158),(42671,10060) +(43336,38389),(43290,38326) +(41260,34410),(41245,34327) +(27907,2695),(27830,2596) +(16309,44972),(16222,44966) +(6230,22262),(6214,22249) +(9266,39458),(9175,39447) +(33120,33548),(33087,33538) +(43659,11416),(43599,11375) +(49707,39258),(49702,39159) +(23520,22140),(23486,22072) +(24736,46502),(24668,46412) +(7826,16851),(7730,16807) +(39114,6048),(39056,5965) +(11859,8753),(11764,8701) +(42254,48367),(42240,48328) +(26136,49185),(26056,49175) +(38395,11209),(38334,11137) +(33249,9425),(33209,9348) +(22131,38502),(22112,38460) +(5306,24344),(5267,24268) +(30292,1198),(30233,1149) +(9903,10896),(9850,10806) +(25568,22911),(25487,22868) +(22048,43391),(22043,43362) +(20852,25827),(20851,25766) +(35204,17119),(35114,17093) +(5575,43431),(5554,43410) +(17727,13623),(17678,13560) +(14721,29520),(14709,29461) +(40317,42220),(40267,42166) +(31435,31012),(31386,30931) +(40655,10103),(40645,10006) +(35783,17802),(35773,17763) +(34874,10210),(34856,10200) +(3694,14279),(3610,14239) +(27854,5493),(27799,5433) +(34913,7234),(34894,7220) +(15758,26445),(15738,26421) +(23710,7272),(23705,7270) +(33679,13468),(33628,13415) +(31271,40495),(31178,40461) +(759,187),(662,163) +(14419,40434),(14402,40381) +(45879,42933),(45814,42872) +(167,17214),(92,17184) +(9964,12210),(9958,12195) +(35834,46257),(35817,46211) +(26077,5629),(25978,5621) +(46177,44640),(46082,44544) +(44780,28753),(44707,28692) +(35491,24729),(35425,24690) +(33914,34190),(33914,34131) +(17709,33253),(17668,33227) +(45516,11888),(45423,11848) +(24497,24752),(24411,24710) +(30333,5952),(30331,5886) +(444,12587),(430,12497) +(7592,22353),(7541,22287) +(13387,37414),(13329,37318) +(21504,35227),(21449,35210) +(18533,12909),(18438,12848) +(41049,27148),(41048,27088) +(18205,12222),(18151,12140) +(18026,5164),(18026,5156) +(34104,29862),(34006,29815) +(18520,49686),(18454,49602) +(37000,41493),(36920,41424) +(43025,25711),(42986,25687) +(38620,47018),(38535,46934) +(24119,36813),(24023,36739) +(48887,26359),(48879,26302) +(47827,14625),(47810,14609) +(10792,30746),(10776,30716) +(30384,40672),(30318,40582) +(48417,22790),(48358,22746) +(14854,5819),(14785,5798) +(19142,44414),(19085,44406) +(31179,27081),(31145,27005) +(19692,8711),(19659,8642) +(39689,14082),(39603,14051) +(11181,39091),(11119,39002) +(46015,23374),(45936,23328) +(12517,49702),(12427,49690) +(21926,21137),(21841,21111) +(31956,12509),(31870,12494) +(5895,2030),(5851,2020) +(27094,5447),(27014,5377) +(35781,8717),(35780,8618) +(14012,12023),(13972,12015) +(1702,12442),(1696,12419) +(28549,5251),(28462,5248) +(26441,21007),(26360,20925) +(49820,7990),(49771,7967) +(26424,29698),(26339,29693) +(35146,6820),(35071,6817) +(15438,18788),(15435,18729) +(47115,5235),(47096,5143) +(33982,9002),(33915,8925) +(14206,37041),(14174,36955) +(24300,36616),(24232,36613) +(44658,1788),(44580,1769) +(31539,43550),(31463,43464) +(16722,9673),(16633,9652) +(44813,20573),(44733,20544) +(42114,32559),(42040,32552) +(41561,36244),(41477,36241) +(39589,33796),(39548,33716) +(20365,26770),(20329,26709) +(28511,208),(28479,114) +(10010,25524),(9930,25508) +(1549,45666),(1512,45621) +(16193,1927),(16166,1869) +(34486,11500),(34421,11401) +(14048,37944),(13994,37901) +(21692,9594),(21617,9496) +(2568,37899),(2557,37811) +(4360,24503),(4278,24443) +(50027,49230),(49951,49214) +(44849,14867),(44836,14813) +(16695,34896),(16683,34840) +(12600,35217),(12593,35129) +(23113,24009),(23030,23962) +(49907,30225),(49810,30158) +(18026,25208),(17970,25208) +(49711,39844),(49651,39790) +(5427,42682),(5357,42637) +(23901,14221),(23802,14184) +(15470,12185),(15376,12163) +(47302,34023),(47292,34001) +(24336,17418),(24315,17393) +(13948,17043),(13903,16970) +(8555,8986),(8530,8953) +(48830,6038),(48743,5986) +(48720,40687),(48623,40610) +(21161,30970),(21146,30896) +(9507,36316),(9411,36261) +(36643,18136),(36614,18106) +(1858,7457),(1851,7402) +(24452,44306),(24372,44252) +(3292,807),(3205,806) +(6845,30694),(6792,30627) +(21333,25786),(21237,25751) +(23008,22574),(22999,22511) +(8790,8893),(8772,8806) +(43333,47968),(43264,47900) +(5377,24103),(5302,24076) +(18410,23993),(18329,23907) +(24752,19126),(24713,19069) +(49772,11378),(49696,11293) +(3468,12920),(3396,12873) +(1746,40342),(1736,40333) +(49187,29737),(49139,29681) +(27657,44952),(27581,44917) +(35407,30177),(35345,30151) +(4071,40568),(4058,40544) +(25998,30513),(25965,30452) +(8195,45403),(8097,45310) +(8276,41689),(8183,41670) +(48435,28550),(48355,28455) +(8139,25449),(8136,25380) +(20302,25574),(20297,25531) +(22055,46659),(22034,46567) +(3531,49962),(3463,49934) +(46828,46938),(46739,46902) +(42294,786),(42212,739) +(8779,3292),(8761,3275) +(48146,46170),(48082,46151) +(21571,10000),(21531,9919) +(35526,26029),(35450,25945) +(38893,22225),(38865,22197) +(22189,37520),(22132,37497) +(810,43261),(751,43198) +(10352,39144),(10290,39093) +(8740,35435),(8720,35432) +(31657,13551),(31583,13484) +(39803,4019),(39755,4014) +(46353,7853),(46312,7824) +(30078,48975),(30021,48970) +(2847,32036),(2819,31966) +(25250,10147),(25165,10140) +(15643,38953),(15585,38947) +(40792,29798),(40731,29731) +(43249,26858),(43215,26835) +(47229,2199),(47201,2134) +(10052,23601),(9958,23570) +(38981,21615),(38892,21604) +(3651,45004),(3570,44917) +(21503,8261),(21409,8166) +(13518,34201),(13465,34105) +(13899,25117),(13836,25114) +(18327,17403),(18301,17349) +(19503,13648),(19483,13607) +(3554,19487),(3529,19466) +(41102,43355),(41070,43314) +(4663,45858),(4583,45765) +(3971,3023),(3931,2975) +(37124,7061),(37080,6993) +(48530,47172),(48459,47160) +(14575,29843),(14509,29750) +(43443,23124),(43357,23038) +(8864,48290),(8857,48263) +(41597,39852),(41577,39791) +(35610,33392),(35556,33353) +(36415,17906),(36328,17846) +(24919,43933),(24839,43883) +(7457,14056),(7395,14051) +(43851,4090),(43801,4080) +(43567,18468),(43471,18388) +(16711,6084),(16652,6055) +(45888,45934),(45846,45880) +(45630,9313),(45585,9248) +(27119,25969),(27094,25884) +(36155,11420),(36120,11405) +(41880,47111),(41808,47049) +(17554,20379),(17482,20374) +(38848,5936),(38763,5869) +(28324,31019),(28276,30944) +(43257,17152),(43176,17091) +(42717,24613),(42691,24527) +(16786,41486),(16763,41403) +(19259,28780),(19160,28711) +(25843,28265),(25760,28171) +(48645,34816),(48546,34755) +(7004,49289),(6976,49236) +(30261,21833),(30181,21776) +(5290,46672),(5219,46661) +(21237,31901),(21188,31849) +(23340,38537),(23253,38472) +(17269,3682),(17183,3586) +(48200,15377),(48110,15369) +(16546,22195),(16477,22142) +(21436,8460),(21378,8449) +(46598,17235),(46577,17138) +(30212,36184),(30152,36092) +(18037,155),(17941,109) +(4945,29201),(4933,29184) +(32835,18782),(32770,18750) +(34160,33104),(34120,33007) +(5151,26989),(5149,26909) +(1801,15549),(1710,15461) +(48988,34819),(48951,34764) +(20904,32547),(20856,32497) +(32654,35183),(32606,35144) +(14336,11763),(14328,11712) +(30546,23808),(30463,23773) +(6813,21006),(6781,20924) +(14199,22030),(14185,21934) +(3783,14709),(3747,14658) +(49428,47052),(49422,46973) +(29551,27682),(29470,27654) +(29170,37260),(29151,37181) +(48924,24689),(48894,24680) +(48497,34052),(48453,33966) +(21263,8203),(21242,8176) +(46537,3797),(46462,3735) +(18406,14579),(18393,14563) +(11583,16529),(11536,16471) +(10564,46257),(10478,46228) +(49769,34513),(49761,34458) +(9202,6482),(9138,6391) +(40387,37411),(40357,37360) +(11966,11802),(11888,11751) +(15551,47438),(15486,47406) +(12017,43288),(11969,43230) +(9717,22574),(9701,22495) +(35083,49443),(35075,49355) +(33857,9320),(33813,9269) +(32106,10581),(32012,10560) +(14345,12485),(14273,12424) +(24187,46416),(24175,46402) +(43854,42159),(43808,42129) +(35399,40707),(35359,40646) +(29585,25576),(29493,25556) +(24919,7829),(24911,7753) +(17049,48390),(17022,48304) +(25224,35012),(25217,34922) +(47397,20853),(47346,20779) +(17221,16558),(17181,16516) +(8669,16491),(8645,16486) +(23502,44241),(23484,44164) +(36169,37046),(36072,37010) +(44775,32394),(44763,32357) +(30685,36871),(30662,36792) +(21783,47642),(21714,47630) +(34847,27467),(34761,27372) +(43925,49912),(43888,49878) +(16455,27861),(16364,27813) +(38406,18310),(38329,18309) +(5408,9461),(5319,9426) +(41856,36900),(41784,36854) +(23723,4460),(23646,4448) +(18454,40138),(18430,40046) +(17505,36822),(17418,36763) +(36686,33534),(36641,33476) +(11347,9454),(11289,9436) +(27816,34752),(27745,34736) +(44213,8559),(44162,8461) +(45359,26789),(45315,26776) +(31249,19475),(31224,19421) +(25917,44239),(25819,44149) +(47313,40691),(47264,40685) +(40577,33848),(40513,33794) +(9606,45253),(9582,45174) +(30005,24521),(29910,24496) +(49332,35375),(49309,35299) +(12164,33871),(12075,33820) +(19598,43327),(19593,43314) +(3818,28584),(3815,28504) +(35579,8611),(35541,8604) +(8811,20986),(8750,20954) +(16139,44777),(16128,44686) +(35550,41501),(35534,41458) +(43180,11927),(43109,11891) +(45798,8465),(45711,8460) +(18196,6886),(18126,6845) +(1774,32167),(1701,32073) +(7030,40790),(7029,40711) +(11676,23009),(11665,22915) +(33990,22561),(33953,22474) +(30366,9447),(30284,9353) +(37626,32913),(37596,32853) +(7730,42561),(7665,42470) +(49347,8403),(49315,8387) +(6874,3499),(6812,3458) +(44189,16999),(44169,16964) +(6312,30167),(6231,30083) +(18932,6611),(18909,6518) +(32262,13076),(32223,13057) +(45989,249),(45910,222) +(42710,855),(42692,796) +(25562,9849),(25535,9802) +(13348,46719),(13260,46689) +(30022,42196),(30005,42160) +(22263,45954),(22243,45950) +(18918,18890),(18820,18795) +(31918,12003),(31852,11989) +(12252,39453),(12211,39398) +(40208,9789),(40194,9759) +(35943,21767),(35914,21693) +(18439,10706),(18383,10618) +(2803,18999),(2778,18925) +(14953,27444),(14875,27397) +(12587,22025),(12545,21928) +(33930,21090),(33918,21009) +(10444,2606),(10407,2553) +(28700,29782),(28665,29703) +(1402,13497),(1397,13465) +(24155,3075),(24083,3062) +(38378,1864),(38339,1849) +(29261,49910),(29247,49818) +(38139,37073),(38098,37057) +(24468,41130),(24418,41053) +(9989,1015),(9959,939) +(47001,33561),(46994,33518) +(47058,16030),(46983,16012) +(35509,1814),(35426,1748) +(3630,48019),(3597,47923) +(47781,12986),(47741,12947) +(16364,9908),(16356,9882) +(17290,41508),(17287,41410) +(42423,26477),(42349,26434) +(10039,920),(9952,833) +(16851,21338),(16846,21314) +(23104,7700),(23062,7688) +(5619,2079),(5611,2075) +(31471,49632),(31375,49549) +(25793,12526),(25783,12456) +(3935,29528),(3866,29513) +(5957,1646),(5947,1595) +(2467,22376),(2429,22349) +(43715,32673),(43664,32595) +(6726,13093),(6636,12994) +(31477,18347),(31421,18299) +(34232,36635),(34200,36552) +(49061,14516),(49008,14442) +(43996,6129),(43955,6074) +(7728,33802),(7670,33703) +(6131,36766),(6053,36749) +(35791,16361),(35696,16329) +(45759,8935),(45675,8886) +(43634,2029),(43537,1940) +(4916,32233),(4844,32181) +(46701,23508),(46623,23477) +(29590,4893),(29552,4871) +(38647,4423),(38574,4396) +(7593,25845),(7497,25751) +(8510,43552),(8432,43492) +(18791,39181),(18730,39162) +(7462,2956),(7454,2858) +(1394,26795),(1392,26780) +(16707,21993),(16609,21932) +(26838,10866),(26803,10836) +(31642,29842),(31585,29760) +(21891,3502),(21863,3406) +(13258,587),(13250,507) +(6072,47397),(6021,47369) +(16605,49730),(16579,49659) +(42830,40981),(42791,40981) +(12975,3706),(12913,3637) +(30925,21660),(30826,21649) +(1455,14229),(1410,14156) +(17583,16486),(17562,16474) +(33377,3387),(33333,3381) +(784,6177),(750,6095) +(22111,44110),(22106,44013) +(1444,403),(1346,344) +(4010,46220),(3982,46212) +(17932,8150),(17861,8127) +(38685,31466),(38636,31416) +(14257,11549),(14242,11522) +(14990,15217),(14904,15211) +(21395,21533),(21307,21520) +(31948,33725),(31885,33694) +(433,49033),(390,48961) +(45205,609),(45173,523) +(25065,35494),(25003,35455) +(33265,6677),(33224,6611) +(18179,22345),(18133,22256) +(3916,13759),(3820,13732) +(1696,13478),(1604,13436) +(47203,25980),(47130,25907) +(24913,13361),(24868,13268) +(13824,40177),(13792,40130) +(25671,13555),(25585,13494) +(20133,37769),(20105,37679) +(26368,16734),(26288,16726) +(30545,35438),(30458,35376) +(48816,22926),(48812,22831) +(48807,31389),(48739,31330) +(11003,10859),(10950,10765) +(17288,8570),(17247,8485) +(38377,31415),(38331,31379) +(19085,23425),(19059,23326) +(40059,17068),(40052,17006) +(18811,13493),(18734,13394) +(36319,17197),(36225,17181) +(14939,38780),(14863,38714) +(49539,17656),(49479,17629) +(42530,45951),(42466,45854) +(27318,26654),(27233,26610) +(49980,35004),(49937,34963) +(18326,32558),(18322,32502) +(45951,28555),(45896,28481) +(12104,33531),(12014,33501) +(22311,41113),(22215,41066) +(25073,18721),(25047,18656) +(14524,13486),(14510,13390) +(40040,36688),(40000,36599) +(21594,11473),(21563,11436) +(44031,22274),(43938,22187) +(729,30683),(668,30601) +(14114,20873),(14102,20803) +(28239,41377),(28222,41308) +(26404,11922),(26317,11843) +(41660,34586),(41585,34501) +(21128,2384),(21101,2368) +(30209,16952),(30156,16858) +(39078,24963),(39045,24898) +(5598,1348),(5499,1294) +(38474,7436),(38450,7364) +(15117,45734),(15024,45693) +(23909,39853),(23888,39780) +(24292,30183),(24282,30148) +(48871,17661),(48868,17637) +(918,18752),(847,18708) +(43615,16162),(43606,16104) +(33763,47410),(33751,47409) +(4798,6485),(4773,6388) +(18524,41539),(18433,41518) +(47745,42449),(47651,42364) +(38936,21237),(38864,21204) +(5251,3516),(5194,3475) +(22269,36269),(22183,36228) +(18736,40983),(18685,40947) +(38393,15444),(38356,15363) +(38134,29898),(38103,29862) +(37789,39557),(37732,39474) +(31906,23005),(31838,23003) +(10647,40094),(10560,40040) +(9914,41547),(9867,41545) +(44221,443),(44125,433) +(41479,10936),(41381,10847) +(42586,6301),(42563,6235) +(2504,17588),(2449,17554) +(7045,18782),(7028,18764) +(41840,32018),(41768,31938) +(38416,17158),(38330,17060) +(8605,39015),(8605,38933) +(5764,43548),(5719,43496) +(20789,29902),(20696,29843) +(36104,47896),(36079,47816) +(31736,13834),(31722,13832) +(32617,19701),(32597,19684) +(1671,18997),(1622,18945) +(36007,26545),(36005,26535) +(31864,17494),(31820,17455) +(27346,28388),(27303,28289) +(8191,9653),(8133,9589) +(7501,21616),(7405,21536) +(35450,9580),(35368,9563) +(29281,37276),(29247,37255) +(6225,17192),(6200,17135) +(43689,8119),(43670,8028) +(41917,49601),(41835,49563) +(44295,13116),(44205,13078) +(22721,44772),(22667,44748) +(32640,11107),(32636,11050) +(20639,28851),(20613,28839) +(32479,10159),(32446,10061) +(27251,16978),(27196,16959) +(41401,33148),(41339,33074) +(49001,8538),(48989,8444) +(37958,35843),(37874,35802) +(46969,41229),(46903,41138) +(18541,8876),(18541,8870) +(4080,31634),(4061,31627) +(8097,35240),(8040,35152) +(18470,21414),(18463,21412) +(20914,17897),(20838,17869) +(42688,11681),(42666,11641) +(47525,25005),(47443,24907) +(32439,14438),(32397,14400) +(39667,19626),(39622,19542) +(1212,44525),(1169,44516) +(29766,4433),(29668,4401) +(25847,49657),(25813,49605) +(33859,17356),(33827,17263) +(28989,45953),(28904,45854) +(37211,30830),(37113,30819) +(45220,26382),(45219,26340) +(12312,43250),(12234,43246) +(37775,41504),(37762,41421) +(45889,33499),(45822,33411) +(49461,22601),(49369,22553) +(39857,33844),(39816,33824) +(46102,15822),(46030,15778) +(46605,31239),(46598,31170) +(23925,5856),(23862,5808) +(15459,4262),(15407,4241) +(12019,4907),(12015,4818) +(38258,17973),(38229,17923) +(40575,29566),(40477,29521) +(29715,45919),(29697,45891) +(11694,9510),(11670,9490) +(7053,44257),(7012,44231) +(16465,8603),(16391,8505) +(29170,15592),(29098,15527) +(20400,37354),(20345,37328) +(5281,10265),(5252,10184) +(6084,48782),(6058,48727) +(11006,6889),(10971,6796) +(16299,19461),(16286,19411) +(13718,29192),(13642,29106) +(3999,2965),(3963,2903) +(18509,12235),(18430,12208) +(49542,38575),(49537,38534) +(15093,41715),(15071,41634) +(6802,8385),(6714,8300) +(15127,17507),(15097,17424) +(36921,3025),(36835,2995) +(32117,24327),(32101,24262) +(27244,24151),(27165,24104) +(36339,42360),(36313,42358) +(47288,46252),(47245,46184) +(37867,6649),(37818,6565) +(14886,22103),(14865,22089) +(39611,17952),(39513,17951) +(37329,31436),(37298,31436) +(5715,39115),(5698,39099) +(13266,7364),(13203,7296) +(16076,10945),(16006,10942) +(7197,41509),(7126,41413) +(14411,40868),(14330,40772) +(12872,33481),(12862,33454) +(17786,19616),(17758,19560) +(1052,37358),(996,37311) +(42825,12643),(42762,12625) +(20007,49858),(19921,49778) +(27155,6355),(27072,6257) +(14117,40208),(14022,40155) +(47280,34069),(47279,34028) +(17551,15803),(17482,15763) +(1725,6673),(1676,6649) +(43984,31128),(43961,31105) +(43772,47042),(43731,47038) +(46901,47317),(46817,47228) +(19877,14179),(19837,14168) +(20691,19989),(20675,19935) +(4011,18914),(3963,18817) +(1023,23378),(933,23317) +(30051,46118),(29966,46039) +(43499,46488),(43496,46409) +(43531,2412),(43447,2396) +(16034,32285),(15976,32220) +(12817,21365),(12740,21298) +(7607,47293),(7585,47293) +(32512,12218),(32463,12170) +(1848,21496),(1839,21439) +(17567,23073),(17478,23046) +(35813,31847),(35807,31792) +(563,30859),(540,30842) +(13145,15488),(13063,15433) +(36754,37479),(36731,37411) +(1125,26069),(1057,25997) +(4539,20676),(4519,20618) +(8476,34721),(8409,34681) +(7794,25691),(7727,25656) +(23842,514),(23800,473) +(47678,41396),(47668,41365) +(6837,25974),(6799,25892) +(13355,11174),(13304,11161) +(37243,25548),(37158,25471) +(12528,30208),(12441,30205) +(14929,1672),(14886,1607) +(27263,49026),(27263,49010) +(15892,21645),(15835,21642) +(29446,48978),(29360,48967) +(41304,9892),(41211,9825) +(37418,49393),(37338,49296) +(41146,32178),(41120,32165) +(28738,13326),(28722,13266) +(14899,36595),(14873,36559) +(1973,31435),(1921,31426) +(19485,17742),(19421,17661) +(33072,20995),(32980,20903) +(47091,30055),(47080,30037) +(45753,12998),(45686,12992) +(11528,7826),(11509,7794) +(21104,13921),(21060,13836) +(16768,15491),(16747,15470) +(13279,20396),(13249,20326) +(4342,49518),(4339,49446) +(20413,15476),(20349,15447) +(45532,5649),(45484,5627) +(18647,27196),(18619,27115) +(1326,17473),(1261,17400) +(47646,19644),(47588,19609) +(35088,1813),(35080,1732) +(38461,34839),(38410,34838) +(34358,11540),(34285,11506) +(26969,7078),(26953,6989) +(12629,40352),(12617,40264) +(33800,7037),(33731,6992) +(24462,13518),(24392,13486) +(33164,47357),(33096,47329) +(15422,18451),(15413,18376) +(19643,12916),(19567,12912) +(40860,42125),(40770,42050) +(49103,29614),(49039,29606) +(36319,35582),(36222,35528) +(8924,36083),(8873,36018) +(49603,44022),(49505,44021) +(7783,40633),(7702,40618) +(25388,49107),(25346,49042) +(28375,38947),(28306,38919) +(47324,22672),(47321,22660) +(2287,8808),(2266,8719) +(44343,16339),(44248,16318) +(2374,28839),(2336,28798) +(22913,40710),(22819,40688) +(47747,684),(47658,627) +(16043,46011),(16021,45984) +(34958,32168),(34903,32092) +(4840,49328),(4752,49258) +(24341,2087),(24330,2009) +(18378,19374),(18327,19358) +(48165,7217),(48156,7141) +(14232,6044),(14182,6004) +(23080,4196),(22983,4191) +(259,1850),(175,1820) +(270,29508),(264,29440) +(45088,11375),(45050,11295) +(29666,39386),(29656,39302) +(8712,8782),(8660,8713) +(15900,6650),(15855,6561) +(28946,28348),(28917,28347) +(32544,25845),(32538,25779) +(44047,6957),(43951,6942) +(36465,588),(36382,503) +(28167,26679),(28150,26673) +(16065,4268),(15975,4180) +(12950,23494),(12893,23494) +(30145,24679),(30056,24654) +(3027,16162),(3001,16071) +(8259,34537),(8202,34484) +(41447,1515),(41427,1454) +(18407,28362),(18309,28303) +(21393,41872),(21328,41816) +(46040,26497),(45996,26408) +(49944,25163),(49902,25153) +(16195,11843),(16159,11831) +(44257,15270),(44254,15214) +(49760,4791),(49699,4713) +(22558,33709),(22519,33681) +(28375,10003),(28336,9938) +(18179,24310),(18106,24256) +(707,30688),(664,30669) +(5851,26118),(5822,26037) +(4266,1292),(4221,1217) +(16516,11331),(16432,11248) +(32374,38277),(32313,38245) +(21939,8015),(21927,7952) +(34322,32051),(34242,32003) +(6262,35977),(6260,35953) +(16717,38594),(16622,38498) +(14564,3433),(14535,3425) +(21078,1000),(20994,974) +(28584,956),(28575,868) +(5538,9962),(5465,9870) +(34183,44102),(34175,44085) +(42507,10289),(42441,10288) +(12671,19936),(12594,19920) +(24835,12179),(24770,12173) +(15664,11538),(15598,11494) +(28892,24446),(28821,24350) +(41654,26720),(41570,26632) +(36583,387),(36503,357) +(10842,34824),(10795,34788) +(11518,42588),(11429,42565) +(12577,40322),(12486,40266) +(2453,4045),(2439,3956) +(31837,33705),(31803,33681) +(24403,27711),(24383,27705) +(4431,2748),(4337,2656) +(3036,2887),(3014,2826) +(37664,16118),(37615,16022) +(8606,18063),(8587,18038) +(24738,25458),(24656,25362) +(45756,34022),(45671,33948) +(34079,15236),(33981,15171) +(9251,22488),(9228,22470) +(25136,2809),(25126,2717) +(5548,47695),(5543,47685) +(13765,40800),(13707,40754) +(25216,30678),(25144,30677) +(22441,17169),(22392,17106) +(1091,4770),(1054,4734) +(36311,50073),(36258,49987) +(22461,33163),(22457,33128) +(35873,28907),(35845,28867) +(42907,15848),(42904,15785) +(6549,24897),(6540,24861) +(21928,37764),(21891,37681) +(21237,41132),(21139,41086) +(12207,24266),(12173,24235) +(40643,49770),(40574,49687) +(32833,35686),(32815,35674) +(14545,18143),(14541,18098) +(33892,42783),(33884,42707) +(33933,8381),(33921,8369) +(12450,19044),(12403,19002) +(10176,45158),(10088,45145) +(35828,12080),(35732,12022) +(28102,13694),(28061,13666) +(49432,31744),(49340,31711) +(16192,37743),(16162,37697) +(46830,867),(46756,790) +(9200,28048),(9159,27986) +(13397,19369),(13340,19288) +(30879,43562),(30785,43545) +(21995,48224),(21920,48143) +(11871,47569),(11809,47568) +(29366,22196),(29280,22154) +(26243,28176),(26203,28116) +(28995,35031),(28906,35014) +(29384,39276),(29352,39183) +(8497,13798),(8471,13789) +(7412,27226),(7334,27220) +(25403,47678),(25363,47654) +(11599,5556),(11574,5502) +(44056,5123),(44008,5111) +(49603,30877),(49579,30840) +(32261,45876),(32206,45865) +(35104,41659),(35048,41587) +(5457,35844),(5376,35782) +(29423,3977),(29354,3959) +(18059,3001),(17965,2961) +(8509,5691),(8463,5620) +(27118,5762),(27083,5747) +(2991,48605),(2939,48559) +(44482,3484),(44425,3459) +(45143,16439),(45046,16365) +(2236,37531),(2147,37530) +(41561,3217),(41490,3210) +(6270,27200),(6171,27166) +(49195,24871),(49138,24798) +(46985,38881),(46897,38845) +(37486,23522),(37404,23441) +(26907,14490),(26900,14391) +(30829,16111),(30756,16056) +(3644,17291),(3587,17262) +(20508,49775),(20472,49680) +(43279,8972),(43198,8936) +(33744,7470),(33734,7439) +(46303,20538),(46284,20498) +(10365,48246),(10291,48154) +(12636,24987),(12545,24933) +(40998,46992),(40989,46916) +(30536,6073),(30531,6018) +(22102,9643),(22051,9594) +(18616,34348),(18530,34332) +(8222,8907),(8123,8848) +(45698,28860),(45698,28770) +(26958,1748),(26924,1726) +(26735,35073),(26659,35025) +(48370,40813),(48293,40737) +(13140,993),(13108,934) +(10588,22893),(10528,22883) +(23645,40789),(23567,40698) +(49548,12374),(49546,12329) +(41135,39626),(41100,39602) +(41374,10856),(41328,10769) +(12234,5765),(12146,5674) +(12832,46941),(12764,46917) +(47886,34532),(47851,34500) +(23777,10549),(23735,10495) +(1291,16913),(1194,16873) +(29239,30554),(29202,30500) +(36485,30007),(36454,29924) +(7067,11320),(7045,11229) +(16939,30482),(16904,30462) +(27423,34386),(27379,34303) +(35170,32021),(35155,31979) +(42570,36477),(42474,36457) +(19695,679),(19682,594) +(47537,39450),(47446,39450) +(19410,22942),(19375,22922) +(34216,40166),(34152,40158) +(37000,24351),(36972,24299) +(24989,1681),(24954,1672) +(54,38679),(3,38602) +(41461,40693),(41411,40599) +(7576,46054),(7545,45963) +(35505,28262),(35413,28222) +(1158,16976),(1145,16927) +(23494,42291),(23437,42229) +(32894,32519),(32880,32485) +(604,13413),(509,13401) +(18396,19712),(18355,19646) +(26657,28234),(26597,28191) +(24240,47211),(24154,47191) +(41778,10741),(41766,10730) +(44022,43776),(44010,43677) +(35967,30055),(35906,29969) +(28878,18042),(28806,18027) +(31507,27302),(31428,27267) +(13267,21935),(13265,21872) +(122,46832),(64,46762) +(10348,45916),(10306,45844) +(22962,12644),(22927,12607) +(6320,22290),(6284,22247) +(2297,11372),(2216,11298) +(29366,36660),(29325,36654) +(13962,39307),(13921,39220) +(11094,19151),(11092,19143) +(32289,23776),(32258,23760) +(36044,17356),(35956,17273) +(46304,38692),(46232,38675) +(10934,42999),(10922,42909) +(4271,21177),(4207,21093) +(7837,19926),(7747,19905) +(25537,36605),(25477,36584) +(22161,14999),(22079,14962) +(5127,31243),(5074,31213) +(14904,40664),(14838,40593) +(29308,8480),(29268,8438) +(17731,7410),(17699,7352) +(44840,29293),(44797,29248) +(15523,31519),(15505,31485) +(34429,38479),(34421,38478) +(3530,23456),(3440,23390) +(4699,6889),(4603,6796) +(47405,48524),(47389,48514) +(23357,43160),(23305,43156) +(16923,1995),(16860,1937) +(47592,33853),(47537,33758) +(31624,37490),(31595,37473) +(42321,13380),(42303,13337) +(3088,16094),(3079,16060) +(22884,2955),(22856,2857) +(17784,23073),(17724,23044) +(32638,45577),(32553,45512) +(13876,44091),(13801,44000) +(27844,24384),(27758,24330) +(28178,10225),(28155,10167) +(39910,14277),(39857,14241) +(30372,19524),(30301,19514) +(38732,43151),(38724,43151) +(32628,2068),(32547,2068) +(13950,28652),(13932,28566) +(38996,41070),(38919,40993) +(31759,45246),(31676,45215) +(5424,34145),(5382,34106) +(14727,45600),(14699,45547) +(31429,21537),(31414,21499) +(14740,3420),(14650,3323) +(21793,39498),(21743,39471) +(18102,25924),(18037,25868) +(33299,683),(33213,594) +(45882,48765),(45809,48721) +(49215,4098),(49180,4067) +(49698,33743),(49614,33663) +(21532,5215),(21514,5151) +(24840,26877),(24826,26808) +(32680,28433),(32631,28364) +(20661,27511),(20584,27414) +(28048,30385),(28009,30315) +(45403,42533),(45389,42464) +(46531,36947),(46531,36850) +(36943,32817),(36865,32737) +(37984,43763),(37888,43748) +(20593,10650),(20557,10610) +(5387,40595),(5326,40585) +(34412,10600),(34352,10539) +(7237,47546),(7206,47451) +(39931,26644),(39915,26598) +(29843,4734),(29800,4669) +(37503,8867),(37406,8821) +(2583,2373),(2570,2294) +(29275,46433),(29256,46350) +(3332,45620),(3287,45581) +(22472,39287),(22472,39257) +(36786,18907),(36708,18884) +(45503,28576),(45482,28494) +(33262,28386),(33163,28365) +(3606,49757),(3538,49697) +(2082,49380),(1991,49281) +(12065,3734),(11983,3663) +(15606,9048),(15596,9028) +(14687,19309),(14637,19263) +(4568,15461),(4499,15428) +(43938,7429),(43923,7391) +(2168,50012),(2108,49914) +(16022,8934),(15963,8928) +(24567,39147),(24561,39102) +(42781,14149),(42765,14088) +(39501,21084),(39468,21078) +(6697,29628),(6693,29584) +(11441,16164),(11364,16125) +(39946,1920),(39868,1844) +(18138,45512),(18111,45438) +(20799,41217),(20718,41138) +(30264,16697),(30240,16639) +(30746,50040),(30727,49992) +(37429,43273),(37423,43205) +(22854,28863),(22789,28810) +(11380,48298),(11287,48242) +(16471,37273),(16439,37223) +(32737,39842),(32661,39811) +(30959,3447),(30949,3357) +(36396,13263),(36348,13187) +(29607,14625),(29531,14619) +(7851,43399),(7824,43334) +(38515,14575),(38496,14492) +(29125,3289),(29086,3264) +(6866,10476),(6839,10424) +(318,31489),(235,31404) +(1140,7007),(1113,6945) +(36574,9291),(36484,9275) +(40320,40937),(40246,40866) +(588,25849),(552,25801) +(6728,42539),(6645,42507) +(12180,6185),(12123,6123) +(32913,44123),(32899,44037) +(25464,16803),(25441,16749) +(23711,5829),(23695,5750) +(31424,34930),(31377,34906) +(42171,8298),(42124,8222) +(451,31104),(375,31083) +(39996,3278),(39943,3260) +(25816,40396),(25735,40362) +(34471,28587),(34399,28547) +(45344,21540),(45297,21496) +(27269,16787),(27246,16763) +(18070,4469),(18022,4423) +(12668,16367),(12645,16295) +(13823,17276),(13730,17251) +(20555,45544),(20511,45498) +(35893,42189),(35861,42177) +(37081,45730),(37076,45705) +(17270,15651),(17201,15552) +(48690,46034),(48667,45945) +(456,16088),(368,16023) +(48707,12416),(48670,12363) +(29692,11509),(29614,11483) +(7005,3668),(6981,3574) +(12162,389),(12103,309) +(12371,24983),(12366,24964) +(6886,48414),(6868,48327) +(10653,26234),(10624,26142) +(8526,48205),(8517,48117) +(10521,31892),(10480,31798) +(43353,1086),(43281,1071) +(21007,35650),(20998,35649) +(2343,4396),(2310,4320) +(29379,12895),(29284,12891) +(27662,17407),(27570,17313) +(9845,29346),(9807,29321) +(43855,38669),(43790,38599) +(20461,44189),(20397,44158) +(11627,17368),(11581,17289) +(2971,38855),(2938,38807) +(43204,47082),(43128,47018) +(9930,46902),(9909,46871) +(30561,48461),(30536,48365) +(44059,7591),(44038,7563) +(46260,16898),(46162,16886) +(27491,2891),(27396,2814) +(36512,26034),(36455,25941) +(31193,20022),(31100,19942) +(17057,13643),(16960,13621) +(26897,3399),(26844,3318) +(1760,5504),(1683,5431) +(29347,5511),(29346,5450) +(38761,42083),(38688,41999) +(11226,4089),(11165,4068) +(46427,42983),(46361,42970) +(12958,30737),(12912,30712) +(44432,46521),(44333,46443) +(16124,2948),(16113,2852) +(24704,25422),(24635,25340) +(30833,46152),(30790,46122) +(4487,37006),(4473,36968) +(41047,23376),(41036,23327) +(16312,49392),(16298,49330) +(30081,14687),(30042,14660) +(11160,13954),(11103,13938) +(33207,23246),(33143,23168) +(14872,7635),(14860,7585) +(20139,23987),(20059,23955) +(10946,49757),(10923,49746) +(39438,36158),(39426,36134) +(35502,2385),(35464,2327) +(17073,42173),(16987,42130) +(6079,17258),(6068,17195) +(40458,15752),(40364,15728) +(23340,7879),(23313,7806) +(31819,15096),(31762,15059) +(31159,40864),(31158,40780) +(26975,32144),(26915,32113) +(34530,10378),(34440,10298) +(18855,49577),(18780,49528) +(16787,16625),(16723,16586) +(32330,26538),(32314,26458) +(34270,28674),(34265,28595) +(10022,16026),(10006,15962) +(23143,1479),(23095,1469) +(33676,4483),(33583,4408) +(31066,22074),(31059,22035) +(21603,47121),(21563,47082) +(30051,4244),(30021,4157) +(30634,39478),(30615,39446) +(34404,48724),(34393,48724) +(31103,21414),(31039,21380) +(22945,47397),(22849,47313) +(18133,32025),(18073,31941) +(4053,25759),(3977,25667) +(39185,39091),(39102,39068) +(43287,7407),(43225,7314) +(13137,31188),(13112,31182) +(46264,1438),(46258,1389) +(22804,43892),(22769,43822) +(7542,1044),(7487,983) +(33022,8321),(32925,8267) +(384,39161),(286,39073) +(28205,24401),(28142,24382) +(31708,39086),(31696,39026) +(36626,15708),(36560,15690) +(17099,16924),(17079,16924) +(10817,6989),(10747,6955) +(24338,19293),(24291,19277) +(27566,17576),(27544,17545) +(23041,38384),(22970,38320) +(12786,8485),(12702,8435) +(13876,49473),(13813,49448) +(31585,46998),(31490,46929) +(30227,8768),(30206,8715) +(32062,39306),(32023,39292) +(25003,35753),(24921,35687) +(3281,6758),(3232,6704) +(11395,30299),(11376,30220) +(5088,15275),(5007,15203) +(31100,39538),(31003,39444) +(2741,17877),(2726,17793) +(42897,48620),(42860,48537) +(4230,15778),(4181,15776) +(17835,27530),(17815,27431) +(34189,10933),(34135,10921) +(7537,39974),(7494,39973) +(21554,3507),(21528,3476) +(9350,32326),(9273,32275) +(16455,8874),(16420,8793) +(7346,34235),(7330,34224) +(16417,48134),(16352,48066) +(41916,4971),(41849,4886) +(15856,1522),(15807,1521) +(41549,40218),(41494,40144) +(9978,16226),(9972,16181) +(14856,13312),(14808,13283) +(38490,41641),(38428,41583) +(25828,7438),(25807,7378) +(21876,30633),(21796,30587) +(1908,14279),(1825,14247) +(32207,10251),(32121,10184) +(370,9493),(328,9441) +(42072,17634),(41974,17600) +(47298,9910),(47235,9846) +(17856,11266),(17782,11225) +(35009,21400),(34956,21396) +(18337,11145),(18335,11133) +(25425,9139),(25381,9085) +(35642,27783),(35621,27782) +(3629,33164),(3575,33163) +(17151,41255),(17115,41204) +(17417,5835),(17402,5751) +(33407,14226),(33329,14141) +(1930,29955),(1889,29931) +(41101,10942),(41065,10844) +(36333,27288),(36281,27233) +(21423,36868),(21367,36825) +(36385,19566),(36341,19510) +(27073,38301),(27066,38232) +(43989,34187),(43984,34174) +(48366,7488),(48316,7483) +(37497,36075),(37415,36043) +(46917,9891),(46887,9870) +(37179,657),(37103,634) +(3877,44736),(3811,44684) +(30556,2975),(30547,2962) +(7629,11447),(7547,11416) +(45687,48147),(45591,48088) +(5635,7184),(5571,7146) +(9611,47327),(9541,47246) +(7119,48224),(7117,48152) +(15233,26480),(15138,26430) +(37468,1526),(37466,1513) +(20855,2786),(20828,2711) +(30538,44084),(30480,44061) +(42231,41527),(42149,41454) +(14963,13239),(14952,13146) +(26819,43996),(26745,43934) +(42172,35953),(42086,35928) +(28785,12611),(28710,12534) +(14089,1704),(14047,1629) +(4343,26242),(4341,26169) +(20327,42244),(20231,42212) +(33671,12700),(33666,12630) +(42144,32642),(42128,32569) +(26590,19483),(26503,19442) +(21741,46259),(21723,46226) +(8822,34700),(8760,34693) +(2710,33521),(2675,33505) +(26067,19998),(26026,19989) +(12244,34509),(12202,34489) +(47162,598),(47119,499) +(33093,49382),(33068,49359) +(35170,26340),(35153,26264) +(22552,35785),(22490,35735) +(36791,23032),(36781,22976) +(22857,10857),(22833,10797) +(47207,37405),(47138,37365) +(21867,2836),(21854,2811) +(3387,31487),(3311,31456) +(47174,48121),(47167,48101) +(24415,22232),(24366,22224) +(7970,29251),(7959,29211) +(18635,31294),(18539,31221) +(8403,13380),(8370,13372) +(738,18097),(737,18054) +(37238,19195),(37218,19114) +(582,47934),(570,47897) +(12359,4635),(12350,4619) +(43272,2013),(43195,1958) +(47568,27149),(47521,27088) +(24695,12827),(24661,12796) +(26259,14077),(26168,14019) +(48478,36135),(48425,36092) +(5230,39250),(5206,39174) +(3488,18562),(3423,18489) +(39502,16331),(39460,16275) +(18296,1478),(18233,1471) +(28627,12430),(28559,12410) +(25257,21981),(25206,21954) +(2410,41192),(2325,41142) +(43681,9631),(43587,9538) +(15086,45309),(15064,45270) +(13824,40807),(13759,40787) +(7090,2207),(7062,2159) +(3685,2480),(3630,2391) +(14810,38335),(14801,38275) +(26668,38018),(26581,38012) +(45562,1517),(45506,1424) +(11001,32481),(10962,32402) +(27743,25245),(27673,25161) +(15952,10598),(15948,10535) +(12705,13308),(12694,13232) +(31992,21195),(31975,21118) +(25834,16652),(25745,16626) +(21022,43625),(20990,43576) +(45094,27254),(45000,27240) +(9688,42601),(9643,42533) +(17746,24659),(17694,24616) +(1509,38859),(1503,38809) +(2067,20438),(2041,20369) +(7885,44528),(7839,44444) +(27432,33052),(27422,32987) +(26577,17157),(26563,17142) +(10815,35985),(10734,35908) +(44891,24067),(44794,23979) +(48626,1900),(48595,1850) +(40659,35541),(40659,35489) +(22231,26628),(22210,26579) +(37408,23016),(37375,22919) +(5920,15916),(5906,15895) +(33125,9952),(33037,9880) +(12142,29705),(12141,29670) +(3672,20995),(3649,20899) +(39147,31967),(39101,31907) +(33812,48458),(33748,48399) +(25038,14639),(24978,14586) +(3859,16010),(3857,15994) +(31926,39496),(31889,39417) +(49300,28064),(49297,28026) +(24121,38305),(24048,38256) +(9252,4205),(9155,4149) +(36124,30451),(36056,30395) +(28809,49557),(28794,49533) +(30500,44504),(30471,44476) +(26866,42395),(26822,42332) +(48195,1784),(48101,1734) +(46201,14109),(46112,14097) +(2415,9975),(2354,9914) +(30485,9581),(30415,9558) +(6385,36838),(6305,36838) +(2799,11189),(2723,11095) +(21998,20503),(21923,20406) +(29151,10714),(29090,10671) +(28850,29276),(28757,29207) +(43386,48845),(43305,48834) +(25173,8310),(25101,8294) +(34244,32352),(34204,32342) +(35595,23728),(35533,23672) +(1122,13581),(1119,13538) +(388,21716),(296,21678) +(48782,11064),(48701,11005) +(40293,12997),(40213,12927) +(28194,46428),(28113,46414) +(4791,18118),(4708,18105) +(471,29808),(448,29775) +(3536,37803),(3447,37737) +(1336,28416),(1275,28392) +(16484,48478),(16422,48454) +(25846,19320),(25811,19296) +(48669,27703),(48575,27615) +(24032,44217),(24029,44127) +(12236,5019),(12233,4986) +(1179,29838),(1113,29778) +(33893,22049),(33867,21955) +(16718,19462),(16700,19440) +(17992,49438),(17894,49433) +(35163,39941),(35081,39885) +(33897,8362),(33853,8328) +(2480,6640),(2456,6599) +(28011,19729),(27937,19679) +(15819,41516),(15809,41440) +(29818,9136),(29747,9089) +(28551,37016),(28529,36941) +(36406,26879),(36374,26872) +(16821,48925),(16758,48914) +(23692,48163),(23595,48160) +(4803,10619),(4759,10522) +(46600,33581),(46553,33518) +(41349,11767),(41310,11710) +(20856,29642),(20799,29562) +(16559,46161),(16504,46131) +(23041,1300),(23003,1287) +(16630,44902),(16554,44853) +(43065,14299),(43013,14274) +(24818,22397),(24796,22348) +(22282,24949),(22218,24921) +(36668,28538),(36631,28456) +(8080,1220),(8018,1146) +(47282,34302),(47277,34269) +(35603,33558),(35557,33495) +(44764,32189),(44700,32175) +(46488,23965),(46449,23868) +(46314,15047),(46216,15013) +(6348,25381),(6286,25363) +(3871,49288),(3819,49251) +(462,38894),(398,38867) +(23196,29214),(23136,29169) +(29024,9775),(29016,9759) +(42016,18555),(41934,18472) +(8772,45981),(8692,45973) +(11028,1351),(10986,1278) +(26684,21668),(26641,21656) +(37262,26005),(37260,25947) +(14899,44069),(14814,44066) +(39635,18701),(39587,18698) +(28528,22948),(28457,22857) +(7755,36528),(7681,36454) +(32461,1172),(32427,1106) +(18775,27359),(18736,27329) +(15379,20031),(15337,19934) +(45888,33592),(45881,33544) +(44013,24694),(43962,24645) +(43347,10699),(43343,10699) +(49999,27218),(49908,27176) +(13698,17326),(13630,17317) +(34850,44313),(34775,44302) +(38076,49235),(37983,49214) +(35570,40218),(35500,40136) +(40062,28973),(40032,28878) +(3567,39847),(3523,39781) +(498,2442),(480,2401) +(29660,43620),(29577,43561) +(10946,47356),(10878,47351) +(8073,44233),(8005,44144) +(9720,13473),(9710,13462) +(3643,38014),(3598,37932) +(16887,1408),(16810,1375) +(7559,27914),(7508,27874) +(30356,18573),(30275,18569) +(12193,48176),(12130,48116) +(11884,7756),(11819,7731) +(18293,33272),(18227,33234) +(46697,47874),(46696,47828) +(35788,32517),(35760,32446) +(33877,36987),(33821,36958) +(31253,22819),(31184,22808) +(7744,23115),(7729,23103) +(21291,39817),(21219,39778) +(13877,43379),(13861,43290) +(42955,1406),(42876,1382) +(49232,15950),(49210,15880) +(48419,32001),(48326,31902) +(18940,43246),(18860,43150) +(32317,38240),(32310,38201) +(11307,48298),(11304,48222) +(38015,18190),(38000,18176) +(27821,1177),(27818,1131) +(18935,26757),(18865,26682) +(42659,48284),(42562,48244) +(30185,23350),(30146,23291) +(16496,11970),(16441,11919) +(162,26040),(120,25963) +(24238,47784),(24185,47746) +(32326,8612),(32274,8568) +(26141,13423),(26051,13407) +(40132,22815),(40089,22812) +(21151,48794),(21056,48740) +(22044,28358),(22031,28334) +(6680,14746),(6605,14669) +(40686,25139),(40632,25070) +(22823,27549),(22816,27507) +(2513,22841),(2427,22811) +(36316,27787),(36218,27728) +(554,35489),(540,35441) +(536,30674),(534,30609) +(25385,38468),(25295,38416) +(19467,47386),(19437,47317) +(22425,38591),(22387,38536) +(32493,17321),(32396,17298) +(40115,47315),(40109,47235) +(25002,2107),(24963,2104) +(3901,9790),(3898,9706) +(40316,1721),(40315,1658) +(40089,3454),(40074,3443) +(793,17897),(761,17897) +(6490,43552),(6434,43522) +(10825,487),(10820,405) +(47703,36067),(47641,36011) +(4480,11671),(4468,11653) +(37713,10642),(37711,10615) +(12315,5302),(12273,5203) +(8709,6617),(8647,6557) +(24467,30535),(24455,30494) +(40440,32757),(40369,32668) +(49449,42447),(49426,42428) +(44867,11197),(44792,11137) +(39173,33241),(39143,33187) +(43836,2212),(43803,2184) +(23819,47613),(23739,47575) +(20583,2134),(20485,2042) +(48922,6169),(48889,6111) +(5230,44613),(5131,44604) +(37060,8051),(37032,7975) +(19148,36711),(19112,36704) +(36305,4216),(36243,4118) +(6329,39089),(6302,39047) +(36703,26367),(36623,26307) +(44753,19721),(44701,19631) +(42094,43310),(42094,43285) +(4276,22377),(4241,22352) +(30329,18906),(30327,18815) +(21970,19605),(21871,19590) +(23722,41924),(23709,41861) +(30965,39775),(30908,39692) +(32394,37895),(32351,37890) +(23968,42162),(23873,42095) +(1776,2621),(1732,2548) +(24951,47758),(24900,47679) +(32917,35771),(32847,35753) +(5428,27773),(5343,27769) +(19650,142),(19630,51) +(39769,17276),(39743,17229) +(5171,24562),(5119,24470) +(32976,35249),(32917,35199) +(4174,24603),(4099,24504) +(38565,36960),(38535,36926) +(39084,4328),(39031,4301) +(32153,38043),(32070,37990) +(38085,30640),(38041,30603) +(14269,18426),(14185,18422) +(42941,30850),(42892,30788) +(32403,25999),(32339,25960) +(16906,191),(16816,139) +(3456,48722),(3418,48721) +(3050,18287),(3022,18243) +(6331,8439),(6234,8364) +(5331,20797),(5319,20793) +(39225,37408),(39216,37348) +(34510,19838),(34488,19810) +(45789,33873),(45770,33786) +(369,1457),(278,1409) +(16531,43785),(16482,43729) +(11974,14789),(11973,14730) +(23128,6811),(23094,6798) +(43962,33659),(43944,33599) +(20967,3115),(20947,3079) +(39257,38606),(39241,38595) +(22431,8246),(22381,8235) +(26007,14672),(25996,14593) +(24762,4261),(24675,4261) +(35402,32077),(35343,31988) +(5141,16476),(5139,16393) +(16439,17564),(16344,17472) +(36983,46663),(36903,46567) +(35170,14144),(35162,14048) +(22290,7841),(22283,7810) +(22414,38398),(22404,38319) +(9011,18177),(8932,18150) +(154,4019),(138,3990) +(20447,4998),(20383,4970) +(38867,35757),(38795,35659) +(32322,15845),(32227,15804) +(29889,12142),(29852,12055) +(36235,36918),(36217,36897) +(41620,6581),(41568,6581) +(24758,38504),(24731,38483) +(42524,12904),(42473,12895) +(17954,49975),(17865,49915) +(1938,39019),(1927,39013) +(4864,33279),(4817,33258) +(45373,41967),(45313,41885) +(28786,19028),(28782,18978) +(41913,44950),(41911,44908) +(33408,14698),(33392,14681) +(27602,3460),(27576,3419) +(3336,3728),(3334,3715) +(9099,910),(9080,813) +(34141,6403),(34071,6367) +(48270,17216),(48252,17130) +(2549,16546),(2461,16474) +(27802,33669),(27735,33642) +(48419,1682),(48323,1583) +(5094,41211),(5002,41123) +(11192,6217),(11190,6146) +(6979,18503),(6959,18421) +(41210,48187),(41140,48143) +(15303,29527),(15273,29441) +(12326,45572),(12267,45570) +(29293,5861),(29212,5826) +(23847,37241),(23761,37178) +(44656,23926),(44653,23831) +(30043,16194),(29977,16105) +(902,9358),(879,9339) +(23850,46501),(23834,46494) +(42333,13300),(42287,13246) +(25226,18086),(25169,18005) +(40252,12082),(40183,12038) +(49275,18076),(49216,18055) +(8255,28878),(8238,28862) +(11325,41286),(11320,41235) +(16948,18588),(16926,18528) +(31394,1099),(31374,1038) +(30705,35772),(30637,35766) +(3858,39131),(3771,39125) +(17565,24892),(17515,24808) +(9221,49715),(9216,49661) +(44945,25769),(44875,25722) +(33408,13563),(33310,13527) +(48505,4407),(48408,4373) +(21859,37217),(21763,37217) +(39393,14422),(39335,14364) +(19905,1154),(19841,1098) +(25946,10388),(25906,10366) +(10104,13748),(10027,13746) +(5822,24629),(5820,24599) +(38194,11287),(38127,11252) +(15694,46757),(15625,46716) +(326,18837),(285,18817) +(49611,47078),(49533,47052) +(48233,18850),(48150,18842) +(29239,9962),(29208,9875) +(40062,44554),(39973,44460) +(19135,20729),(19059,20643) +(31969,40664),(31896,40643) +(3725,9191),(3711,9095) +(44280,40158),(44264,40108) +(37236,42756),(37160,42694) +(27958,19055),(27888,18959) +(45270,17661),(45187,17601) +(12115,39546),(12061,39525) +(10227,32295),(10168,32231) +(39264,31123),(39226,31085) +(6566,40000),(6532,39904) +(30058,6975),(30012,6903) +(49631,6909),(49597,6823) +(42168,10926),(42134,10905) +(44892,30042),(44858,29970) +(19540,19803),(19495,19788) +(18403,25454),(18371,25404) +(22929,26795),(22841,26722) +(16648,30213),(16626,30174) +(3440,7495),(3429,7468) +(30708,49028),(30643,48998) +(26258,14164),(26255,14151) +(44206,31653),(44121,31637) +(1510,15179),(1426,15130) +(6986,30496),(6887,30416) +(7192,43403),(7138,43339) +(39921,22071),(39866,21976) +(45870,17011),(45796,16919) +(15939,9563),(15917,9539) +(23728,24737),(23691,24725) +(6444,40416),(6363,40375) +(21899,23861),(21857,23765) +(20610,36765),(20533,36742) +(46520,33082),(46433,32983) +(21406,20902),(21311,20895) +(37913,42300),(37814,42269) +(18216,8177),(18161,8173) +(32967,8258),(32899,8244) +(14978,40230),(14971,40149) +(30343,39152),(30266,39101) +(25917,5835),(25843,5806) +(5169,45366),(5141,45314) +(16221,20898),(16209,20875) +(13151,19869),(13145,19811) +(44399,2801),(44337,2713) +(10959,48311),(10957,48230) +(4794,11711),(4732,11661) +(764,10149),(762,10091) +(15985,46067),(15898,46028) +(41434,22870),(41342,22867) +(43769,23796),(43743,23756) +(10017,18440),(9919,18384) +(21141,43119),(21097,43112) +(7782,13424),(7694,13398) +(25088,36224),(25059,36150) +(46325,48722),(46241,48631) +(11042,33125),(11011,33071) +(22347,13460),(22290,13375) +(3508,20538),(3483,20536) +(5331,42945),(5272,42875) +(2368,15537),(2339,15503) +(45314,31830),(45254,31817) +(34358,2649),(34319,2589) +(17576,30407),(17572,30323) +(29836,41324),(29746,41287) +(21036,39996),(21014,39899) +(26886,6460),(26787,6400) +(15709,5625),(15627,5558) +(37415,15979),(37414,15911) +(47761,16860),(47728,16813) +(35814,48252),(35755,48173) +(28559,20810),(28496,20715) +(12034,11921),(12002,11905) +(1818,27450),(1805,27406) +(33810,45499),(33806,45413) +(17376,18175),(17323,18138) +(34106,28135),(34049,28106) +(44947,23165),(44919,23091) +(37670,41904),(37616,41840) +(12614,15027),(12555,14969) +(43301,75),(43227,43) +(27526,15096),(27450,15088) +(26947,33409),(26853,33333) +(1537,43572),(1471,43499) +(21607,35452),(21605,35375) +(24869,46565),(24818,46531) +(4774,30335),(4723,30257) +(11615,18316),(11579,18310) +(18444,15819),(18354,15763) +(47267,22574),(47203,22518) +(22287,49538),(22203,49511) +(43010,16270),(43010,16202) +(1623,8350),(1578,8254) +(21220,43808),(21137,43748) +(40397,16471),(40358,16434) +(34839,1377),(34744,1327) +(17096,5730),(17090,5637) +(28156,37782),(28155,37723) +(3672,5686),(3586,5638) +(21856,48656),(21840,48638) +(6907,7791),(6892,7761) +(17952,21370),(17862,21350) +(37793,13461),(37784,13381) +(14740,49655),(14709,49604) +(21690,6337),(21593,6289) +(10423,33548),(10364,33498) +(39187,23274),(39136,23197) +(21882,37247),(21835,37167) +(11343,16957),(11281,16914) +(38279,43400),(38264,43352) +(23167,30271),(23086,30224) +(46278,6037),(46180,5964) +(28626,31165),(28605,31095) +(31018,367),(30946,333) +(23541,12541),(23530,12523) +(49741,14535),(49691,14511) +(31444,12702),(31425,12612) +(22406,26536),(22316,26534) +(6807,9761),(6758,9723) +(15698,1941),(15687,1848) +(49310,4625),(49295,4584) +(21345,18939),(21269,18887) +(31433,30493),(31411,30439) +(44980,12400),(44950,12372) +(25054,13949),(24984,13949) +(40538,7253),(40483,7212) +(16967,8627),(16936,8604) +(26872,3646),(26804,3594) +(24575,42883),(24530,42883) +(11823,5755),(11771,5721) +(2553,46189),(2513,46174) +(24993,14552),(24898,14470) +(28453,1719),(28419,1665) +(8925,22603),(8878,22589) +(47635,15380),(47546,15378) +(35378,18112),(35324,18058) +(27347,22264),(27293,22200) +(44323,29044),(44273,28958) +(41538,38324),(41484,38290) +(19128,49932),(19112,49849) +(17904,12548),(17867,12503) +(35103,14426),(35092,14336) +(29807,10142),(29714,10052) +(44507,22903),(44462,22847) +(11419,13324),(11399,13251) +(8573,42221),(8562,42123) +(46798,45843),(46765,45765) +(12028,31783),(11967,31749) +(10635,45300),(10604,45251) +(9626,8248),(9587,8194) +(18290,741),(18246,732) +(39949,44672),(39932,44641) +(7897,11692),(7893,11637) +(20165,42246),(20112,42168) +(4341,48390),(4285,48338) +(30126,28913),(30088,28869) +(40565,1733),(40472,1721) +(9981,30147),(9915,30133) +(47292,25511),(47217,25462) +(20137,24489),(20104,24392) +(2385,28283),(2381,28189) +(20429,10052),(20357,10009) +(8395,38568),(8348,38480) +(17381,36112),(17349,36038) +(37845,30953),(37759,30926) +(27452,12732),(27411,12652) +(38196,32186),(38114,32116) +(6527,49356),(6508,49315) +(43891,29789),(43856,29723) +(6146,37192),(6085,37107) +(42012,28897),(41939,28808) +(14909,13815),(14846,13757) +(11120,24095),(11035,24049) +(3132,41545),(3053,41526) +(40084,40315),(39994,40261) +(39671,17445),(39576,17361) +(47135,35853),(47085,35831) +(39297,1941),(39290,1911) +(47143,35898),(47072,35880) +(16017,6711),(15989,6686) +(47110,30305),(47087,30213) +(38102,27639),(38091,27602) +(17954,22544),(17863,22453) +(39891,11791),(39815,11739) +(13996,20290),(13922,20278) +(22284,23143),(22190,23081) +(25345,24019),(25313,24017) +(47134,44803),(47055,44761) +(41360,16573),(41326,16503) +(10464,1071),(10457,998) +(23515,47517),(23451,47499) +(9308,8452),(9238,8392) +(28695,5657),(28671,5644) +(45104,9913),(45077,9871) +(337,455),(240,359) +(11562,45479),(11472,45428) +(11952,18466),(11931,18425) +(35789,5154),(35775,5128) +(19024,18299),(18979,18230) +(43056,38113),(42975,38067) +(10075,26847),(10064,26806) +(3065,8107),(3029,8038) +(24766,19059),(24749,18985) +(14438,24805),(14413,24708) +(9523,3058),(9485,2998) +(24516,31262),(24478,31204) +(49513,26044),(49434,26035) +(14110,38528),(14103,38461) +(31679,35618),(31619,35618) +(10029,20258),(10008,20248) +(39269,37586),(39233,37539) +(12343,8197),(12247,8113) +(11155,44223),(11111,44134) +(25437,20606),(25338,20534) +(46604,16156),(46570,16131) +(4636,14004),(4592,13941) +(15975,29628),(15912,29556) +(49887,24274),(49805,24184) +(11812,13440),(11723,13418) +(21589,38179),(21531,38085) +(32255,44463),(32219,44454) +(15023,12698),(14989,12687) +(28906,48630),(28818,48568) +(28886,38905),(28861,38832) +(34786,22285),(34740,22240) +(46513,46780),(46425,46780) +(26626,31759),(26551,31677) +(19792,25967),(19763,25933) +(20432,14394),(20388,14365) +(27092,7301),(27052,7278) +(22283,987),(22198,928) +(6197,24363),(6112,24311) +(46601,49259),(46551,49231) +(12392,48052),(12363,48038) +(46116,31386),(46067,31356) +(7354,16855),(7289,16778) +(47501,42808),(47495,42761) +(16461,25487),(16391,25398) +(42678,18798),(42678,18756) +(9466,18207),(9419,18185) +(17467,14177),(17416,14097) +(28533,31886),(28487,31832) +(13225,38472),(13188,38395) +(5180,40970),(5173,40902) +(83,10271),(15,10265) +(2111,6784),(2016,6690) +(41835,11064),(41798,10995) +(29273,48585),(29181,48536) +(29066,21615),(28985,21543) +(19805,44143),(19727,44128) +(48919,21468),(48875,21467) +(28790,34287),(28721,34251) +(10911,33074),(10869,32989) +(6111,16519),(6032,16489) +(43889,33838),(43837,33768) +(32323,21685),(32304,21644) +(9552,27819),(9539,27753) +(38266,49852),(38233,49844) +(37672,48362),(37663,48277) +(32550,47029),(32529,46931) +(46307,6620),(46272,6616) +(23192,46608),(23105,46566) +(30399,48330),(30335,48239) +(36268,25058),(36235,24984) +(19181,8120),(19089,8098) +(24376,19983),(24294,19925) +(18297,18375),(18202,18292) +(31608,6215),(31575,6168) +(12788,49510),(12784,49468) +(46071,13013),(46035,12991) +(27647,8218),(27582,8201) +(49580,11076),(49537,11050) +(35501,33782),(35501,33687) +(19969,3148),(19964,3082) +(37728,49153),(37726,49152) +(5322,48440),(5321,48435) +(48003,10096),(47904,10005) +(39361,22318),(39348,22236) +(30488,7456),(30437,7430) +(18533,39476),(18481,39394) +(39462,23701),(39433,23604) +(26701,18300),(26686,18235) +(17405,35577),(17387,35517) +(33971,29928),(33953,29919) +(6328,10241),(6276,10217) +(32459,44259),(32453,44217) +(1715,42385),(1647,42357) +(48113,6960),(48103,6872) +(30561,4255),(30476,4240) +(38907,43619),(38827,43553) +(29149,20773),(29070,20698) +(17006,1543),(16970,1497) +(11737,18808),(11714,18788) +(13019,30534),(13005,30481) +(39224,31729),(39191,31683) +(4942,41680),(4907,41596) +(12287,37187),(12188,37172) +(30758,29579),(30725,29531) +(16604,17963),(16581,17912) +(19459,15888),(19409,15812) +(34696,24783),(34600,24725) +(21621,14159),(21558,14110) +(12193,46149),(12145,46096) +(37781,4715),(37692,4635) +(41854,44125),(41807,44040) +(23604,23585),(23571,23533) +(7853,36967),(7797,36908) +(2755,13279),(2720,13206) +(4314,15424),(4283,15383) +(29584,12685),(29493,12594) +(25138,33726),(25042,33691) +(38393,10270),(38326,10185) +(4247,12615),(4225,12567) +(36100,33156),(36100,33107) +(20024,40796),(20016,40708) +(3927,44892),(3914,44843) +(10317,43168),(10226,43096) +(22057,3419),(22042,3334) +(37097,21814),(37025,21811) +(32084,21564),(31996,21491) +(34079,39921),(34058,39911) +(23078,47459),(23018,47373) +(38109,616),(38082,568) +(11862,40382),(11764,40292) +(33403,33320),(33389,33289) +(36639,24829),(36623,24829) +(12995,45080),(12992,45040) +(16545,19981),(16532,19891) +(26155,10659),(26154,10634) +(24423,255),(24360,213) +(823,22487),(781,22442) +(12823,20064),(12735,20040) +(19688,11710),(19681,11654) +(2892,20452),(2836,20424) +(15533,10807),(15464,10711) +(46994,41143),(46955,41082) +(18155,2421),(18069,2392) +(2628,12688),(2605,12602) +(35128,8396),(35044,8365) +(44765,49615),(44758,49524) +(11226,44529),(11178,44515) +(31334,32463),(31291,32456) +(43224,23387),(43168,23364) +(30882,10414),(30798,10395) +(29139,967),(29139,923) +(29959,45244),(29877,45223) +(19946,217),(19941,118) +(49732,22033),(49642,22012) +(32914,15360),(32879,15290) +(47825,21097),(47747,21030) +(10788,5131),(10746,5086) +(15497,9698),(15481,9678) +(10617,47195),(10601,47117) +(42392,10583),(42340,10550) +(10753,33520),(10669,33509) +(5553,21580),(5521,21527) +(36840,12336),(36817,12320) +(49785,12554),(49702,12553) +(17737,38349),(17639,38277) +(48000,7823),(47956,7814) +(5019,3184),(4931,3160) +(30120,3524),(30063,3492) +(37044,2016),(37001,1942) +(23496,38566),(23469,38528) +(17255,48957),(17200,48903) +(27815,2138),(27808,2090) +(40440,11129),(40368,11105) +(35305,21772),(35272,21717) +(41308,45065),(41229,44973) +(14893,28807),(14817,28789) +(30776,45824),(30731,45772) +(742,40724),(652,40672) +(5985,41133),(5927,41097) +(9576,10226),(9540,10218) +(21407,23207),(21323,23160) +(44880,34228),(44877,34169) +(29146,49694),(29143,49682) +(28502,34886),(28471,34832) +(30662,5584),(30604,5528) +(12612,26081),(12552,26001) +(17166,49308),(17098,49270) +(9586,14116),(9488,14104) +(37323,47576),(37264,47482) +(48009,49713),(48004,49614) +(49308,23780),(49297,23760) +(8667,32342),(8592,32294) +(37826,48560),(37822,48485) +(24493,18653),(24486,18616) +(17914,3850),(17887,3775) +(34270,43873),(34231,43826) +(7753,44715),(7660,44651) +(44328,36364),(44265,36350) +(10146,3030),(10111,2975) +(35273,40106),(35269,40062) +(38566,43846),(38547,43760) +(12400,41394),(12377,41378) +(45196,38286),(45153,38250) +(48511,14972),(48428,14883) +(25939,36328),(25886,36277) +(38997,11007),(38979,10917) +(30342,518),(30244,453) +(6876,7468),(6867,7454) +(17566,27575),(17566,27480) +(18869,28538),(18858,28475) +(16825,33309),(16726,33255) +(14585,26111),(14490,26035) +(28743,49392),(28664,49349) +(26652,23359),(26618,23297) +(40129,33653),(40102,33584) +(41074,26393),(41038,26389) +(3869,33564),(3869,33536) +(28455,14205),(28364,14163) +(13866,45603),(13770,45543) +(21666,30586),(21578,30544) +(29978,11931),(29893,11868) +(1594,1043),(1517,971) +(948,1201),(907,1156) +(27547,13692),(27545,13677) +(13661,38184),(13566,38154) +(2389,40026),(2317,39938) +(35481,46379),(35481,46320) +(26917,45698),(26864,45689) +(23933,41617),(23909,41539) +(8912,8471),(8862,8401) +(9625,4747),(9558,4692) +(34743,35056),(34721,34969) +(39544,21762),(39475,21717) +(11741,26330),(11656,26293) +(39015,1315),(38966,1285) +(13418,44237),(13326,44202) +(2107,17672),(2093,17616) +(42448,28844),(42370,28764) +(49843,5175),(49808,5145) +(6536,23000),(6467,22958) +(11114,5822),(11027,5739) +(48457,11074),(48384,11024) +(12343,23110),(12310,23074) +(17300,24847),(17276,24825) +(8823,8253),(8793,8238) +(3449,171),(3354,108) +(21650,23955),(21605,23883) +(13260,3234),(13193,3214) +(25361,10896),(25305,10806) +(25051,25042),(25011,25001) +(25044,25088),(25015,25005) +(25007,25061),(25002,25013) +(25066,25105),(25003,25007) +(25028,25012),(25015,25011) +(25031,25057),(25006,25018) +(25015,25042),(25004,25012) +(25091,25049),(25019,25019) +(25023,25011),(25000,25004) +(25053,25104),(25010,25012) +(25058,25001),(25018,25000) +(25059,25051),(25008,25016) +(25043,25069),(25007,25004) +(25006,25101),(25002,25002) +(25095,25012),(25014,25007) +(25054,25052),(25019,25013) +(25108,25077),(25009,25018) +(25007,25023),(25003,25002) +(25076,25098),(25002,25016) +(25030,25077),(25012,25006) diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out new file mode 100644 index 0000000000..fae662547d --- /dev/null +++ b/contrib/cube/expected/cube.out @@ -0,0 +1,962 @@ +-- +-- Test cube datatype +-- +-- +-- first, define the datatype. Turn off echoing so that expected file +-- does not depend on contents of cube.sql. +-- +\set ECHO none +-- +-- testing the input and output functions +-- +-- Any number (a one-dimensional point) +SELECT '1'::cube AS cube; + cube +------ + (1) +(1 row) + +SELECT '-1'::cube AS cube; + cube +------ + (-1) +(1 row) + +SELECT '1.'::cube AS cube; + cube +------ + (1) +(1 row) + +SELECT '-1.'::cube AS cube; + cube +------ + (-1) +(1 row) + +SELECT '.1'::cube AS cube; + cube +------- + (0.1) +(1 row) + +SELECT '-.1'::cube AS cube; +ERROR: parse error, expecting `FLOAT' or `O_PAREN' or `O_BRACKET' at or before position 2, character ('.', \056), input: '-.1' + +SELECT '1.0'::cube AS cube; + cube +------ + (1) +(1 row) + +SELECT '-1.0'::cube AS cube; + cube +------ + (-1) +(1 row) + +SELECT '1e7'::cube AS cube; + cube +--------- + (1e+07) +(1 row) + +SELECT '-1e7'::cube AS cube; + cube +---------- + (-1e+07) +(1 row) + +SELECT '1.0e7'::cube AS cube; + cube +--------- + (1e+07) +(1 row) + +SELECT '-1.0e7'::cube AS cube; + cube +---------- + (-1e+07) +(1 row) + +SELECT '1e+7'::cube AS cube; + cube +--------- + (1e+07) +(1 row) + +SELECT '-1e+7'::cube AS cube; + cube +---------- + (-1e+07) +(1 row) + +SELECT '1.0e+7'::cube AS cube; + cube +--------- + (1e+07) +(1 row) + +SELECT '-1.0e+7'::cube AS cube; + cube +---------- + (-1e+07) +(1 row) + +SELECT '1e-7'::cube AS cube; + cube +--------- + (1e-07) +(1 row) + +SELECT '-1e-7'::cube AS cube; + cube +---------- + (-1e-07) +(1 row) + +SELECT '1.0e-7'::cube AS cube; + cube +--------- + (1e-07) +(1 row) + +SELECT '-1.0e-7'::cube AS cube; + cube +---------- + (-1e-07) +(1 row) + +SELECT '1e700'::cube AS cube; + cube +------- + (inf) +(1 row) + +SELECT '-1e700'::cube AS cube; + cube +-------- + (-inf) +(1 row) + +SELECT '1e-700'::cube AS cube; + cube +------ + (0) +(1 row) + +SELECT '-1e-700'::cube AS cube; + cube +------ + (0) +(1 row) + +-- simple lists (points) +SELECT '1,2'::cube AS cube; + cube +-------- + (1, 2) +(1 row) + +SELECT '(1,2)'::cube AS cube; + cube +-------- + (1, 2) +(1 row) + +SELECT '1,2,3,4,5'::cube AS cube; + cube +----------------- + (1, 2, 3, 4, 5) +(1 row) + +SELECT '(1,2,3,4,5)'::cube AS cube; + cube +----------------- + (1, 2, 3, 4, 5) +(1 row) + +-- double lists (cubes) +SELECT '(0),(0)'::cube AS cube; + cube +------ + (0) +(1 row) + +SELECT '(0),(1)'::cube AS cube; + cube +--------- + (0),(1) +(1 row) + +SELECT '[(0),(0)]'::cube AS cube; + cube +------ + (0) +(1 row) + +SELECT '[(0),(1)]'::cube AS cube; + cube +--------- + (0),(1) +(1 row) + +SELECT '(0,0,0,0),(0,0,0,0)'::cube AS cube; + cube +-------------- + (0, 0, 0, 0) +(1 row) + +SELECT '(0,0,0,0),(1,0,0,0)'::cube AS cube; + cube +--------------------------- + (0, 0, 0, 0),(1, 0, 0, 0) +(1 row) + +SELECT '[(0,0,0,0),(0,0,0,0)]'::cube AS cube; + cube +-------------- + (0, 0, 0, 0) +(1 row) + +SELECT '[(0,0,0,0),(1,0,0,0)]'::cube AS cube; + cube +--------------------------- + (0, 0, 0, 0),(1, 0, 0, 0) +(1 row) + +-- invalid input: parse errors +SELECT ''::cube AS cube; +ERROR: cube_in: can't parse an empty string +SELECT 'ABC'::cube AS cube; +ERROR: parse error, expecting `FLOAT' or `O_PAREN' or `O_BRACKET' at or before position 1, character ('A', \101), input: 'ABC' + +SELECT '()'::cube AS cube; +ERROR: parse error, expecting `FLOAT' at or before position 2, character (')', \051), input: '()' + +SELECT '[]'::cube AS cube; +ERROR: parse error, expecting `O_PAREN' at or before position 2, character (']', \135), input: '[]' + +SELECT '[()]'::cube AS cube; +ERROR: parse error, expecting `FLOAT' at or before position 3, character (')', \051), input: '[()]' + +SELECT '[(1)]'::cube AS cube; +ERROR: parse error, expecting `COMMA' at or before position 5, character (']', \135), input: '[(1)]' + +SELECT '[(1),]'::cube AS cube; +ERROR: parse error, expecting `O_PAREN' at or before position 6, character (']', \135), input: '[(1),]' + +SELECT '[(1),2]'::cube AS cube; +ERROR: parse error, expecting `O_PAREN' at or before position 7, character (']', \135), input: '[(1),2]' + +SELECT '[(1),(2),(3)]'::cube AS cube; +ERROR: parse error, expecting `C_BRACKET' at or before position 9, character (',', \054), input: '[(1),(2),(3)]' + +SELECT '1,'::cube AS cube; +ERROR: parse error, expecting `FLOAT' at or before position 2, character (',', \054), input: '1,' + +SELECT '1,2,'::cube AS cube; +ERROR: parse error, expecting `FLOAT' at or before position 4, character (',', \054), input: '1,2,' + +SELECT '1,,2'::cube AS cube; +ERROR: parse error, expecting `FLOAT' at or before position 3, character (',', \054), input: '1,,2' + +SELECT '(1,)'::cube AS cube; +ERROR: parse error, expecting `FLOAT' at or before position 4, character (')', \051), input: '(1,)' + +SELECT '(1,2,)'::cube AS cube; +ERROR: parse error, expecting `FLOAT' at or before position 6, character (')', \051), input: '(1,2,)' + +SELECT '(1,,2)'::cube AS cube; +ERROR: parse error, expecting `FLOAT' at or before position 4, character (',', \054), input: '(1,,2)' + +-- invalid input: semantic errors and trailing garbage +SELECT '[(1),(2)],'::cube AS cube; -- 0 +ERROR: (0) bad cube representation; garbage at or before char 9, (',', \054) + +SELECT '[(1,2,3),(2,3)]'::cube AS cube; -- 1 +ERROR: (1) bad cube representation; different point dimensions in (1,2,3) and (2,3) + +SELECT '[(1,2),(1,2,3)]'::cube AS cube; -- 1 +ERROR: (1) bad cube representation; different point dimensions in (1,2) and (1,2,3) + +SELECT '(1),(2),'::cube AS cube; -- 2 +ERROR: (2) bad cube representation; garbage at or before char 7, (',', \054) + +SELECT '(1,2,3),(2,3)'::cube AS cube; -- 3 +ERROR: (3) bad cube representation; different point dimensions in (1,2,3) and (2,3) + +SELECT '(1,2),(1,2,3)'::cube AS cube; -- 3 +ERROR: (3) bad cube representation; different point dimensions in (1,2) and (1,2,3) + +SELECT '(1,2,3)ab'::cube AS cube; -- 4 +ERROR: (4) bad cube representation; garbage at or before char 8, ('b', \142) + +SELECT '(1,2,3)a'::cube AS cube; -- 5 +ERROR: (5) bad cube representation; garbage at or before char 8, ('end of input', \000) + +SELECT '(1,2)('::cube AS cube; -- 5 +ERROR: (5) bad cube representation; garbage at or before char 6, ('end of input', \000) + +SELECT '1,2ab'::cube AS cube; -- 6 +ERROR: (6) bad cube representation; garbage at or before char 4, ('b', \142) + +SELECT '1 e7'::cube AS cube; -- 6 +ERROR: (6) bad cube representation; garbage at or before char 3, ('7', \067) + +SELECT '1,2a'::cube AS cube; -- 7 +ERROR: (7) bad cube representation; garbage at or before char 4, ('end of input', \000) + +SELECT '1..2'::cube AS cube; -- 7 +ERROR: (7) bad cube representation; garbage at or before char 4, ('end of input', \000) + +-- +-- testing the operators +-- +-- equality/inequality: +-- +SELECT '24, 33.20'::cube = '24, 33.20'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '24, 33.20'::cube != '24, 33.20'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '24, 33.20'::cube = '24, 33.21'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '24, 33.20'::cube != '24, 33.21'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(2,0),(3,1)'::cube = '(2,0,0,0,0),(3,1,0,0,0)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(2,0),(3,1)'::cube = '(2,0,0,0,0),(3,1,0,0,1)'::cube AS bool; + bool +------ + f +(1 row) + +-- "lower than" / "greater than" +-- (these operators are not useful for anything but ordering) +-- +SELECT '1'::cube > '2'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '1'::cube < '2'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '1,1'::cube > '1,2'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '1,1'::cube < '1,2'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(2,0),(3,1)'::cube > '(2,0,0,0,0),(3,1,0,0,1)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(2,0),(3,1)'::cube < '(2,0,0,0,0),(3,1,0,0,1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(2,0),(3,1)'::cube > '(2,0,0,0,1),(3,1,0,0,0)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(2,0),(3,1)'::cube < '(2,0,0,0,1),(3,1,0,0,0)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(2,0),(3,1)'::cube > '(2,0,0,0,0),(3,1,0,0,0)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(2,0),(3,1)'::cube < '(2,0,0,0,0),(3,1,0,0,0)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(2,0,0,0,0),(3,1,0,0,1)'::cube > '(2,0),(3,1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(2,0,0,0,0),(3,1,0,0,1)'::cube < '(2,0),(3,1)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(2,0,0,0,1),(3,1,0,0,0)'::cube > '(2,0),(3,1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(2,0,0,0,1),(3,1,0,0,0)'::cube < '(2,0),(3,1)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(2,0,0,0,0),(3,1,0,0,0)'::cube > '(2,0),(3,1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(2,0,0,0,0),(3,1,0,0,0)'::cube < '(2,0),(3,1)'::cube AS bool; + bool +------ + f +(1 row) + +-- "overlap" +-- +SELECT '1'::cube && '1'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '1'::cube && '2'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '0'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '1'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '1,1,1'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '[(1,1,1),(2,2,2)]'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '[(1,1),(2,2)]'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '[(2,1,1),(2,2,2)]'::cube AS bool; + bool +------ + f +(1 row) + +-- "overlap on the left" / "overlap on the right" +-- (these operators are not useful at all but R-tree seems to be +-- sensitive to their presence) +-- +SELECT '1'::cube &< '0'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '1'::cube &< '1'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '1'::cube &< '2'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(0),(1)'::cube &< '0'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(0),(1)'::cube &< '1'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(0),(1)'::cube &< '(0),(0.5)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(0),(1)'::cube &< '(0),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(0),(1)'::cube &< '(0),(2)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(0),(1)'::cube &< '(1),(2)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(0),(1)'::cube &< '(2),(3)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '0'::cube &> '1'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '1'::cube &> '1'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '2'::cube &> '1'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '0'::cube &> '(0),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '1'::cube &> '(0),(1)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(0),(0.5)' &> '(0),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(0),(1)'::cube &> '(0),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(0),(2)'::cube &> '(0),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(1),(2)'::cube &> '(0),(1)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(2),(3)'::cube &> '(0),(1)'::cube AS bool; + bool +------ + f +(1 row) + +-- "left" / "right" +-- (these operators are not useful but for 1-D or 2-D cubes, but R-tree +-- seems to want them defined) +-- +SELECT '1'::cube << '0'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '1'::cube << '1'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '1'::cube << '2'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(0),(1)'::cube << '0'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(0),(1)'::cube << '1'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(0),(1)'::cube << '(0),(0.5)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(0),(1)'::cube << '(0),(1)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(0),(1)'::cube << '(0),(2)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(0),(1)'::cube << '(1),(2)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(0),(1)'::cube << '(2),(3)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '0'::cube >> '1'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '1'::cube >> '1'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '2'::cube >> '1'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '0'::cube >> '(0),(1)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '1'::cube >> '(0),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(0),(0.5)' >> '(0),(1)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(0),(1)'::cube >> '(0),(1)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(0),(2)'::cube >> '(0),(1)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(1),(2)'::cube >> '(0),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(2),(3)'::cube >> '(0),(1)'::cube AS bool; + bool +------ + t +(1 row) + +-- "contained in" (the left operand is the cube entirely enclosed by +-- the right operand): +-- +SELECT '0'::cube ~ '0'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '0,0,0'::cube ~ '0,0,0'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '0,0'::cube ~ '0,0,1'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '0,0,0'::cube ~ '0,0,1'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '1,0,0'::cube ~ '0,0,1'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(1,0,0),(0,0,1)'::cube ~ '(1,0,0),(0,0,1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(1,0,0),(0,0,1)'::cube ~ '(-1,-1,-1),(1,1,1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(1,0,0),(0,0,1)'::cube ~ '(-1,-1,-1,-1),(1,1,1,1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '0'::cube ~ '(-1),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '1'::cube ~ '(-1),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '-1'::cube ~ '(-1),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(-1),(1)'::cube ~ '(-1),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(-1),(1)'::cube ~ '(-1,-1),(1,1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(-2),(1)'::cube ~ '(-1),(1)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(-2),(1)'::cube ~ '(-1,-1),(1,1)'::cube AS bool; + bool +------ + f +(1 row) + +-- "contains" (the left operand is the cube that entirely encloses the +-- right operand) +-- +SELECT '0'::cube @ '0'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '0,0,0'::cube @ '0,0,0'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '0,0,1'::cube @ '0,0'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '0,0,1'::cube @ '0,0,0'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '0,0,1'::cube @ '1,0,0'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(1,0,0),(0,0,1)'::cube @ '(1,0,0),(0,0,1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(-1,-1,-1),(1,1,1)'::cube @ '(1,0,0),(0,0,1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(-1,-1,-1,-1),(1,1,1,1)'::cube @ '(1,0,0),(0,0,1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(-1),(1)'::cube @ '0'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(-1),(1)'::cube @ '1'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(-1),(1)'::cube @ '-1'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(-1),(1)'::cube @ '(-1),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(-1,-1),(1,1)'::cube @ '(-1),(1)'::cube AS bool; + bool +------ + t +(1 row) + +SELECT '(-1),(1)'::cube @ '(-2),(1)'::cube AS bool; + bool +------ + f +(1 row) + +SELECT '(-1,-1),(1,1)'::cube @ '(-2),(1)'::cube AS bool; + bool +------ + f +(1 row) + +-- Load some example data and build the index +-- +CREATE TABLE test_cube (c cube); +\copy test_cube from 'data/test_cube.data' +CREATE INDEX test_cube_ix ON test_cube USING gist (c); +SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)'; + c +-------------------------- + (2424, 160),(2424, 81) + (759, 187),(662, 163) + (1444, 403),(1346, 344) + (337, 455),(240, 359) + (1594, 1043),(1517, 971) +(5 rows) + +-- Test sorting +SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c; + c +-------------------------- + (337, 455),(240, 359) + (759, 187),(662, 163) + (1444, 403),(1346, 344) + (1594, 1043),(1517, 971) + (2424, 160),(2424, 81) +(5 rows) + diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql new file mode 100644 index 0000000000..70feb8a217 --- /dev/null +++ b/contrib/cube/sql/cube.sql @@ -0,0 +1,246 @@ +-- +-- Test cube datatype +-- + +-- +-- first, define the datatype. Turn off echoing so that expected file +-- does not depend on contents of cube.sql. +-- +\set ECHO none +\i cube.sql +\set ECHO all + +-- +-- testing the input and output functions +-- + +-- Any number (a one-dimensional point) +SELECT '1'::cube AS cube; +SELECT '-1'::cube AS cube; +SELECT '1.'::cube AS cube; +SELECT '-1.'::cube AS cube; +SELECT '.1'::cube AS cube; +SELECT '-.1'::cube AS cube; +SELECT '1.0'::cube AS cube; +SELECT '-1.0'::cube AS cube; +SELECT '1e7'::cube AS cube; +SELECT '-1e7'::cube AS cube; +SELECT '1.0e7'::cube AS cube; +SELECT '-1.0e7'::cube AS cube; +SELECT '1e+7'::cube AS cube; +SELECT '-1e+7'::cube AS cube; +SELECT '1.0e+7'::cube AS cube; +SELECT '-1.0e+7'::cube AS cube; +SELECT '1e-7'::cube AS cube; +SELECT '-1e-7'::cube AS cube; +SELECT '1.0e-7'::cube AS cube; +SELECT '-1.0e-7'::cube AS cube; +SELECT '1e700'::cube AS cube; +SELECT '-1e700'::cube AS cube; +SELECT '1e-700'::cube AS cube; +SELECT '-1e-700'::cube AS cube; + +-- simple lists (points) +SELECT '1,2'::cube AS cube; +SELECT '(1,2)'::cube AS cube; +SELECT '1,2,3,4,5'::cube AS cube; +SELECT '(1,2,3,4,5)'::cube AS cube; + +-- double lists (cubes) +SELECT '(0),(0)'::cube AS cube; +SELECT '(0),(1)'::cube AS cube; +SELECT '[(0),(0)]'::cube AS cube; +SELECT '[(0),(1)]'::cube AS cube; +SELECT '(0,0,0,0),(0,0,0,0)'::cube AS cube; +SELECT '(0,0,0,0),(1,0,0,0)'::cube AS cube; +SELECT '[(0,0,0,0),(0,0,0,0)]'::cube AS cube; +SELECT '[(0,0,0,0),(1,0,0,0)]'::cube AS cube; + +-- invalid input: parse errors +SELECT ''::cube AS cube; +SELECT 'ABC'::cube AS cube; +SELECT '()'::cube AS cube; +SELECT '[]'::cube AS cube; +SELECT '[()]'::cube AS cube; +SELECT '[(1)]'::cube AS cube; +SELECT '[(1),]'::cube AS cube; +SELECT '[(1),2]'::cube AS cube; +SELECT '[(1),(2),(3)]'::cube AS cube; +SELECT '1,'::cube AS cube; +SELECT '1,2,'::cube AS cube; +SELECT '1,,2'::cube AS cube; +SELECT '(1,)'::cube AS cube; +SELECT '(1,2,)'::cube AS cube; +SELECT '(1,,2)'::cube AS cube; + +-- invalid input: semantic errors and trailing garbage +SELECT '[(1),(2)],'::cube AS cube; -- 0 +SELECT '[(1,2,3),(2,3)]'::cube AS cube; -- 1 +SELECT '[(1,2),(1,2,3)]'::cube AS cube; -- 1 +SELECT '(1),(2),'::cube AS cube; -- 2 +SELECT '(1,2,3),(2,3)'::cube AS cube; -- 3 +SELECT '(1,2),(1,2,3)'::cube AS cube; -- 3 +SELECT '(1,2,3)ab'::cube AS cube; -- 4 +SELECT '(1,2,3)a'::cube AS cube; -- 5 +SELECT '(1,2)('::cube AS cube; -- 5 +SELECT '1,2ab'::cube AS cube; -- 6 +SELECT '1 e7'::cube AS cube; -- 6 +SELECT '1,2a'::cube AS cube; -- 7 +SELECT '1..2'::cube AS cube; -- 7 + +-- +-- testing the operators +-- + +-- equality/inequality: +-- +SELECT '24, 33.20'::cube = '24, 33.20'::cube AS bool; +SELECT '24, 33.20'::cube != '24, 33.20'::cube AS bool; +SELECT '24, 33.20'::cube = '24, 33.21'::cube AS bool; +SELECT '24, 33.20'::cube != '24, 33.21'::cube AS bool; +SELECT '(2,0),(3,1)'::cube = '(2,0,0,0,0),(3,1,0,0,0)'::cube AS bool; +SELECT '(2,0),(3,1)'::cube = '(2,0,0,0,0),(3,1,0,0,1)'::cube AS bool; + +-- "lower than" / "greater than" +-- (these operators are not useful for anything but ordering) +-- +SELECT '1'::cube > '2'::cube AS bool; +SELECT '1'::cube < '2'::cube AS bool; +SELECT '1,1'::cube > '1,2'::cube AS bool; +SELECT '1,1'::cube < '1,2'::cube AS bool; + +SELECT '(2,0),(3,1)'::cube > '(2,0,0,0,0),(3,1,0,0,1)'::cube AS bool; +SELECT '(2,0),(3,1)'::cube < '(2,0,0,0,0),(3,1,0,0,1)'::cube AS bool; +SELECT '(2,0),(3,1)'::cube > '(2,0,0,0,1),(3,1,0,0,0)'::cube AS bool; +SELECT '(2,0),(3,1)'::cube < '(2,0,0,0,1),(3,1,0,0,0)'::cube AS bool; +SELECT '(2,0),(3,1)'::cube > '(2,0,0,0,0),(3,1,0,0,0)'::cube AS bool; +SELECT '(2,0),(3,1)'::cube < '(2,0,0,0,0),(3,1,0,0,0)'::cube AS bool; +SELECT '(2,0,0,0,0),(3,1,0,0,1)'::cube > '(2,0),(3,1)'::cube AS bool; +SELECT '(2,0,0,0,0),(3,1,0,0,1)'::cube < '(2,0),(3,1)'::cube AS bool; +SELECT '(2,0,0,0,1),(3,1,0,0,0)'::cube > '(2,0),(3,1)'::cube AS bool; +SELECT '(2,0,0,0,1),(3,1,0,0,0)'::cube < '(2,0),(3,1)'::cube AS bool; +SELECT '(2,0,0,0,0),(3,1,0,0,0)'::cube > '(2,0),(3,1)'::cube AS bool; +SELECT '(2,0,0,0,0),(3,1,0,0,0)'::cube < '(2,0),(3,1)'::cube AS bool; + + +-- "overlap" +-- +SELECT '1'::cube && '1'::cube AS bool; +SELECT '1'::cube && '2'::cube AS bool; + +SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '0'::cube AS bool; +SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '1'::cube AS bool; +SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '1,1,1'::cube AS bool; +SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '[(1,1,1),(2,2,2)]'::cube AS bool; +SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '[(1,1),(2,2)]'::cube AS bool; +SELECT '[(-1,-1,-1),(1,1,1)]'::cube && '[(2,1,1),(2,2,2)]'::cube AS bool; + +-- "overlap on the left" / "overlap on the right" +-- (these operators are not useful at all but R-tree seems to be +-- sensitive to their presence) +-- +SELECT '1'::cube &< '0'::cube AS bool; +SELECT '1'::cube &< '1'::cube AS bool; +SELECT '1'::cube &< '2'::cube AS bool; + +SELECT '(0),(1)'::cube &< '0'::cube AS bool; +SELECT '(0),(1)'::cube &< '1'::cube AS bool; +SELECT '(0),(1)'::cube &< '(0),(0.5)'::cube AS bool; +SELECT '(0),(1)'::cube &< '(0),(1)'::cube AS bool; +SELECT '(0),(1)'::cube &< '(0),(2)'::cube AS bool; +SELECT '(0),(1)'::cube &< '(1),(2)'::cube AS bool; +SELECT '(0),(1)'::cube &< '(2),(3)'::cube AS bool; + +SELECT '0'::cube &> '1'::cube AS bool; +SELECT '1'::cube &> '1'::cube AS bool; +SELECT '2'::cube &> '1'::cube AS bool; + +SELECT '0'::cube &> '(0),(1)'::cube AS bool; +SELECT '1'::cube &> '(0),(1)'::cube AS bool; +SELECT '(0),(0.5)' &> '(0),(1)'::cube AS bool; +SELECT '(0),(1)'::cube &> '(0),(1)'::cube AS bool; +SELECT '(0),(2)'::cube &> '(0),(1)'::cube AS bool; +SELECT '(1),(2)'::cube &> '(0),(1)'::cube AS bool; +SELECT '(2),(3)'::cube &> '(0),(1)'::cube AS bool; + + +-- "left" / "right" +-- (these operators are not useful but for 1-D or 2-D cubes, but R-tree +-- seems to want them defined) +-- +SELECT '1'::cube << '0'::cube AS bool; +SELECT '1'::cube << '1'::cube AS bool; +SELECT '1'::cube << '2'::cube AS bool; + +SELECT '(0),(1)'::cube << '0'::cube AS bool; +SELECT '(0),(1)'::cube << '1'::cube AS bool; +SELECT '(0),(1)'::cube << '(0),(0.5)'::cube AS bool; +SELECT '(0),(1)'::cube << '(0),(1)'::cube AS bool; +SELECT '(0),(1)'::cube << '(0),(2)'::cube AS bool; +SELECT '(0),(1)'::cube << '(1),(2)'::cube AS bool; +SELECT '(0),(1)'::cube << '(2),(3)'::cube AS bool; + +SELECT '0'::cube >> '1'::cube AS bool; +SELECT '1'::cube >> '1'::cube AS bool; +SELECT '2'::cube >> '1'::cube AS bool; + +SELECT '0'::cube >> '(0),(1)'::cube AS bool; +SELECT '1'::cube >> '(0),(1)'::cube AS bool; +SELECT '(0),(0.5)' >> '(0),(1)'::cube AS bool; +SELECT '(0),(1)'::cube >> '(0),(1)'::cube AS bool; +SELECT '(0),(2)'::cube >> '(0),(1)'::cube AS bool; +SELECT '(1),(2)'::cube >> '(0),(1)'::cube AS bool; +SELECT '(2),(3)'::cube >> '(0),(1)'::cube AS bool; + + +-- "contained in" (the left operand is the cube entirely enclosed by +-- the right operand): +-- +SELECT '0'::cube ~ '0'::cube AS bool; +SELECT '0,0,0'::cube ~ '0,0,0'::cube AS bool; +SELECT '0,0'::cube ~ '0,0,1'::cube AS bool; +SELECT '0,0,0'::cube ~ '0,0,1'::cube AS bool; +SELECT '1,0,0'::cube ~ '0,0,1'::cube AS bool; +SELECT '(1,0,0),(0,0,1)'::cube ~ '(1,0,0),(0,0,1)'::cube AS bool; +SELECT '(1,0,0),(0,0,1)'::cube ~ '(-1,-1,-1),(1,1,1)'::cube AS bool; +SELECT '(1,0,0),(0,0,1)'::cube ~ '(-1,-1,-1,-1),(1,1,1,1)'::cube AS bool; +SELECT '0'::cube ~ '(-1),(1)'::cube AS bool; +SELECT '1'::cube ~ '(-1),(1)'::cube AS bool; +SELECT '-1'::cube ~ '(-1),(1)'::cube AS bool; +SELECT '(-1),(1)'::cube ~ '(-1),(1)'::cube AS bool; +SELECT '(-1),(1)'::cube ~ '(-1,-1),(1,1)'::cube AS bool; +SELECT '(-2),(1)'::cube ~ '(-1),(1)'::cube AS bool; +SELECT '(-2),(1)'::cube ~ '(-1,-1),(1,1)'::cube AS bool; + + +-- "contains" (the left operand is the cube that entirely encloses the +-- right operand) +-- +SELECT '0'::cube @ '0'::cube AS bool; +SELECT '0,0,0'::cube @ '0,0,0'::cube AS bool; +SELECT '0,0,1'::cube @ '0,0'::cube AS bool; +SELECT '0,0,1'::cube @ '0,0,0'::cube AS bool; +SELECT '0,0,1'::cube @ '1,0,0'::cube AS bool; +SELECT '(1,0,0),(0,0,1)'::cube @ '(1,0,0),(0,0,1)'::cube AS bool; +SELECT '(-1,-1,-1),(1,1,1)'::cube @ '(1,0,0),(0,0,1)'::cube AS bool; +SELECT '(-1,-1,-1,-1),(1,1,1,1)'::cube @ '(1,0,0),(0,0,1)'::cube AS bool; +SELECT '(-1),(1)'::cube @ '0'::cube AS bool; +SELECT '(-1),(1)'::cube @ '1'::cube AS bool; +SELECT '(-1),(1)'::cube @ '-1'::cube AS bool; +SELECT '(-1),(1)'::cube @ '(-1),(1)'::cube AS bool; +SELECT '(-1,-1),(1,1)'::cube @ '(-1),(1)'::cube AS bool; +SELECT '(-1),(1)'::cube @ '(-2),(1)'::cube AS bool; +SELECT '(-1,-1),(1,1)'::cube @ '(-2),(1)'::cube AS bool; + + +-- Load some example data and build the index +-- +CREATE TABLE test_cube (c cube); + +\copy test_cube from 'data/test_cube.data' + +CREATE INDEX test_cube_ix ON test_cube USING gist (c); +SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)'; + +-- Test sorting +SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c;